github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/wasm/binary/memory_test.go (about)

     1  package binary
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/tetratelabs/wazero/api"
     9  	"github.com/tetratelabs/wazero/experimental"
    10  	"github.com/tetratelabs/wazero/internal/testing/binaryencoding"
    11  	"github.com/tetratelabs/wazero/internal/testing/require"
    12  	"github.com/tetratelabs/wazero/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  			name:     "min 0, max 1, shared",
   170  			input:    &wasm.Memory{Max: 1, IsMaxEncoded: true, IsShared: true},
   171  			expected: []byte{0x3, 0, 1},
   172  		},
   173  	}
   174  
   175  	for _, tt := range tests {
   176  		tc := tt
   177  
   178  		b := binaryencoding.EncodeMemory(tc.input)
   179  		t.Run(fmt.Sprintf("encode %s", tc.name), func(t *testing.T) {
   180  			require.Equal(t, tc.expected, b)
   181  		})
   182  
   183  		t.Run(fmt.Sprintf("decode %s", tc.name), func(t *testing.T) {
   184  			tmax := max
   185  			expectedDecoded := tc.input
   186  			if tc.memoryLimitPages != 0 {
   187  				// If a memory limit exists, then the expected module Max reflects that limit.
   188  				tmax = tc.memoryLimitPages
   189  				expectedDecoded.Max = tmax
   190  			}
   191  
   192  			features := api.CoreFeaturesV2
   193  			if tc.input.IsShared {
   194  				features = features.SetEnabled(experimental.CoreFeaturesThreads, true)
   195  			}
   196  			binary, err := decodeMemory(bytes.NewReader(b), features, newMemorySizer(tmax, false), tmax)
   197  			require.NoError(t, err)
   198  			require.Equal(t, binary, expectedDecoded)
   199  		})
   200  	}
   201  }
   202  
   203  func TestDecodeMemoryType_Errors(t *testing.T) {
   204  	max := wasm.MemoryLimitPages
   205  
   206  	tests := []struct {
   207  		name           string
   208  		input          []byte
   209  		threadsEnabled bool
   210  		expectedErr    string
   211  	}{
   212  		{
   213  			name:        "max < min",
   214  			input:       []byte{0x1, 0x80, 0x80, 0x4, 0},
   215  			expectedErr: "min 65536 pages (4 Gi) > max 0 pages (0 Ki)",
   216  		},
   217  		{
   218  			name:        "min > limit",
   219  			input:       []byte{0x0, 0xff, 0xff, 0xff, 0xff, 0xf},
   220  			expectedErr: "min 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)",
   221  		},
   222  		{
   223  			name:        "max > limit",
   224  			input:       []byte{0x1, 0, 0xff, 0xff, 0xff, 0xff, 0xf},
   225  			expectedErr: "max 4294967295 pages (3 Ti) over limit of 65536 pages (4 Gi)",
   226  		},
   227  		{
   228  			name:        "shared but no threads",
   229  			input:       []byte{0x2, 0, 0x80, 0x80, 0x4},
   230  			expectedErr: "shared memory requested but threads feature not enabled",
   231  		},
   232  		{
   233  			name:           "shared but no max",
   234  			input:          []byte{0x2, 0, 0x80, 0x80, 0x4},
   235  			threadsEnabled: true,
   236  			expectedErr:    "shared memory requires a maximum size to be specified",
   237  		},
   238  	}
   239  
   240  	for _, tt := range tests {
   241  		tc := tt
   242  
   243  		t.Run(tc.name, func(t *testing.T) {
   244  			features := api.CoreFeaturesV2
   245  			if tc.threadsEnabled {
   246  				features = features.SetEnabled(experimental.CoreFeaturesThreads, true)
   247  			} else {
   248  				// Allow test to work if threads is ever added to default features by explicitly removing threads features
   249  				features = features.SetEnabled(experimental.CoreFeaturesThreads, false)
   250  			}
   251  			_, err := decodeMemory(bytes.NewReader(tc.input), features, newMemorySizer(max, false), max)
   252  			require.EqualError(t, err, tc.expectedErr)
   253  		})
   254  	}
   255  }