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:
@@ -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) {
|
||||
|
||||
11
config.go
11
config.go
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
17
internal/platform/crypto.go
Normal file
17
internal/platform/crypto.go
Normal 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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user