1 package wasm
2
3 import (
4 "fmt"
5 "math"
6
7 "github.com/tetratelabs/wazero/api"
8 "github.com/tetratelabs/wazero/internal/leb128"
9 )
10
11
12 type Table struct {
13 Min uint32
14 Max *uint32
15 Type RefType
16 }
17
18
19 type RefType = byte
20
21 const (
22
23 RefTypeFuncref = ValueTypeFuncref
24
25 RefTypeExternref = ValueTypeExternref
26 )
27
28 func RefTypeName(t RefType) (ret string) {
29 switch t {
30 case RefTypeFuncref:
31 ret = "funcref"
32 case RefTypeExternref:
33 ret = "externref"
34 default:
35 ret = fmt.Sprintf("unknown(0x%x)", t)
36 }
37 return
38 }
39
40
41
42
43 type ElementMode = byte
44
45 const (
46
47 ElementModeActive ElementMode = iota
48
49 ElementModePassive
50
51 ElementModeDeclarative
52 )
53
54
55
56
57 type ElementSegment struct {
58
59
60 OffsetExpr ConstantExpression
61
62
63
64 TableIndex Index
65
66
67
68
69 Init []Index
70
71
72 Type RefType
73
74
75 Mode ElementMode
76 }
77
78 const (
79
80
81
82
83
84 ElementInitNullReference Index = 1 << 31
85
86
87
88
89
90
91
92 ElementInitImportedGlobalFunctionReference Index = 1 << 30
93 )
94
95
96
97
98 func unwrapElementInitGlobalReference(init Index) (_ Index, ok bool) {
99 if init&ElementInitImportedGlobalFunctionReference == ElementInitImportedGlobalFunctionReference {
100 return init &^ ElementInitImportedGlobalFunctionReference, true
101 }
102 return init, false
103 }
104
105
106
107 func (e *ElementSegment) IsActive() bool {
108 return e.Mode == ElementModeActive
109 }
110
111
112
113
114 type TableInstance struct {
115
116
117
118 References []Reference
119
120
121 Min uint32
122
123
124 Max *uint32
125
126
127 Type RefType
128 }
129
130
131
132
133 type ElementInstance = []Reference
134
135
136 type Reference = uintptr
137
138
139
140 func (m *Module) validateTable(enabledFeatures api.CoreFeatures, tables []Table, maximumTableIndex uint32) error {
141 if len(tables) > int(maximumTableIndex) {
142 return fmt.Errorf("too many tables in a module: %d given with limit %d", len(tables), maximumTableIndex)
143 }
144
145 importedTableCount := m.ImportTableCount
146
147
148 funcCount := m.ImportFunctionCount + m.SectionElementCount(SectionIDFunction)
149 globalsCount := m.ImportGlobalCount + m.SectionElementCount(SectionIDGlobal)
150
151
152
153 for i := range m.ElementSection {
154 elem := &m.ElementSection[i]
155 idx := Index(i)
156 initCount := uint32(len(elem.Init))
157
158 if elem.Type == RefTypeFuncref {
159
160 for ei, init := range elem.Init {
161 if init == ElementInitNullReference {
162 continue
163 }
164 index, ok := unwrapElementInitGlobalReference(init)
165 if ok {
166 if index >= globalsCount {
167 return fmt.Errorf("%s[%d].init[%d] globalidx %d out of range", SectionIDName(SectionIDElement), idx, ei, index)
168 }
169 } else {
170 if index >= funcCount {
171 return fmt.Errorf("%s[%d].init[%d] funcidx %d out of range", SectionIDName(SectionIDElement), idx, ei, index)
172 }
173 }
174 }
175 } else {
176 for j, elem := range elem.Init {
177 if elem != ElementInitNullReference {
178 return fmt.Errorf("%s[%d].init[%d] must be ref.null but was %v", SectionIDName(SectionIDElement), idx, j, elem)
179 }
180 }
181 }
182
183 if elem.IsActive() {
184 if len(tables) <= int(elem.TableIndex) {
185 return fmt.Errorf("unknown table %d as active element target", elem.TableIndex)
186 }
187
188 t := tables[elem.TableIndex]
189 if t.Type != elem.Type {
190 return fmt.Errorf("element type mismatch: table has %s but element has %s",
191 RefTypeName(t.Type), RefTypeName(elem.Type),
192 )
193 }
194
195
196 oc := elem.OffsetExpr.Opcode
197 if oc == OpcodeGlobalGet {
198 globalIdx, _, err := leb128.LoadUint32(elem.OffsetExpr.Data)
199 if err != nil {
200 return fmt.Errorf("%s[%d] couldn't read global.get parameter: %w", SectionIDName(SectionIDElement), idx, err)
201 } else if err = m.verifyImportGlobalI32(SectionIDElement, idx, globalIdx); err != nil {
202 return err
203 }
204 } else if oc == OpcodeI32Const {
205
206
207
208 if !enabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes) && elem.TableIndex >= importedTableCount {
209
210 o, _, err := leb128.LoadInt32(elem.OffsetExpr.Data)
211 if err != nil {
212 return fmt.Errorf("%s[%d] couldn't read i32.const parameter: %w", SectionIDName(SectionIDElement), idx, err)
213 }
214 offset := Index(o)
215 if err = checkSegmentBounds(t.Min, uint64(initCount)+uint64(offset), idx); err != nil {
216 return err
217 }
218 }
219 } else {
220 return fmt.Errorf("%s[%d] has an invalid const expression: %s", SectionIDName(SectionIDElement), idx, InstructionName(oc))
221 }
222 }
223 }
224 return nil
225 }
226
227
228
229
230
231
232
233
234 func (m *ModuleInstance) buildTables(module *Module, skipBoundCheck bool) (err error) {
235 idx := module.ImportTableCount
236 for i := range module.TableSection {
237 tsec := &module.TableSection[i]
238
239 m.Tables[idx] = &TableInstance{
240 References: make([]Reference, tsec.Min), Min: tsec.Min, Max: tsec.Max,
241 Type: tsec.Type,
242 }
243 idx++
244 }
245
246 if !skipBoundCheck {
247 for elemI := range module.ElementSection {
248 elem := &module.ElementSection[elemI]
249 table := m.Tables[elem.TableIndex]
250 var offset uint32
251 if elem.OffsetExpr.Opcode == OpcodeGlobalGet {
252
253 globalIdx, _, _ := leb128.LoadUint32(elem.OffsetExpr.Data)
254 global := m.Globals[globalIdx]
255 offset = uint32(global.Val)
256 } else {
257
258 o, _, _ := leb128.LoadInt32(elem.OffsetExpr.Data)
259 offset = uint32(o)
260 }
261
262
263 initCount := uint64(len(elem.Init))
264 if err = checkSegmentBounds(table.Min, uint64(offset)+initCount, Index(elemI)); err != nil {
265 return
266 }
267 }
268 }
269 return
270 }
271
272
273
274
275
276
277
278
279 func checkSegmentBounds(min uint32, requireMin uint64, idx Index) error {
280 if requireMin > uint64(min) {
281 return fmt.Errorf("%s[%d].init exceeds min table size", SectionIDName(SectionIDElement), idx)
282 }
283 return nil
284 }
285
286 func (m *Module) verifyImportGlobalI32(sectionID SectionID, sectionIdx Index, idx uint32) error {
287 ig := uint32(math.MaxUint32)
288 for i := range m.ImportSection {
289 imp := &m.ImportSection[i]
290 if imp.Type == ExternTypeGlobal {
291 ig++
292 if ig == idx {
293 if imp.DescGlobal.ValType != ValueTypeI32 {
294 return fmt.Errorf("%s[%d] (global.get %d): import[%d].global.ValType != i32", SectionIDName(sectionID), sectionIdx, idx, i)
295 }
296 return nil
297 }
298 }
299 }
300 return fmt.Errorf("%s[%d] (global.get %d): out of range of imported globals", SectionIDName(sectionID), sectionIdx, idx)
301 }
302
303
304
305
306
307 func (t *TableInstance) Grow(delta uint32, initialRef Reference) (currentLen uint32) {
308 currentLen = uint32(len(t.References))
309 if delta == 0 {
310 return
311 }
312
313 if newLen := int64(currentLen) + int64(delta);
314 newLen >= math.MaxUint32 || (t.Max != nil && newLen > int64(*t.Max)) {
315 return 0xffffffff
316 }
317 t.References = append(t.References, make([]uintptr, delta)...)
318
319
320
321 newRegion := t.References[currentLen:]
322 newRegion[0] = initialRef
323 for i := 1; i < len(newRegion); i *= 2 {
324 copy(newRegion[i:], newRegion[:i])
325 }
326 return
327 }
328
View as plain text