github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/wasm/binary/memory_test.go (about)

     1  package binary
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/wasilibs/wazerox/api"
     9  	"github.com/wasilibs/wazerox/experimental"
    10  	"github.com/wasilibs/wazerox/internal/testing/binaryencoding"
    11  	"github.com/wasilibs/wazerox/internal/testing/require"
    12  	"github.com/wasilibs/wazerox/internal/wasm"
    13  )
    14  
    15  func Test_newMemorySizer(t *testing.T) {
    16  	zero := uint32(0)
    17  	ten := uint32(10)
    18  	defaultLimit := wasm.MemoryLimitPages
    19  
    20  	tests := []struct {
    21  		name                                       string
    22  		memoryCapacityFromMax                      bool
    23  		limit                                      uint32
    24  		min                                        uint32
    25  		max                                        *uint32
    26  		expectedMin, expectedCapacity, expectedMax uint32
    27  	}{
    28  		{
    29  			name:             "min 0",
    30  			limit:            defaultLimit,
    31  			min:              zero,
    32  			max:              &defaultLimit,
    33  			expectedMin:      zero,
    34  			expectedCapacity: zero,
    35  			expectedMax:      defaultLimit,
    36  		},
    37  		{
    38  			name:             "min 0 defaults max to defaultLimit",
    39  			limit:            defaultLimit,
    40  			min:              zero,
    41  			expectedMin:      zero,
    42  			expectedCapacity: zero,
    43  			expectedMax:      defaultLimit,
    44  		},
    45  		{
    46  			name:             "min 0, max 0",
    47  			limit:            defaultLimit,
    48  			min:              zero,
    49  			max:              &zero,
    50  			expectedMin:      zero,
    51  			expectedCapacity: zero,
    52  			expectedMax:      zero,
    53  		},
    54  		{
    55  			name:             "min 0, max 10",
    56  			limit:            defaultLimit,
    57  			min:              zero,
    58  			max:              &ten,
    59  			expectedMin:      zero,
    60  			expectedCapacity: zero,
    61  			expectedMax:      ten,
    62  		},
    63  		{
    64  			name:                  "min 0, max 10 memoryCapacityFromMax",
    65  			limit:                 defaultLimit,
    66  			memoryCapacityFromMax: true,
    67  			min:                   zero,
    68  			max:                   &ten,
    69  			expectedMin:           zero,
    70  			expectedCapacity:      ten,
    71  			expectedMax:           ten,
    72  		},
    73  		{
    74  			name:             "min 10, no max",
    75  			limit:            200,
    76  			min:              10,
    77  			expectedMin:      10,
    78  			expectedCapacity: 10,
    79  			expectedMax:      200,
    80  		},
    81  		{
    82  			name:                  "min 10, no max memoryCapacityFromMax",
    83  			memoryCapacityFromMax: true,
    84  			limit:                 200,
    85  			min:                   10,
    86  			expectedMin:           10,
    87  			expectedCapacity:      200,
    88  			expectedMax:           200,
    89  		},
    90  		{
    91  			name:             "min=max",
    92  			limit:            defaultLimit,
    93  			min:              ten,
    94  			max:              &ten,
    95  			expectedMin:      ten,
    96  			expectedCapacity: ten,
    97  			expectedMax:      ten,
    98  		},
    99  		{
   100  			name:             "max > memoryLimitPages",
   101  			limit:            5,
   102  			min:              0,
   103  			max:              &ten,
   104  			expectedMin:      0,
   105  			expectedCapacity: 0,
   106  			expectedMax:      5,
   107  		},
   108  	}
   109  
   110  	for _, tt := range tests {
   111  		tc := tt
   112  		t.Run(tc.name, func(t *testing.T) {
   113  			sizer := newMemorySizer(tc.limit, tc.memoryCapacityFromMax)
   114  			min, capacity, max := sizer(tc.min, tc.max)
   115  			require.Equal(t, tc.expectedMin, min)
   116  			require.Equal(t, tc.expectedCapacity, capacity)
   117  			require.Equal(t, tc.expectedMax, max)
   118  		})
   119  	}
   120  }
   121  
   122  func TestMemoryType(t *testing.T) {
   123  	zero := uint32(0)
   124  	max := wasm.MemoryLimitPages
   125  
   126  	tests := []struct {
   127  		name             string
   128  		input            *wasm.Memory
   129  		memoryLimitPages uint32
   130  		expected         []byte
   131  	}{
   132  		{
   133  			name:     "min 0",
   134  			input:    &wasm.Memory{Max: max, IsMaxEncoded: true},
   135  			expected: []byte{0x1, 0, 0x80, 0x80, 0x4},
   136  		},
   137  		{
   138  			name:     "min 0 default max",
   139  			input:    &wasm.Memory{Max: max},
   140  			expected: []byte{0x0, 0},
   141  		},
   142  		{
   143  			name:     "min 0, max 0",
   144  			input:    &wasm.Memory{Max: zero, IsMaxEncoded: true},
   145  			expected: []byte{0x1, 0, 0},
   146  		},
   147  		{
   148  			name:     "min=max",
   149  			input:    &wasm.Memory{Min: 1, Cap: 1, Max: 1, IsMaxEncoded: true},
   150  			expected: []byte{0x1, 1, 1},
   151  		},
   152  		{
   153  			name:     "min 0, max largest",
   154  			input:    &wasm.Memory{Max: max, IsMaxEncoded: true},
   155  			expected: []byte{0x1, 0, 0x80, 0x80, 0x4},
   156  		},
   157  		{
   158  			name:     "min largest max largest",
   159  			input:    &wasm.Memory{Min: max, Cap: max, Max: max, IsMaxEncoded: true},
   160  			expected: []byte{0x1, 0x80, 0x80, 0x4, 0x80, 0x80, 0x4},
   161  		},
   162  		{
   163  			name:             "min 0, max largest, wazero limit",
   164  			input:            &wasm.Memory{Max: max, IsMaxEncoded: true},
   165  			memoryLimitPages: 512,
   166  			expected:         []byte{0x1, 0, 0x80, 0x80, 0x4},
   167  		},
   168  	}
   169  
   170  	for _, tt := range tests {
   171  		tc := tt
   172  
   173  		b := binaryencoding.EncodeMemory(tc.input)
   174  		t.Run(fmt.Sprintf("encode %s", tc.name), func(t *testing.T) {
   175  			require.Equal(t, tc.expected, b)
   176  		})
   177  
   178  		t.Run(fmt.Sprintf("decode %s", tc.name), func(t *testing.T) {
   179  			tmax := max
   180  			expectedDecoded := tc.input
   181  			if tc.memoryLimitPages != 0 {
   182  				// If a memory limit exists, then the expected module Max reflects that limit.
   183  				tmax = tc.memoryLimitPages
   184  				expectedDecoded.Max = tmax
   185  			}
   186  
   187  			binary, err := decodeMemory(bytes.NewReader(b), api.CoreFeaturesV2, newMemorySizer(tmax, false), tmax)
   188  			require.NoError(t, err)
   189  			require.Equal(t, binary, expectedDecoded)
   190  		})
   191  	}
   192  }
   193  
   194  func TestDecodeMemoryType_Errors(t *testing.T) {
   195  	max := wasm.MemoryLimitPages
   196  
   197  	tests := []struct {
   198  		name           string
   199  		input          []byte
   200  		threadsEnabled bool
   201  		expectedErr    string
   202  	}{
   203  		{
   204  			name:        "max < min",
   205  			input:       []byte{0x1, 0x80, 0x80, 0x4, 0},
   206  			expectedErr: "min 65536 pages (4 Gi) > max 0 pages (0 Ki)",
   207  		},
   208  		{
   209  			name:        "min > limit",
   210  			input:       []byte{0x0, 0xff, 0xff, 0xff, 0xff, 0xf},
   211  			expectedErr: "min 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)",
   212  		},
   213  		{
   214  			name:        "max > limit",
   215  			input:       []byte{0x1, 0, 0xff, 0xff, 0xff, 0xff, 0xf},
   216  			expectedErr: "max 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)",
   217  		},
   218  		{
   219  			name:        "shared but no threads",
   220  			input:       []byte{0x2, 0, 0x80, 0x80, 0x4},
   221  			expectedErr: "shared memory requested but threads feature not enabled",
   222  		},
   223  		{
   224  			name:           "shared but no max",
   225  			input:          []byte{0x2, 0, 0x80, 0x80, 0x4},
   226  			threadsEnabled: true,
   227  			expectedErr:    "shared memory requires a maximum size to be specified",
   228  		},
   229  	}
   230  
   231  	for _, tt := range tests {
   232  		tc := tt
   233  
   234  		t.Run(tc.name, func(t *testing.T) {
   235  			features := api.CoreFeaturesV2
   236  			if tc.threadsEnabled {
   237  				features = features.SetEnabled(experimental.CoreFeaturesThreads, true)
   238  			} else {
   239  				// Allow test to work if threads is ever added to default features by explicitly removing threads features
   240  				features = features.SetEnabled(experimental.CoreFeaturesThreads, false)
   241  			}
   242  			_, err := decodeMemory(bytes.NewReader(tc.input), features, newMemorySizer(max, false), max)
   243  			require.EqualError(t, err, tc.expectedErr)
   244  		})
   245  	}
   246  }