Skip to content

Commit

Permalink
Provided interface for random source.
Browse files Browse the repository at this point in the history
Use pseudo-random source in tests.
  • Loading branch information
r8d8 committed Feb 14, 2022
1 parent 2875c25 commit 5754d38
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 12 deletions.
43 changes: 32 additions & 11 deletions wasi/wasi.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package wasi

import (
crand "crypto/rand"
"encoding/binary"
"errors"
"io"
"io/fs"
"math"
"math/rand"
"os"
"reflect"
"time"
Expand Down Expand Up @@ -171,6 +171,24 @@ const (
wasiSnapshotPreview1Name = "wasi_snapshot_preview1"
)

type RandomSource interface {
Read([]byte) (int, error)
Int31() (int32, error)
}

// Non-deterministic random source using crypto/rand
type CryptoRandomSource struct{}

func (c *CryptoRandomSource) Read(p []byte) (n int, err error) {
return crand.Read(p)
}

func (c *CryptoRandomSource) Int31() (v int32, err error) {
err = binary.Read(crand.Reader, binary.BigEndian, &v)

return v, err
}

type api struct {
args *nullTerminatedStrings
stdin io.Reader
Expand All @@ -179,7 +197,7 @@ type api struct {
opened map[uint32]fileEntry
// timeNowUnixNano is mutable for testing
timeNowUnixNano func() uint64
randSource *rand.Rand
randSource RandomSource
}

func (a *api) register(store *wasm.Store) (err error) {
Expand Down Expand Up @@ -354,7 +372,6 @@ func registerAPI(store *wasm.Store, opts ...Option) (API, error) {
}

func newAPI(opts ...Option) *api {
s := rand.NewSource(time.Now().UnixNano())
ret := &api{
args: &nullTerminatedStrings{},
stdin: os.Stdin,
Expand All @@ -364,7 +381,7 @@ func newAPI(opts ...Option) *api {
timeNowUnixNano: func() uint64 {
return uint64(time.Now().UnixNano())
},
randSource: rand.New(s),
randSource: &CryptoRandomSource{},
}

// apply functional options
Expand All @@ -374,15 +391,16 @@ func newAPI(opts ...Option) *api {
return ret
}

func (a *api) seedRandSource(seed int64) {
a.randSource.Seed(seed)
}
func (a *api) randUnusedFD() (uint32, error) {
v, err := a.randSource.Int31()
if err != nil {
return 0, err
}

func (a *api) randUnusedFD() uint32 {
fd := uint32(rand.Int31())
fd := uint32(v)
for {
if _, ok := a.opened[fd]; !ok {
return fd
return fd, nil
}
fd = (fd + 1) % (1 << 31)
}
Expand Down Expand Up @@ -436,7 +454,10 @@ func (a *api) path_open(ctx *wasm.HostFunctionCallContext, fd, dirFlags, pathPtr
}
}

newFD := a.randUnusedFD()
newFD, err := a.randUnusedFD()
if err != nil {
return ErrnoInval
}

a.opened[newFD] = fileEntry{
file: f,
Expand Down
25 changes: 24 additions & 1 deletion wasi/wasi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package wasi
import (
"context"
_ "embed"
mrand "math/rand"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -317,6 +318,28 @@ func TestAPI_ClockTimeGet_Errors(t *testing.T) {
//go:embed testdata/random.wat
var randomWat []byte

// Non-deterministic random rource using crypto/rand
type DummyRandomSource struct {
rng *mrand.Rand
}

func (d *DummyRandomSource) Read(p []byte) (n int, err error) {
return d.rng.Read(p)
}

func (d *DummyRandomSource) Int31() (v int32, err error) {
return d.rng.Int31(), nil

}

func NewDummyRandomSource(seed int64) RandomSource {
s := mrand.NewSource(seed)

return &DummyRandomSource{
rng: mrand.New(s),
}
}

func TestAPI_RandomGet(t *testing.T) {
store, wasiAPI := instantiateWasmStore(t, randomWat, "test")
maskLength := 7 // number of bytes to write '?' to tell what we've written
Expand All @@ -330,7 +353,7 @@ func TestAPI_RandomGet(t *testing.T) {
var buf = uint32(1) // offset,
var seed = int64(42) // and seed value

wasiAPI.(*api).seedRandSource(seed)
wasiAPI.(*api).randSource = NewDummyRandomSource(seed)

t.Run("API.RandomGet", func(t *testing.T) {
maskMemory(store, maskLength)
Expand Down

0 comments on commit 5754d38

Please sign in to comment.