structs2.js (12179B)
1 // |jit-test| test-also=--gc-zeal=2 2 3 // This tests 8- and 16-bit field accesses for structs. 4 5 // Check that struct.new writes for 8-bit fields do not overwrite subsequent 6 // data. Because the writes happen forwards in the address space, the only 7 // way I could think to do this is to force an 8-bit field to occupy the last 8 // byte of the OOL malloc'd block, and then hope that ASan runs in automation 9 // will pick up any overrun. I think it's impossible to test this from inside 10 // the JS+wasm universe. Hence the test is pretty pointless from a purely 11 // JS+wasm interpretation. 12 { 13 let txt = 14 `(module 15 (type $hasOOL (struct 16 ;; In-line storage; 16 fields that preserve 16-alignment 17 (field i64) (field i64) (field i64) (field i64) ;; 32 18 (field i64) (field i64) (field i64) (field i64) ;; 64 19 (field i64) (field i64) (field i64) (field i64) ;; 96 20 (field i64) (field i64) (field i64) (field i64) ;; 128 21 ;; Out-of-line storage (or maybe it starts earlier, but 22 ;; definitely not after this point). 16 bytes on the 23 ;; basis that StructLayout::close will round the requested 24 ;; block size up to at max the next 16 byte boundary. 25 ;; The goal is that the last (field i8) is right at the 26 ;; end of the resulting malloc'd block, so that, if the 27 ;; struct.new initialisation code mistakenly initialises 28 ;; that field with a write larger than 8 bits, then we'll 29 ;; have a write off the end of the malloc'd block, which 30 ;; ASan automation runs should detect. 31 (field i8) (field i8) (field i8) (field i8) 32 (field i8) (field i8) (field i8) (field i8) 33 (field i8) (field i8) (field i8) (field i8) 34 (field i8) (field i8) (field i8) (field i8)) 35 ) 36 (func (export "build8") 37 (param $filler i64) (param $interesting i32) (result eqref) 38 (struct.new $hasOOL 39 (local.get $filler) (local.get $filler) 40 (local.get $filler) (local.get $filler) 41 (local.get $filler) (local.get $filler) 42 (local.get $filler) (local.get $filler) 43 (local.get $filler) (local.get $filler) 44 (local.get $filler) (local.get $filler) 45 (local.get $filler) (local.get $filler) 46 (local.get $filler) (local.get $filler) 47 (local.get $interesting) (local.get $interesting) 48 (local.get $interesting) (local.get $interesting) 49 (local.get $interesting) (local.get $interesting) 50 (local.get $interesting) (local.get $interesting) 51 (local.get $interesting) (local.get $interesting) 52 (local.get $interesting) (local.get $interesting) 53 (local.get $interesting) (local.get $interesting) 54 (local.get $interesting) (local.get $interesting) 55 ) 56 ) 57 )`; 58 let exports = wasmEvalText(txt).exports; 59 let obj8 = exports.build8(0x1234n, 0x5678); 60 // The above call should trigger OOB writes if the struct.new field 61 // writes are too large, but those will only be visible if we're running 62 // on ASan or Valgrind. In any case, add a fake data dependency below, so 63 // that the construction of the object can't (so easily) be optimised away. 64 assertEq(wasmGcReadField(obj8, 0) + BigInt(wasmGcReadField(obj8, 31)), 0x12ACn); // == 0x1234 + 0x78 65 } 66 67 // And exactly the same, except for 16 bit fields. 68 { 69 let txt = 70 `(module 71 (type $hasOOL (struct 72 ;; in-line storage 73 (field i64) (field i64) (field i64) (field i64) ;; 32 74 (field i64) (field i64) (field i64) (field i64) ;; 64 75 (field i64) (field i64) (field i64) (field i64) ;; 96 76 (field i64) (field i64) (field i64) (field i64) ;; 128 77 (field i16) (field i16) (field i16) (field i16) 78 (field i16) (field i16) (field i16) (field i16)) 79 ) 80 (func (export "build16") 81 (param $filler i64) (param $interesting i32) (result eqref) 82 (struct.new $hasOOL 83 (local.get $filler) (local.get $filler) 84 (local.get $filler) (local.get $filler) 85 (local.get $filler) (local.get $filler) 86 (local.get $filler) (local.get $filler) 87 (local.get $filler) (local.get $filler) 88 (local.get $filler) (local.get $filler) 89 (local.get $filler) (local.get $filler) 90 (local.get $filler) (local.get $filler) 91 (local.get $interesting) (local.get $interesting) 92 (local.get $interesting) (local.get $interesting) 93 (local.get $interesting) (local.get $interesting) 94 (local.get $interesting) (local.get $interesting) 95 ) 96 ) 97 )`; 98 let exports = wasmEvalText(txt).exports; 99 let obj16 = exports.build16(0x4321n, 0x7865); 100 assertEq(wasmGcReadField(obj16, 0) + BigInt(wasmGcReadField(obj16, 23)), 0xBB86n); // == 0x4321 + 0x7865 101 } 102 103 // Test that 8-bit field writes do not overwrite adjacent fields. 104 { 105 let txt = 106 `(module 107 (type $struct8x8 108 (struct (field i8) (field i8) (field i8) (field (mut i8)) 109 (field i8) (field i8) (field i8) (field i8) 110 )) 111 (func (export "create") (result eqref) 112 (struct.new $struct8x8 (i32.const 0x55) (i32.const 0x55) 113 (i32.const 0x55) (i32.const 0x55) 114 (i32.const 0x55) (i32.const 0x55) 115 (i32.const 0x55) (i32.const 0x55) 116 )) 117 (func (export "writeField8x8_3") (param $p eqref) (param $v i32) 118 (struct.set $struct8x8 3 (ref.cast (ref null $struct8x8) (local.get $p)) 119 (local.get $v)) 120 ) 121 )`; 122 let exports = wasmEvalText(txt).exports; 123 let theObject = exports.create(); 124 exports.writeField8x8_3(theObject, 0x77); 125 assertEq(wasmGcReadField(theObject, 0), 0x55); 126 assertEq(wasmGcReadField(theObject, 1), 0x55); 127 assertEq(wasmGcReadField(theObject, 2), 0x55); 128 assertEq(wasmGcReadField(theObject, 3), 0x77); 129 assertEq(wasmGcReadField(theObject, 4), 0x55); 130 assertEq(wasmGcReadField(theObject, 5), 0x55); 131 assertEq(wasmGcReadField(theObject, 6), 0x55); 132 assertEq(wasmGcReadField(theObject, 7), 0x55); 133 } 134 135 // Test that 16-bit field writes do not overwrite adjacent fields. 136 { 137 let txt = 138 `(module 139 (type $struct16x8 140 (struct (field i16) (field i16) (field i16) (field (mut i16)) 141 (field i16) (field i16) (field i16) (field i16) 142 )) 143 (func (export "create") (result eqref) 144 (struct.new $struct16x8 (i32.const 0x5555) (i32.const 0x5555) 145 (i32.const 0x5555) (i32.const 0x5555) 146 (i32.const 0x5555) (i32.const 0x5555) 147 (i32.const 0x5555) (i32.const 0x5555) 148 )) 149 (func (export "writeField16x8_3") (param $p eqref) (param $v i32) 150 (struct.set $struct16x8 3 (ref.cast (ref null $struct16x8) (local.get $p)) 151 (local.get $v)) 152 ) 153 )`; 154 let exports = wasmEvalText(txt).exports; 155 let theObject = exports.create(); 156 exports.writeField16x8_3(theObject, 0x7766); 157 assertEq(wasmGcReadField(theObject, 0), 0x5555); 158 assertEq(wasmGcReadField(theObject, 1), 0x5555); 159 assertEq(wasmGcReadField(theObject, 2), 0x5555); 160 assertEq(wasmGcReadField(theObject, 3), 0x7766); 161 assertEq(wasmGcReadField(theObject, 4), 0x5555); 162 assertEq(wasmGcReadField(theObject, 5), 0x5555); 163 assertEq(wasmGcReadField(theObject, 6), 0x5555); 164 assertEq(wasmGcReadField(theObject, 7), 0x5555); 165 } 166 167 // Test that 8-bit field reads sign/zero extend correctly. 168 { 169 let txt = 170 `(module 171 (type $struct8x8 172 (struct (field i8) (field i8) (field i8) (field i8) 173 (field i8) (field i8) (field i8) (field i8) 174 )) 175 (func (export "create") (result eqref) 176 (struct.new $struct8x8 (i32.const 0x11) (i32.const 0x82) 177 (i32.const 0x23) (i32.const 0x94) 178 (i32.const 0x35) (i32.const 0xA6) 179 (i32.const 0x47) (i32.const 0xB8) 180 )) 181 ;; read i8 from a field, unsigned extend, read value has top bit 0 182 (func (export "readU8hi0") (param $p eqref) (result i32) 183 (struct.get_u $struct8x8 2 (ref.cast (ref null $struct8x8) (local.get $p))) 184 ) 185 ;; read i8 from a field, unsigned extend, read value has top bit 1 186 (func (export "readU8hi1") (param $p eqref) (result i32) 187 (struct.get_u $struct8x8 3 (ref.cast (ref null $struct8x8) (local.get $p))) 188 ) 189 ;; read i8 from a field, signed extend, read value has top bit 0 190 (func (export "readS8hi0") (param $p eqref) (result i32) 191 (struct.get_s $struct8x8 4 (ref.cast (ref null $struct8x8) (local.get $p))) 192 ) 193 ;; read i8 from a field, signed extend, read value has top bit 1 194 (func (export "readS8hi1") (param $p eqref) (result i32) 195 (struct.get_s $struct8x8 5 (ref.cast (ref null $struct8x8) (local.get $p))) 196 ) 197 )`; 198 let exports = wasmEvalText(txt).exports; 199 let theObject = exports.create(); 200 assertEq(exports.readU8hi0(theObject), 0x23); // zx of 0x23 201 assertEq(exports.readU8hi1(theObject), 0x94); // zx of 0x94 202 assertEq(exports.readS8hi0(theObject), 0x35); // sx of 0x35 203 assertEq(exports.readS8hi1(theObject), -0x5A); // sx of 0xA6 204 } 205 206 // Test that 16-bit field reads sign/zero extend correctly. 207 { 208 let txt = 209 `(module 210 (type $struct16x8 211 (struct (field i16) (field i16) (field i16) (field i16) 212 (field i16) (field i16) (field i16) (field i16) 213 )) 214 (func (export "create") (result eqref) 215 (struct.new $struct16x8 (i32.const 0x11FF) (i32.const 0x82FE) 216 (i32.const 0x23FD) (i32.const 0x94FC) 217 (i32.const 0x35FB) (i32.const 0xA6FA) 218 (i32.const 0x47F9) (i32.const 0xB8F8) 219 )) 220 ;; read i16 from a field, unsigned extend, read value has top bit 0 221 (func (export "readU16hi0") (param $p eqref) (result i32) 222 (struct.get_u $struct16x8 2 (ref.cast (ref null $struct16x8) (local.get $p))) 223 ) 224 ;; read i16 from a field, unsigned extend, read value has top bit 1 225 (func (export "readU16hi1") (param $p eqref) (result i32) 226 (struct.get_u $struct16x8 3 (ref.cast (ref null $struct16x8) (local.get $p))) 227 ) 228 ;; read i16 from a field, signed extend, read value has top bit 0 229 (func (export "readS16hi0") (param $p eqref) (result i32) 230 (struct.get_s $struct16x8 4 (ref.cast (ref null $struct16x8) (local.get $p))) 231 ) 232 ;; read i16 from a field, signed extend, read value has top bit 1 233 (func (export "readS16hi1") (param $p eqref) (result i32) 234 (struct.get_s $struct16x8 5 (ref.cast (ref null $struct16x8) (local.get $p))) 235 ) 236 )`; 237 let exports = wasmEvalText(txt).exports; 238 let theObject = exports.create(); 239 assertEq(exports.readU16hi0(theObject), 0x23FD); // zx of 0x23FD 240 assertEq(exports.readU16hi1(theObject), 0x94FC); // zx of 0x94FC 241 assertEq(exports.readS16hi0(theObject), 0x35FB); // sx of 0x35FB 242 assertEq(exports.readS16hi1(theObject), -0x5906); // sx of 0xA6FC 243 }