1
- use parity_wasm:: elements:: { External , ImportEntry , Module } ;
1
+ use parity_wasm:: elements:: { External , ImportEntry , Module , TableType } ;
2
2
use std:: collections:: BTreeSet ;
3
3
use std:: collections:: HashSet ;
4
4
@@ -49,10 +49,25 @@ const SUPPORTED_INTERFACE_VERSIONS: &[&str] = &[
49
49
] ;
50
50
51
51
const MEMORY_LIMIT : u32 = 512 ; // in pages
52
+ /// The upper limit for the `max` value of each table. CosmWasm contracts have
53
+ /// initial=max for 1 table. See
54
+ ///
55
+ /// ```plain
56
+ /// $ wasm-objdump --section=table -x packages/vm/testdata/hackatom.wasm
57
+ /// Section Details:
58
+ ///
59
+ /// Table[1]:
60
+ /// - table[0] type=funcref initial=161 max=161
61
+ /// ```
62
+ ///
63
+ /// As of March 2023, on Juno mainnet the largest value for production contracts
64
+ /// is 485. Most are between 100 and 300.
65
+ const TABLE_SIZE_LIMIT : u32 = 2500 ; // entries
52
66
53
67
/// Checks if the data is valid wasm and compatibility with the CosmWasm API (imports and exports)
54
68
pub fn check_wasm ( wasm_code : & [ u8 ] , available_capabilities : & HashSet < String > ) -> VmResult < ( ) > {
55
69
let module = deserialize_wasm ( wasm_code) ?;
70
+ check_wasm_tables ( & module) ?;
56
71
check_wasm_memories ( & module) ?;
57
72
check_interface_version ( & module) ?;
58
73
check_wasm_exports ( & module) ?;
@@ -61,6 +76,38 @@ pub fn check_wasm(wasm_code: &[u8], available_capabilities: &HashSet<String>) ->
61
76
Ok ( ( ) )
62
77
}
63
78
79
+ fn check_wasm_tables ( module : & Module ) -> VmResult < ( ) > {
80
+ let sections: & [ TableType ] = module
81
+ . table_section ( )
82
+ . map_or ( & [ ] , |section| section. entries ( ) ) ;
83
+ match sections. len ( ) {
84
+ 0 => Ok ( ( ) ) ,
85
+ 1 => {
86
+ let limits = sections[ 0 ] . limits ( ) ;
87
+ if let Some ( maximum) = limits. maximum ( ) {
88
+ if limits. initial ( ) > maximum {
89
+ return Err ( VmError :: static_validation_err (
90
+ "Wasm contract's first table section has a initial limit > max limit" ,
91
+ ) ) ;
92
+ }
93
+ if maximum > TABLE_SIZE_LIMIT {
94
+ return Err ( VmError :: static_validation_err (
95
+ "Wasm contract's first table section has a too large max limit" ,
96
+ ) ) ;
97
+ }
98
+ Ok ( ( ) )
99
+ } else {
100
+ Err ( VmError :: static_validation_err (
101
+ "Wasm contract must not have unbound table section" ,
102
+ ) )
103
+ }
104
+ }
105
+ _ => Err ( VmError :: static_validation_err (
106
+ "Wasm contract must not have more than 1 table section" ,
107
+ ) ) ,
108
+ }
109
+ }
110
+
64
111
fn check_wasm_memories ( module : & Module ) -> VmResult < ( ) > {
65
112
let section = match module. memory_section ( ) {
66
113
Some ( section) => section,
@@ -252,6 +299,38 @@ mod tests {
252
299
} ;
253
300
}
254
301
302
+ #[ test]
303
+ fn check_wasm_tables_works ( ) {
304
+ // No tables is fine
305
+ let wasm = wat:: parse_str ( "(module)" ) . unwrap ( ) ;
306
+ check_wasm_tables ( & deserialize_wasm ( & wasm) . unwrap ( ) ) . unwrap ( ) ;
307
+
308
+ // One table (bound)
309
+ let wasm = wat:: parse_str ( "(module (table $name 123 123 funcref))" ) . unwrap ( ) ;
310
+ check_wasm_tables ( & deserialize_wasm ( & wasm) . unwrap ( ) ) . unwrap ( ) ;
311
+
312
+ // One table (bound, initial > max)
313
+ let wasm = wat:: parse_str ( "(module (table $name 124 123 funcref))" ) . unwrap ( ) ;
314
+ let err = check_wasm_tables ( & deserialize_wasm ( & wasm) . unwrap ( ) ) . unwrap_err ( ) ;
315
+ assert ! ( err
316
+ . to_string( )
317
+ . contains( "Wasm contract's first table section has a initial limit > max limit" ) ) ;
318
+
319
+ // One table (bound, max too large)
320
+ let wasm = wat:: parse_str ( "(module (table $name 100 9999 funcref))" ) . unwrap ( ) ;
321
+ let err = check_wasm_tables ( & deserialize_wasm ( & wasm) . unwrap ( ) ) . unwrap_err ( ) ;
322
+ assert ! ( err
323
+ . to_string( )
324
+ . contains( "Wasm contract's first table section has a too large max limit" ) ) ;
325
+
326
+ // One table (unbound)
327
+ let wasm = wat:: parse_str ( "(module (table $name 100 funcref))" ) . unwrap ( ) ;
328
+ let err = check_wasm_tables ( & deserialize_wasm ( & wasm) . unwrap ( ) ) . unwrap_err ( ) ;
329
+ assert ! ( err
330
+ . to_string( )
331
+ . contains( "Wasm contract must not have unbound table section" ) ) ;
332
+ }
333
+
255
334
#[ test]
256
335
fn check_wasm_memories_ok ( ) {
257
336
let wasm = wat:: parse_str ( "(module (memory 1))" ) . unwrap ( ) ;
0 commit comments