• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/tests/custom/99_bugs/62_struct_pack_char_callback_buffer

  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