Switches default random source to deterministic (#736)

This changes the default random source to provide deterministic values
similar to how nanotime and walltime do. This also prevents any worries
about if wasm can deplete the host's underlying source of entropy.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2022-08-06 21:47:50 +12:00
committed by GitHub
parent fe56fabd63
commit 9414b0bb74
8 changed files with 56 additions and 28 deletions

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"context"
_ "embed"
"encoding/hex"
"errors"
"io"
"strings"
@@ -123,8 +124,7 @@ func TestAbort_Error(t *testing.T) {
}
func TestSeed(t *testing.T) {
b := []byte{0, 1, 2, 3, 4, 5, 6, 7}
mod, r, log := requireProxyModule(t, NewFunctionExporter(), wazero.NewModuleConfig().WithRandSource(bytes.NewReader(b)))
mod, r, log := requireProxyModule(t, NewFunctionExporter(), wazero.NewModuleConfig())
defer r.Close(testCtx)
ret, err := mod.ExportedFunction(functionSeed).Call(testCtx)
@@ -132,11 +132,11 @@ func TestSeed(t *testing.T) {
require.Equal(t, `
--> proxy.seed()
==> env.~lib/builtins/seed()
<== (7.949928895127363e-275)
<-- (7.949928895127363e-275)
<== (4.958153677776298e-175)
<-- (4.958153677776298e-175)
`, "\n"+log.String())
require.Equal(t, b, u64.LeBytes(ret[0]))
require.Equal(t, "538c7f96b164bf1b", hex.EncodeToString(u64.LeBytes(ret[0])))
}
func TestSeed_error(t *testing.T) {

View File

@@ -565,12 +565,15 @@ type ModuleConfig interface {
// See WithNanosleep
WithSysNanosleep() ModuleConfig
// WithRandSource configures a source of random bytes. Defaults to crypto/rand.Reader.
// WithRandSource configures a source of random bytes. Defaults to return a
// deterministic source. You might override this with crypto/rand.Reader
//
// This reader is most commonly used by the functions like "random_get" in "wasi_snapshot_preview1" or "seed" in
// AssemblyScript standard "env" although it could be used by functions imported from other modules.
// This reader is most commonly used by the functions like "random_get" in
// "wasi_snapshot_preview1", "seed" in AssemblyScript standard "env", and
// "getRandomData" when runtime.GOOS is "js".
//
// Note: The caller is responsible to close any io.Reader they supply: It is not closed on api.Module Close.
// Note: The caller is responsible to close any io.Reader they supply: It
// is not closed on api.Module Close.
WithRandSource(io.Reader) ModuleConfig
}

View File

@@ -2,6 +2,7 @@ package wazero
import (
"context"
"crypto/rand"
"io"
"io/fs"
"math"
@@ -479,6 +480,23 @@ func TestModuleConfig_toSysContext(t *testing.T) {
testFS2, // fs
),
},
{
name: "WithRandSource",
input: base.WithRandSource(rand.Reader),
expected: requireSysContext(t,
math.MaxUint32, // max
nil, // args
nil, // environ
nil, // stdin
nil, // stdout
nil, // stderr
rand.Reader, // randSource
&wt, 1, // walltime, walltimeResolution
&nt, 1, // nanotime, nanotimeResolution
nil, // nanosleep
nil, // fs
),
},
}
for _, tt := range tests {

View File

@@ -0,0 +1,17 @@
package platform
import (
"io"
"math/rand"
)
// seed is a fixed seed value for NewFakeRandSource.
//
// Trivia: While arbitrary, 42 was chosen as it is the "Ultimate Answer" in
// the Douglas Adams novel "The Hitchhiker's Guide to the Galaxy."
const seed = int64(42)
// NewFakeRandSource returns a deterministic source of random values.
func NewFakeRandSource() io.Reader {
return rand.New(rand.NewSource(seed))
}

View File

@@ -2,7 +2,6 @@ package sys
import (
"context"
"crypto/rand"
"errors"
"fmt"
"io"
@@ -118,7 +117,7 @@ func (c *Context) FS(ctx context.Context) *FSContext {
return c.fsc
}
// RandSource is a source of random bytes and defaults to crypto/rand.Reader.
// RandSource is a source of random bytes and defaults to a deterministic source.
// see wazero.ModuleConfig WithRandSource
func (c *Context) RandSource() io.Reader {
return c.randSource
@@ -153,8 +152,10 @@ func NewContext(
stdin io.Reader,
stdout, stderr io.Writer,
randSource io.Reader,
walltime *sys.Walltime, walltimeResolution sys.ClockResolution,
nanotime *sys.Nanotime, nanotimeResolution sys.ClockResolution,
walltime *sys.Walltime,
walltimeResolution sys.ClockResolution,
nanotime *sys.Nanotime,
nanotimeResolution sys.ClockResolution,
nanosleep *sys.Nanosleep,
fs fs.FS,
) (sysCtx *Context, err error) {
@@ -187,7 +188,7 @@ func NewContext(
}
if randSource == nil {
sysCtx.randSource = rand.Reader
sysCtx.randSource = platform.NewFakeRandSource()
} else {
sysCtx.randSource = randSource
}

View File

@@ -3,7 +3,6 @@ package sys
import (
"bytes"
"context"
"crypto/rand"
"io"
"testing"
"time"
@@ -55,7 +54,7 @@ func TestDefaultSysContext(t *testing.T) {
require.Zero(t, sysCtx.Nanotime(testCtx)) // See above on functions.
require.Equal(t, sys.ClockResolution(1), sysCtx.NanotimeResolution())
require.Equal(t, &ns, sysCtx.nanosleep)
require.Equal(t, rand.Reader, sysCtx.RandSource())
require.Equal(t, platform.NewFakeRandSource(), sysCtx.RandSource())
require.Equal(t, NewFSContext(testfs.FS{}), sysCtx.FS(testCtx))
}

View File

@@ -12,8 +12,7 @@ import (
)
func Test_randomGet(t *testing.T) {
mod, r, log := requireProxyModule(t, wazero.NewModuleConfig().
WithRandSource(deterministicRandomSource()))
mod, r, log := requireProxyModule(t, wazero.NewModuleConfig())
defer r.Close(testCtx)
expectedMemory := []byte{
@@ -42,8 +41,7 @@ func Test_randomGet(t *testing.T) {
}
func Test_randomGet_Errors(t *testing.T) {
mod, r, log := requireProxyModule(t, wazero.NewModuleConfig().
WithRandSource(deterministicRandomSource()))
mod, r, log := requireProxyModule(t, wazero.NewModuleConfig())
defer r.Close(testCtx)
memorySize := mod.Memory().Size(testCtx)

View File

@@ -4,8 +4,6 @@ import (
"bytes"
"context"
_ "embed"
"io"
"math/rand"
"testing"
"github.com/tetratelabs/wazero"
@@ -16,12 +14,6 @@ import (
"github.com/tetratelabs/wazero/internal/testing/require"
)
const seed = int64(42) // fixed seed value
var deterministicRandomSource = func() io.Reader {
return rand.New(rand.NewSource(seed))
}
// testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")