/home/travis/build/MoarVM/MoarVM/src/core/validation.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* TODO: validate |
4 | | * - args of prepargs, getcode |
5 | | * - any cu->strings index (28 currently) |
6 | | * - cur_frame->args indexes |
7 | | */ |
8 | | |
9 | | /* Macros for getting things from the bytecode stream. */ |
10 | | /* GET_REG is defined differently here from interp.c */ |
11 | 18.7M | #define GET_REG(pc, idx) *((MVMuint16 *)(pc + idx)) |
12 | | #define GET_I16(pc, idx) *((MVMint16 *)(pc + idx)) |
13 | 3.27M | #define GET_UI16(pc, idx) *((MVMuint16 *)(pc + idx)) |
14 | | #define GET_I32(pc, idx) *((MVMint32 *)(pc + idx)) |
15 | 3.10M | #define GET_UI32(pc, idx) *((MVMuint32 *)(pc + idx)) |
16 | | #define GET_N32(pc, idx) *((MVMnum32 *)(pc + idx)) |
17 | | |
18 | 0 | #define MSG(val, msg) "Bytecode validation error at offset %" PRIu32 \ |
19 | 0 | ", instruction %" PRIu32 ":\n" msg, \ |
20 | 0 | (MVMuint32)((val)->cur_op - (val)->bc_start), (val)->cur_instr |
21 | | |
22 | | enum { |
23 | | MARK_regular = ' ', |
24 | | MARK_special = '.', |
25 | | MARK_sequence = ':', |
26 | | MARK_head = '+', |
27 | | MARK_body = '*', |
28 | | MARK_tail = '-' |
29 | | }; |
30 | | |
31 | | typedef struct { |
32 | | MVMThreadContext *tc; |
33 | | MVMCompUnit *cu; |
34 | | MVMStaticFrame *frame; |
35 | | MVMuint32 loc_count; |
36 | | MVMuint16 *loc_types; |
37 | | MVMuint32 bc_size; |
38 | | MVMuint8 *bc_start; |
39 | | MVMuint8 *bc_end; |
40 | | MVMuint8 *src_cur_op; |
41 | | MVMuint8 *src_bc_end; |
42 | | MVMuint8 *labels; |
43 | | MVMuint8 *cur_op; |
44 | | const MVMOpInfo *cur_info; |
45 | | const char *cur_mark; |
46 | | MVMuint32 cur_instr; |
47 | | MVMCallsite *cur_call; |
48 | | MVMuint16 cur_arg; |
49 | | MVMint32 acceptable_max_arity; |
50 | | MVMint16 checkarity_seen; |
51 | | MVMCallsiteEntry expected_named_arg; |
52 | | MVMuint16 remaining_args; |
53 | | MVMuint16 remaining_positionals; |
54 | | MVMuint32 remaining_jumplabels; |
55 | | MVMuint32 reg_type_var; |
56 | | } Validator; |
57 | | |
58 | | |
59 | | MVM_NO_RETURN static void fail(Validator *val, const char *msg, ...) MVM_FORMAT(printf, 2, 3) MVM_NO_RETURN_ATTRIBUTE; |
60 | 0 | static void fail(Validator *val, const char *msg, ...) { |
61 | 0 | va_list args; |
62 | 0 |
|
63 | 0 | va_start(args, msg); |
64 | 0 |
|
65 | 0 | MVM_free(val->labels); |
66 | 0 | MVM_exception_throw_adhoc_va(val->tc, msg, args); |
67 | 0 |
|
68 | 0 | va_end(args); |
69 | 0 | } |
70 | | |
71 | | |
72 | 0 | static void fail_illegal_mark(Validator *val) { |
73 | 0 | fail(val, MSG(val, "illegal op mark '%.2s'"), val->cur_mark); |
74 | 0 | } |
75 | | |
76 | | |
77 | 40.3M | static void ensure_bytes(Validator *val, MVMuint32 count) { |
78 | 40.3M | if (val->src_cur_op + count > val->src_bc_end) |
79 | 0 | fail(val, MSG(val, "truncated stream")); |
80 | 40.3M | #ifdef MVM_BIGENDIAN |
81 | | /* Endian swap equivalent of memcpy(val->cur_op, val->src_cur_op, count); */ |
82 | | { |
83 | | MVMuint8 *d = val->cur_op + count; |
84 | | while (count--) { |
85 | | *--d = *val->src_cur_op++; |
86 | | } |
87 | | } |
88 | | #else |
89 | 40.3M | val->src_cur_op += count; |
90 | 40.3M | #endif |
91 | 40.3M | } |
92 | | |
93 | | |
94 | 680k | static void ensure_op(Validator *val, MVMuint16 opcode) { |
95 | 680k | if (val->cur_info->opcode != opcode) { |
96 | 0 | fail(val, MSG(val, "expected op %s but got %s"), |
97 | 0 | MVM_op_get_op(opcode)->name, val->cur_info->name); |
98 | 0 | } |
99 | 680k | } |
100 | | |
101 | | |
102 | 11.6k | static void ensure_no_remaining_jumplabels(Validator *val) { |
103 | 11.6k | if (val->remaining_jumplabels != 0) |
104 | 0 | fail(val, MSG(val, "%" PRIu32 " jumplist labels missing their goto ops"), |
105 | 0 | val->remaining_jumplabels); |
106 | 11.6k | } |
107 | | |
108 | | |
109 | 565k | static void ensure_no_remaining_positionals(Validator *val) { |
110 | 565k | if (val->remaining_positionals != 0) |
111 | 0 | fail(val, MSG(val, "callsite expects %" PRIu16 " more positionals"), |
112 | 0 | val->remaining_positionals); |
113 | 565k | } |
114 | | |
115 | | |
116 | 0 | static void ensure_no_remaining_args(Validator *val) { |
117 | 0 | if (val->remaining_args != 0) |
118 | 0 | fail(val, MSG(val, "callsite expects %" PRIu16 " more args"), |
119 | 0 | val->remaining_args); |
120 | 0 | } |
121 | | |
122 | | |
123 | 13.0M | MVM_STATIC_INLINE const MVMOpInfo * get_info(Validator *val, MVMuint16 opcode) { |
124 | 13.0M | const MVMOpInfo *info; |
125 | 13.0M | |
126 | 13.0M | if (opcode < MVM_OP_EXT_BASE) { |
127 | 13.0M | info = MVM_op_get_op(opcode); |
128 | 13.0M | if (!info) |
129 | 0 | fail(val, MSG(val, "invalid opcode %u"), opcode); |
130 | 13.0M | } |
131 | 0 | else { |
132 | 0 | MVMuint16 index = opcode - MVM_OP_EXT_BASE; |
133 | 0 | MVMExtOpRecord *record; |
134 | 0 |
|
135 | 0 | if (index >= val->cu->body.num_extops) |
136 | 0 | fail(val, MSG(val, |
137 | 0 | "invalid extension opcode %u - should be less than %u"), |
138 | 0 | opcode, MVM_OP_EXT_BASE + val->cu->body.num_extops); |
139 | 0 |
|
140 | 0 | record = &val->cu->body.extops[index]; |
141 | 0 | info = MVM_ext_resolve_extop_record(val->tc, record); |
142 | 0 | if (!info) |
143 | 0 | fail(val, MSG(val, "extension op '%s' not registered"), |
144 | 0 | MVM_string_utf8_encode_C_string(val->tc, record->name)); |
145 | 0 | } |
146 | 13.0M | |
147 | 13.0M | return info; |
148 | 13.0M | } |
149 | | |
150 | | |
151 | 13.0M | MVM_STATIC_INLINE void read_op(Validator *val) { |
152 | 13.0M | MVMuint16 opcode; |
153 | 13.0M | const MVMOpInfo *info; |
154 | 13.0M | MVMuint32 pos; |
155 | 13.0M | |
156 | 13.0M | ensure_bytes(val, 2); |
157 | 13.0M | |
158 | 13.0M | opcode = *(MVMuint16 *)val->cur_op; |
159 | 13.0M | info = get_info(val, opcode); |
160 | 13.0M | pos = val->cur_op - val->bc_start; |
161 | 13.0M | |
162 | 13.0M | #if 0 |
163 | | MVM_string_print(val->tc, val->cu->body.filename); |
164 | | printf(" %u %s %.2s\n", val->cur_instr, info->name, info->mark); |
165 | | #endif |
166 | 13.0M | |
167 | 13.0M | val->labels[pos] |= MVM_BC_op_boundary; |
168 | 13.0M | val->cur_info = info; |
169 | 13.0M | val->cur_mark = info->mark; |
170 | 13.0M | val->cur_op += 2; |
171 | 13.0M | val->cur_instr += 1; |
172 | 13.0M | } |
173 | | |
174 | | |
175 | 0 | static void unread_op(Validator *val) { |
176 | 0 | val->src_cur_op -= 2; |
177 | 0 | val->cur_op -= 2; |
178 | 0 | val->cur_instr -= 1; |
179 | 0 | } |
180 | | |
181 | | |
182 | 117k | static void validate_branch_targets(Validator *val) { |
183 | 117k | MVMuint32 pos, instr; |
184 | 117k | |
185 | 87.1M | for (pos = 0, instr = (MVMuint32)-1; pos < val->bc_size; pos++) { |
186 | 87.0M | MVMuint32 flag = val->labels[pos]; |
187 | 87.0M | |
188 | 87.0M | if (flag & MVM_BC_op_boundary) |
189 | 13.0M | instr++; |
190 | 87.0M | |
191 | 87.0M | if ((flag & MVM_BC_branch_target) && !(flag & MVM_BC_op_boundary)) |
192 | 0 | fail(val, MSG(val, "branch targets offset %" PRIu32 |
193 | 0 | "within instruction %" PRIu32), pos, instr); |
194 | 87.0M | } |
195 | 117k | } |
196 | | |
197 | | |
198 | 117k | static void validate_final_return(Validator *val) { |
199 | 117k | if (!val->bc_size || val->cur_mark[1] != 'r') |
200 | 0 | fail(val, MSG(val, "missing final return instruction")); |
201 | 117k | } |
202 | | |
203 | | |
204 | 7.97M | static void validate_literal_operand(Validator *val, MVMuint32 flags) { |
205 | 7.97M | MVMuint32 type = flags & MVM_operand_type_mask; |
206 | 7.97M | MVMuint32 size; |
207 | 7.97M | |
208 | 7.97M | switch (type) { |
209 | 0 | case MVM_operand_int8: size = 1; break; |
210 | 3.51M | case MVM_operand_int16: size = 2; break; |
211 | 1.42k | case MVM_operand_int32: size = 4; break; |
212 | 26.2k | case MVM_operand_int64: size = 8; break; |
213 | 0 | case MVM_operand_num32: size = 4; break; |
214 | 591 | case MVM_operand_num64: size = 8; break; |
215 | 565k | case MVM_operand_callsite: size = 2; break; |
216 | 760k | case MVM_operand_coderef: size = 2; break; |
217 | 1.72M | case MVM_operand_str: size = 4; break; |
218 | 1.37M | case MVM_operand_ins: size = 4; break; |
219 | 0 |
|
220 | 0 | case MVM_operand_obj: |
221 | 0 | case MVM_operand_type_var: |
222 | 0 | fail(val, MSG(val, "operand type %i can't be a literal"), type); |
223 | 0 |
|
224 | 0 | default: |
225 | 0 | fail(val, MSG(val, "unknown operand type %i"), type); |
226 | 7.97M | } |
227 | 7.97M | |
228 | 7.97M | ensure_bytes(val, size); |
229 | 7.97M | |
230 | 7.97M | switch (type) { |
231 | 565k | case MVM_operand_callsite: { |
232 | 565k | MVMuint16 index = GET_UI16(val->cur_op, 0); |
233 | 565k | MVMuint32 count = val->cu->body.orig_callsites; |
234 | 565k | if (index >= count) |
235 | 0 | fail(val, MSG(val, "callsite index %" PRIu16 |
236 | 0 | " out of range 0..%" PRIu32), index, count - 1); |
237 | 565k | break; |
238 | 565k | } |
239 | 565k | |
240 | 760k | case MVM_operand_coderef: { |
241 | 760k | MVMuint16 index = GET_UI16(val->cur_op, 0); |
242 | 760k | MVMuint32 count = val->cu->body.orig_frames; |
243 | 760k | if (index >= count) |
244 | 0 | fail(val, MSG(val, "coderef index %" PRIu16 |
245 | 0 | " out of range 0..%" PRIu32), index, count - 1); |
246 | 760k | break; |
247 | 565k | } |
248 | 565k | |
249 | 1.72M | case MVM_operand_str: { |
250 | 1.72M | MVMuint32 index = GET_UI32(val->cur_op, 0); |
251 | 1.72M | MVMuint32 count = val->cu->body.orig_strings; |
252 | 1.72M | if (index >= count) |
253 | 0 | fail(val, MSG(val, "string index %" PRIu32 |
254 | 0 | " out of range 0..%" PRIu32), index, count - 1); |
255 | 1.72M | break; |
256 | 565k | } |
257 | 565k | |
258 | 1.37M | case MVM_operand_ins: { |
259 | 1.37M | MVMuint32 offset = GET_UI32(val->cur_op, 0); |
260 | 1.37M | if (offset >= val->bc_size) |
261 | 0 | fail(val, MSG(val, "branch instruction offset %" PRIu32 |
262 | 0 | " out of range 0..%" PRIu32), offset, val->bc_size - 1); |
263 | 1.37M | val->labels[offset] |= MVM_BC_branch_target; |
264 | 1.37M | } |
265 | 7.97M | } |
266 | 7.97M | |
267 | 7.97M | val->cur_op += size; |
268 | 7.97M | } |
269 | | |
270 | | |
271 | 18.7M | static void validate_reg_operand(Validator *val, MVMuint32 flags) { |
272 | 18.7M | MVMuint32 operand_type = flags & MVM_operand_type_mask; |
273 | 18.7M | MVMuint32 reg_type; |
274 | 18.7M | MVMuint16 reg; |
275 | 18.7M | |
276 | 18.7M | ensure_bytes(val, 2); |
277 | 18.7M | |
278 | 18.7M | reg = GET_REG(val->cur_op, 0); |
279 | 18.7M | if (reg >= val->loc_count) |
280 | 0 | fail(val, MSG(val, "register operand index %" PRIu16 |
281 | 0 | " out of range 0..%" PRIu32), reg, val->loc_count - 1); |
282 | 18.7M | |
283 | 18.7M | reg_type = val->loc_types[reg] << 3; |
284 | 18.7M | |
285 | 18.7M | if (operand_type == MVM_operand_type_var) { |
286 | 2.30M | if (!val->reg_type_var) { |
287 | 1.20M | val->reg_type_var = reg_type; |
288 | 1.20M | goto next_operand; |
289 | 1.20M | } |
290 | 2.30M | |
291 | 1.09M | operand_type = val->reg_type_var; |
292 | 1.09M | } |
293 | 18.7M | |
294 | 17.5M | if (reg_type != operand_type) |
295 | 0 | fail(val, MSG(val, "operand type %i does not match register type %i"), |
296 | 0 | operand_type, reg_type); |
297 | 17.5M | |
298 | 18.7M | next_operand: |
299 | 18.7M | val->cur_op += 2; |
300 | 18.7M | } |
301 | | |
302 | | |
303 | 281k | static void validate_lex_operand(Validator *val, MVMuint32 flags) { |
304 | 281k | MVMuint32 operand_type = flags & MVM_operand_type_mask; |
305 | 281k | MVMuint16 lex_index, frame_index, i; |
306 | 281k | MVMuint32 lex_count, lex_type; |
307 | 281k | MVMStaticFrame *frame = val->frame; |
308 | 281k | |
309 | 281k | /* Two steps forward, two steps back to keep the error reporting happy, |
310 | 281k | and to make the endian conversion within ensure_bytes correct. |
311 | 281k | (Both are using val->cur_op, and want it to have different values.) */ |
312 | 281k | ensure_bytes(val, 2); |
313 | 281k | lex_index = GET_UI16(val->cur_op, 0); |
314 | 281k | val->cur_op += 2; |
315 | 281k | ensure_bytes(val, 2); |
316 | 281k | val->cur_op -= 2; |
317 | 281k | frame_index = GET_UI16(val->cur_op, 2); |
318 | 281k | |
319 | 446k | for (i = frame_index; i; i--) { |
320 | 165k | frame = frame->body.outer; |
321 | 165k | if (!frame) |
322 | 0 | fail(val, MSG(val, "lexical operand requires %" PRIu16 |
323 | 0 | " more enclosing scopes"), i); |
324 | 165k | } |
325 | 281k | |
326 | 281k | if (!frame->body.fully_deserialized) |
327 | 1 | MVM_bytecode_finish_frame(val->tc, frame->body.cu, frame, 0); |
328 | 281k | |
329 | 281k | lex_count = frame->body.num_lexicals; |
330 | 281k | if (lex_index >= lex_count) |
331 | 0 | fail(val, MSG(val, "lexical operand index %" PRIu16 |
332 | 0 | " out of range 0.. %" PRIu32), lex_index, lex_count - 1); |
333 | 281k | |
334 | 281k | lex_type = frame->body.lexical_types[lex_index] << 3; |
335 | 281k | |
336 | 281k | if (operand_type == MVM_operand_type_var) { |
337 | 281k | if (!val->reg_type_var) { |
338 | 90.6k | val->reg_type_var = lex_type; |
339 | 90.6k | goto next_operand; |
340 | 90.6k | } |
341 | 281k | |
342 | 190k | operand_type = val->reg_type_var; |
343 | 190k | } |
344 | 281k | |
345 | 190k | if (lex_type != operand_type) |
346 | 0 | fail(val, MSG(val, "operand type %i does not match lexical type %i"), |
347 | 0 | operand_type, lex_type); |
348 | 190k | |
349 | 281k | next_operand: |
350 | 281k | val->cur_op += 4; |
351 | 281k | } |
352 | | |
353 | | |
354 | 25.1M | static void validate_operand(Validator *val, MVMuint32 flags) { |
355 | 25.1M | MVMuint32 rw = flags & MVM_operand_rw_mask; |
356 | 25.1M | |
357 | 25.1M | switch (rw) { |
358 | 6.63M | case MVM_operand_literal: |
359 | 6.63M | validate_literal_operand(val, flags); |
360 | 6.63M | break; |
361 | 6.63M | |
362 | 18.2M | case MVM_operand_read_reg: |
363 | 18.2M | case MVM_operand_write_reg: |
364 | 18.2M | validate_reg_operand(val, flags); |
365 | 18.2M | break; |
366 | 18.2M | |
367 | 281k | case MVM_operand_read_lex: |
368 | 281k | case MVM_operand_write_lex: |
369 | 281k | validate_lex_operand(val, flags); |
370 | 281k | break; |
371 | 281k | |
372 | 0 | default: |
373 | 0 | fail(val, MSG(val, "invalid instruction rw flag %i"), rw); |
374 | 25.1M | } |
375 | 25.1M | } |
376 | | |
377 | | |
378 | 13.0M | static void validate_operands(Validator *val) { |
379 | 13.0M | const MVMuint8 *operands = val->cur_info->operands; |
380 | 13.0M | |
381 | 13.0M | val->reg_type_var = 0; |
382 | 13.0M | |
383 | 13.0M | switch (val->cur_info->opcode) { |
384 | 11.6k | case MVM_OP_jumplist: { |
385 | 11.6k | MVMint64 count; |
386 | 11.6k | |
387 | 11.6k | validate_literal_operand(val, operands[0]); |
388 | 11.6k | count = MVM_BC_get_I64(val->cur_op, -8); |
389 | 11.6k | if (count < 0 || count > UINT32_MAX) |
390 | 0 | fail(val, MSG(val, "illegal jumplist label count %" PRIi64), |
391 | 0 | count); |
392 | 11.6k | |
393 | 11.6k | validate_reg_operand(val, operands[1]); |
394 | 11.6k | |
395 | 11.6k | break; |
396 | 11.6k | } |
397 | 117k | case MVM_OP_checkarity: { |
398 | 117k | validate_literal_operand(val, operands[0]); |
399 | 117k | |
400 | 117k | validate_literal_operand(val, operands[1]); |
401 | 117k | val->acceptable_max_arity = GET_UI16(val->cur_op, -2); |
402 | 117k | val->checkarity_seen = 1; |
403 | 117k | |
404 | 117k | break; |
405 | 11.6k | } |
406 | 545k | case MVM_OP_wval: |
407 | 545k | case MVM_OP_wval_wide: { |
408 | 545k | validate_reg_operand(val, operands[0]); |
409 | 545k | validate_literal_operand(val, operands[1]); |
410 | 545k | if (GET_UI16(val->cur_op, -2) >= val->cu->body.num_scs) |
411 | 0 | fail(val, MSG(val, "out of range SC index %u"), GET_UI16(val->cur_op, -2)); |
412 | 545k | validate_literal_operand(val, operands[2]); |
413 | 545k | break; |
414 | 545k | } |
415 | 545k | |
416 | 12.3M | default: { |
417 | 12.3M | int i; |
418 | 12.3M | |
419 | 12.3M | if (val->cur_mark[1] == 'p') { |
420 | 161k | /* First of all, bail out if no checkarity was seen yet. */ |
421 | 161k | if (!val->checkarity_seen) { |
422 | 0 | fail(val, MSG(val, "param op without checkarity op seen.")); |
423 | 0 | } |
424 | 161k | |
425 | 161k | /* For the p-marked ops, which is a subset of param_* ops, |
426 | 161k | * we check the second argument against the value checkarity |
427 | 161k | * checked against. */ |
428 | 493k | for (i = 0; i < val->cur_info->num_operands; i++) { |
429 | 331k | validate_operand(val, val->cur_info->operands[i]); |
430 | 331k | |
431 | 331k | /* This is the argument we want to check */ |
432 | 331k | if (i == 1) { |
433 | 161k | MVMint16 value = GET_UI16(val->cur_op, -2); |
434 | 161k | if (value > val->acceptable_max_arity) { |
435 | 0 | fail(val, MSG(val, "tried to take arg number %d after checkarity with %d"), value, val->acceptable_max_arity); |
436 | 0 | } |
437 | 161k | } |
438 | 331k | } |
439 | 161k | } |
440 | 12.1M | else { |
441 | 36.9M | for (i = 0; i < val->cur_info->num_operands; i++) |
442 | 24.8M | validate_operand(val, val->cur_info->operands[i]); |
443 | 12.1M | } |
444 | 12.3M | } |
445 | 13.0M | } |
446 | 13.0M | } |
447 | | |
448 | | |
449 | 11.6k | static void validate_sequence(Validator *val) { |
450 | 11.6k | int seq_id = val->cur_mark[1]; |
451 | 11.6k | |
452 | 11.6k | switch (seq_id) { |
453 | 11.6k | case 'j': { |
454 | 11.6k | ensure_op(val, MVM_OP_jumplist); |
455 | 11.6k | validate_operands(val); |
456 | 11.6k | val->remaining_jumplabels = (MVMuint32)MVM_BC_get_I64(val->cur_op, -10); |
457 | 11.6k | break; |
458 | 11.6k | } |
459 | 11.6k | |
460 | 0 | default: |
461 | 0 | fail(val, MSG(val, "unknown instruction sequence '%c'"), seq_id); |
462 | 11.6k | } |
463 | 11.6k | |
464 | 103k | while (val->cur_op < val->bc_end) { |
465 | 103k | int type, id; |
466 | 103k | |
467 | 103k | read_op(val); |
468 | 103k | type = val->cur_mark[0]; |
469 | 103k | id = val->cur_mark[1]; |
470 | 103k | |
471 | 103k | switch (type) { |
472 | 103k | case MARK_special: |
473 | 103k | if (id == seq_id) |
474 | 103k | break; |
475 | 103k | /* FALLTHROUGH */ |
476 | 103k | |
477 | 0 | case MARK_regular: |
478 | 0 | case MARK_sequence: |
479 | 0 | case MARK_head: |
480 | 0 | unread_op(val); |
481 | 0 | goto terminate_seq; |
482 | 0 |
|
483 | 0 | default: |
484 | 0 | fail_illegal_mark(val); |
485 | 103k | } |
486 | 103k | |
487 | 103k | switch (seq_id) { |
488 | 103k | case 'j': |
489 | 103k | ensure_op(val, MVM_OP_goto); |
490 | 103k | validate_operands(val); |
491 | 103k | |
492 | 103k | val->remaining_jumplabels--; |
493 | 103k | if (val->remaining_jumplabels == 0) |
494 | 11.6k | goto terminate_seq; |
495 | 91.6k | break; |
496 | 103k | } |
497 | 103k | } |
498 | 11.6k | |
499 | 11.6k | terminate_seq: |
500 | 11.6k | switch (seq_id) { |
501 | 11.6k | case 'j': |
502 | 11.6k | ensure_no_remaining_jumplabels(val); |
503 | 11.6k | break; |
504 | 11.6k | } |
505 | 11.6k | } |
506 | | |
507 | | |
508 | 1.29M | static void validate_arg(Validator *val) { |
509 | 1.29M | MVMCallsiteEntry flags; |
510 | 1.29M | |
511 | 1.29M | val->remaining_args--; |
512 | 1.29M | |
513 | 1.29M | if (val->expected_named_arg) { |
514 | 87.4k | flags = val->expected_named_arg; |
515 | 87.4k | val->expected_named_arg = 0; |
516 | 87.4k | } |
517 | 1.20M | else { |
518 | 1.20M | MVMuint16 index = val->cur_arg++; |
519 | 1.20M | MVMuint16 count = val->cur_call->arg_count; |
520 | 1.20M | |
521 | 1.20M | if (index >= count) |
522 | 0 | fail (val, MSG(val, "argument index %" PRIu16 |
523 | 0 | " not in range 0..%" PRIu32), index, count - 1); |
524 | 1.20M | |
525 | 1.20M | flags = val->cur_call->arg_flags[index]; |
526 | 1.20M | |
527 | 1.20M | switch (flags & ~MVM_CALLSITE_ARG_MASK) { |
528 | 1.11M | case 0: /* positionals */ |
529 | 1.11M | case MVM_CALLSITE_ARG_FLAT: |
530 | 1.11M | val->remaining_positionals--; |
531 | 1.11M | break; |
532 | 1.11M | |
533 | 3.67k | case MVM_CALLSITE_ARG_FLAT_NAMED: |
534 | 3.67k | /* Nothing to do for this case. */ |
535 | 3.67k | break; |
536 | 1.11M | |
537 | 87.4k | case MVM_CALLSITE_ARG_NAMED: |
538 | 87.4k | val->expected_named_arg = flags & MVM_CALLSITE_ARG_MASK; |
539 | 87.4k | goto named_arg; |
540 | 1.11M | |
541 | 0 | default: |
542 | 0 | fail(val, MSG(val, "invalid argument flags (%i) at index %" |
543 | 0 | PRIu16), (int)(flags & ~MVM_CALLSITE_ARG_MASK), index); |
544 | 1.20M | } |
545 | 1.20M | } |
546 | 1.29M | |
547 | 1.20M | goto regular_arg; |
548 | 1.29M | |
549 | 87.4k | named_arg: |
550 | 87.4k | if (val->cur_info->opcode != MVM_OP_argconst_s) |
551 | 0 | fail(val, MSG(val, "expected instruction 'argconst_s' but got '%s'"), |
552 | 0 | val->cur_info->name); |
553 | 87.4k | return; |
554 | 1.29M | |
555 | 1.20M | regular_arg: |
556 | 1.20M | switch (val->cur_info->opcode) { |
557 | 805k | case MVM_OP_arg_o: |
558 | 805k | if(!(flags & MVM_CALLSITE_ARG_OBJ)) |
559 | 0 | goto fail_arg; |
560 | 805k | break; |
561 | 805k | |
562 | 311k | case MVM_OP_arg_s: |
563 | 311k | if(!(flags & MVM_CALLSITE_ARG_STR)) |
564 | 0 | goto fail_arg; |
565 | 311k | break; |
566 | 311k | |
567 | 0 | case MVM_OP_argconst_s: |
568 | 0 | if(!(flags & MVM_CALLSITE_ARG_STR)) |
569 | 0 | goto fail_arg; |
570 | 0 | break; |
571 | 0 |
|
572 | 89.7k | case MVM_OP_arg_i: |
573 | 89.7k | if(!(flags & MVM_CALLSITE_ARG_INT)) |
574 | 0 | goto fail_arg; |
575 | 89.7k | break; |
576 | 89.7k | |
577 | 992 | case MVM_OP_arg_n: |
578 | 992 | if(!(flags & MVM_CALLSITE_ARG_NUM)) |
579 | 0 | goto fail_arg; |
580 | 992 | break; |
581 | 992 | |
582 | 0 | default: |
583 | 0 | fail(val, MSG(val, |
584 | 0 | "unexpected instruction '%s' during argument preparation"), |
585 | 0 | val->cur_info->name); |
586 | 0 |
|
587 | 0 | fail_arg: |
588 | 0 | fail(val, MSG(val, "invalid argument (%i) for instruction %s"), |
589 | 0 | (int)flags, val->cur_info->name); |
590 | 1.20M | } |
591 | 1.20M | } |
592 | | |
593 | | |
594 | 565k | static void validate_block(Validator *val) { |
595 | 565k | int block_id = val->cur_mark[1]; |
596 | 565k | |
597 | 565k | switch (block_id) { |
598 | 565k | case 'a': { |
599 | 565k | MVMuint16 index; |
600 | 565k | |
601 | 565k | ensure_op(val, MVM_OP_prepargs); |
602 | 565k | validate_operands(val); |
603 | 565k | index = GET_UI16(val->cur_op, -2); |
604 | 565k | val->cur_call = val->cu->body.callsites[index]; |
605 | 565k | val->cur_arg = 0; |
606 | 565k | val->expected_named_arg = 0; |
607 | 565k | val->remaining_args = val->cur_call->arg_count; |
608 | 565k | val->remaining_positionals = val->cur_call->num_pos; |
609 | 565k | |
610 | 565k | break; |
611 | 565k | } |
612 | 565k | |
613 | 0 | default: |
614 | 0 | fail(val, MSG(val, "unknown instruction block '%c'"), block_id); |
615 | 565k | } |
616 | 565k | |
617 | 1.86M | while (val->cur_op < val->bc_end) { |
618 | 1.86M | int type, id; |
619 | 1.86M | |
620 | 1.86M | read_op(val); |
621 | 1.86M | type = val->cur_mark[0]; |
622 | 1.86M | id = val->cur_mark[1]; |
623 | 1.86M | |
624 | 1.86M | if (id != block_id) |
625 | 0 | fail(val, MSG(val, "expected instruction marked '%c' but got '%c'"), |
626 | 0 | block_id, id); |
627 | 1.86M | |
628 | 1.86M | switch (type) { |
629 | 1.29M | case MARK_body: break; |
630 | 565k | case MARK_tail: goto terminate_block; |
631 | 0 | default: fail_illegal_mark(val); |
632 | 1.86M | } |
633 | 1.86M | |
634 | 1.29M | switch (block_id) { |
635 | 1.29M | case 'a': |
636 | 1.29M | validate_operands(val); |
637 | 1.29M | validate_arg(val); |
638 | 1.29M | break; |
639 | 1.29M | } |
640 | 1.29M | } |
641 | 565k | |
642 | 565k | terminate_block: |
643 | 565k | switch (block_id) { |
644 | 565k | case 'a': |
645 | 565k | validate_operands(val); |
646 | 565k | ensure_no_remaining_positionals(val); |
647 | 565k | break; |
648 | 565k | } |
649 | 565k | } |
650 | | |
651 | | |
652 | | /* Validate that a static frame's bytecode is executable by the interpreter. */ |
653 | | void MVM_validate_static_frame(MVMThreadContext *tc, |
654 | 117k | MVMStaticFrame *static_frame) { |
655 | 117k | MVMStaticFrameBody *fb = &static_frame->body; |
656 | 117k | Validator val[1]; |
657 | 117k | |
658 | 117k | val->tc = tc; |
659 | 117k | val->cu = fb->cu; |
660 | 117k | val->frame = static_frame; |
661 | 117k | val->loc_count = fb->num_locals; |
662 | 117k | val->loc_types = fb->local_types; |
663 | 117k | val->bc_size = fb->bytecode_size; |
664 | 117k | val->src_cur_op = fb->bytecode; |
665 | 117k | val->src_bc_end = fb->bytecode + fb->bytecode_size; |
666 | 117k | val->labels = MVM_calloc(1, fb->bytecode_size); |
667 | 117k | val->cur_info = NULL; |
668 | 117k | val->cur_mark = NULL; |
669 | 117k | val->cur_instr = 0; |
670 | 117k | val->cur_call = NULL; |
671 | 117k | val->cur_arg = 0; |
672 | 117k | |
673 | 117k | val->acceptable_max_arity = 0; |
674 | 117k | val->checkarity_seen = 0; |
675 | 117k | |
676 | 117k | val->expected_named_arg = 0; |
677 | 117k | val->remaining_positionals = 0; |
678 | 117k | val->remaining_jumplabels = 0; |
679 | 117k | val->reg_type_var = 0; |
680 | 117k | |
681 | 117k | #ifdef MVM_BIGENDIAN |
682 | | assert(fb->bytecode == fb->orig_bytecode); |
683 | | val->bc_start = MVM_malloc(fb->bytecode_size); |
684 | | memset(val->bc_start, 0xDB, fb->bytecode_size); |
685 | | fb->bytecode = val->bc_start; |
686 | | #else |
687 | 117k | val->bc_start = fb->bytecode; |
688 | 117k | #endif |
689 | 117k | val->bc_end = val->bc_start + fb->bytecode_size; |
690 | 117k | val->cur_op = val->bc_start; |
691 | 117k | |
692 | 11.1M | while (val->cur_op < val->bc_end) { |
693 | 11.0M | read_op(val); |
694 | 11.0M | if (val->cur_mark && val->cur_mark[0] == 's') |
695 | 0 | fail(val, MSG(val, "Illegal appearance of spesh op")); |
696 | 11.0M | |
697 | 11.0M | switch (val->cur_mark[0]) { |
698 | 10.4M | case MARK_regular: |
699 | 10.4M | case MARK_special: |
700 | 10.4M | validate_operands(val); |
701 | 10.4M | break; |
702 | 10.4M | |
703 | 11.6k | case MARK_sequence: |
704 | 11.6k | validate_sequence(val); |
705 | 11.6k | break; |
706 | 10.4M | |
707 | 565k | case MARK_head: |
708 | 565k | validate_block(val); |
709 | 565k | break; |
710 | 10.4M | |
711 | 0 | default: |
712 | 0 | fail_illegal_mark(val); |
713 | 11.0M | } |
714 | 11.0M | } |
715 | 117k | |
716 | 117k | validate_branch_targets(val); |
717 | 117k | validate_final_return(val); |
718 | 117k | |
719 | 117k | /* Validation successful. Clear up instruction offsets. */ |
720 | 117k | MVM_free(val->labels); |
721 | 117k | } |