1 Packing a non-string value with the `c` format converts it through its 2 tostring() callback. The destination address within the buffer was 3 computed before invoking the callback, so a callback growing, moving or 4 pulling the buffer made packing write to freed or foreign memory. An 5 exception or exit() pending once the conversion returned did not stop 6 packing either. 7 8 1. A callback growing the buffer must not leave the character written to 9 the old allocation. 10 11 -- Testcase -- 12 let struct; 13 14 try { 15 struct = require('struct'); 16 } 17 catch (e) { 18 exit(77); 19 } 20 21 let b = struct.buffer('AB'); 22 let value = proto({}, { tostring: () => { 23 b.length(262144); 24 gc(); 25 26 return 'Z'; 27 } }); 28 29 b.put('cB', value, 7); 30 print(hexenc(b.slice(0, 4)), ':', b.pos(), ':', b.length(), '\n'); 31 -- End -- 32 33 -- Args -- 34 -R 35 -- End -- 36 37 -- Expect stdout -- 38 5a070000:2:262144 39 -- End -- 40 41 42 2. Repeated characters and subsequent fields keep their offsets when 43 callbacks grow the buffer or move its position, in all byte orders. 44 45 -- Testcase -- 46 let struct; 47 48 try { 49 struct = require('struct'); 50 } 51 catch (e) { 52 exit(77); 53 } 54 55 for (let order in [ '', '@', '=', '<', '>', '!' ]) { 56 let b = struct.buffer('ABCD'); 57 b.pos(1); 58 59 let first = proto({}, { tostring: () => { 60 b.length(262144); 61 62 return 'Z'; 63 } }); 64 65 let second = proto({}, { tostring: () => { 66 b.length(524288); 67 b.pos(4096); 68 gc(); 69 70 return 'Y'; 71 } }); 72 73 b.put(order + '2cB', first, second, 7); 74 print(hexenc(b.slice(0, 4)), ':', b.pos(), ':', b.length(), '\n'); 75 } 76 -- End -- 77 78 -- Args -- 79 -R 80 -- End -- 81 82 -- Expect stdout -- 83 415a5907:4:524288 84 415a5907:4:524288 85 415a5907:4:524288 86 415a5907:4:524288 87 415a5907:4:524288 88 415a5907:4:524288 89 -- End -- 90 91 92 3. A callback pulling the buffer contents must not see the returned 93 string modified, packing continues in a newly allocated buffer. 94 95 -- Testcase -- 96 let struct; 97 98 try { 99 struct = require('struct'); 100 } 101 catch (e) { 102 exit(77); 103 } 104 105 let b = struct.buffer('ABCD'); 106 let saved; 107 108 b.pos(1); 109 b.put('cB', proto({}, { tostring: () => { 110 saved = b.pull(); 111 gc(); 112 113 return 'Z'; 114 } }), 7); 115 116 print(hexenc(saved), '\n'); 117 print(hexenc(b.slice()), ':', b.pos(), ':', b.length(), '\n'); 118 -- End -- 119 120 -- Args -- 121 -R 122 -- End -- 123 124 -- Expect stdout -- 125 41424344 126 005a07:3:3 127 -- End -- 128 129 130 4. An exception pending after the conversion stops packing without 131 invoking callbacks of further fields, previous writes are retained and 132 the buffer stays usable. 133 134 -- Testcase -- 135 let struct; 136 137 try { 138 struct = require('struct'); 139 } 140 catch (e) { 141 exit(77); 142 } 143 144 let b = struct.buffer('ABCD'); 145 146 try { 147 b.put('B2c', 7, 148 proto({}, { tostring: () => { 149 b.length(262144); 150 gc(); 151 die('conversion failed'); 152 } }), 153 proto({}, { tostring: () => { 154 print('unexpected callback\n'); 155 156 return 'Y'; 157 } })); 158 } 159 catch (e) { 160 print(e.message, '\n'); 161 } 162 163 print(hexenc(b.slice(0, 4)), ':', b.pos(), ':', b.length(), '\n'); 164 165 b.put('c', 'Z'); 166 print(hexenc(b.slice(0, 4)), ':', b.pos(), '\n'); 167 -- End -- 168 169 -- Args -- 170 -R 171 -- End -- 172 173 -- Expect stdout -- 174 conversion failed 175 07424344:0:262144 176 5a424344:1 177 -- End -- 178 179 180 5. Calling exit() within a callback releases a temporary buffer along 181 with the VM stack, packing must not access it afterwards. 182 183 -- Testcase -- 184 let struct; 185 186 try { 187 struct = require('struct'); 188 } 189 catch (e) { 190 exit(77); 191 } 192 193 struct.buffer().put('2c', 194 proto({}, { tostring: () => exit(3) }), 195 proto({}, { tostring: () => { 196 print('unexpected callback\n'); 197 198 return 'Y'; 199 } })); 200 201 print('not reached\n'); 202 -- End -- 203 204 -- Args -- 205 -R 206 -- End -- 207 208 -- Expect exitcode -- 209 3 210 -- End --
This page was automatically generated by LXR 0.3.1. • OpenWrt