/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 | 16.3M | #define GET_REG(pc, idx) *((MVMuint16 *)(pc + idx)) |
12 | | #define GET_I16(pc, idx) *((MVMint16 *)(pc + idx)) |
13 | 2.36M | #define GET_UI16(pc, idx) *((MVMuint16 *)(pc + idx)) |
14 | | #define GET_I32(pc, idx) *((MVMint32 *)(pc + idx)) |
15 | 2.70M | #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_GCC; |
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 | 35.1M | static void ensure_bytes(Validator *val, MVMuint32 count) { |
78 | 35.1M | if (val->src_cur_op + count > val->src_bc_end) |
79 | 0 | fail(val, MSG(val, "truncated stream")); |
80 | 35.1M | #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 | 35.1M | val->src_cur_op += count; |
90 | 35.1M | #endif |
91 | 35.1M | } |
92 | | |
93 | | |
94 | 593k | static void ensure_op(Validator *val, MVMuint16 opcode) { |
95 | 593k | 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 | 593k | } |
100 | | |
101 | | |
102 | 10.3k | static void ensure_no_remaining_jumplabels(Validator *val) { |
103 | 10.3k | if (val->remaining_jumplabels != 0) |
104 | 0 | fail(val, MSG(val, "%" PRIu32 " jumplist labels missing their goto ops"), |
105 | 0 | val->remaining_jumplabels); |
106 | 10.3k | } |
107 | | |
108 | | |
109 | 492k | static void ensure_no_remaining_positionals(Validator *val) { |
110 | 492k | if (val->remaining_positionals != 0) |
111 | 0 | fail(val, MSG(val, "callsite expects %" PRIu16 " more positionals"), |
112 | 0 | val->remaining_positionals); |
113 | 492k | } |
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 | 11.3M | MVM_STATIC_INLINE const MVMOpInfo * get_info(Validator *val, MVMuint16 opcode) { |
124 | 11.3M | const MVMOpInfo *info; |
125 | 11.3M | |
126 | 11.3M | if (opcode < MVM_OP_EXT_BASE) { |
127 | 11.3M | info = MVM_op_get_op(opcode); |
128 | 11.3M | if (!info) |
129 | 0 | fail(val, MSG(val, "invalid opcode %u"), opcode); |
130 | 11.3M | } |
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 | 11.3M | |
147 | 11.3M | return info; |
148 | 11.3M | } |
149 | | |
150 | | |
151 | 11.3M | MVM_STATIC_INLINE void read_op(Validator *val) { |
152 | 11.3M | MVMuint16 opcode; |
153 | 11.3M | const MVMOpInfo *info; |
154 | 11.3M | MVMuint32 pos; |
155 | 11.3M | |
156 | 11.3M | ensure_bytes(val, 2); |
157 | 11.3M | |
158 | 11.3M | opcode = *(MVMuint16 *)val->cur_op; |
159 | 11.3M | info = get_info(val, opcode); |
160 | 11.3M | pos = val->cur_op - val->bc_start; |
161 | 11.3M | |
162 | 11.3M | #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 | 11.3M | |
167 | 11.3M | val->labels[pos] |= MVM_BC_op_boundary; |
168 | 11.3M | val->cur_info = info; |
169 | 11.3M | val->cur_mark = info->mark; |
170 | 11.3M | val->cur_op += 2; |
171 | 11.3M | val->cur_instr += 1; |
172 | 11.3M | } |
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 | 102k | static void validate_branch_targets(Validator *val) { |
183 | 102k | MVMuint32 pos, instr; |
184 | 102k | |
185 | 75.8M | for (pos = 0, instr = (MVMuint32)-1; pos < val->bc_size; pos++) { |
186 | 75.7M | MVMuint32 flag = val->labels[pos]; |
187 | 75.7M | |
188 | 75.7M | if (flag & MVM_BC_op_boundary) |
189 | 11.3M | instr++; |
190 | 75.7M | |
191 | 75.7M | 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 | 75.7M | } |
195 | 102k | } |
196 | | |
197 | | |
198 | 102k | static void validate_final_return(Validator *val) { |
199 | 102k | if (!val->bc_size || val->cur_mark[1] != 'r') |
200 | 0 | fail(val, MSG(val, "missing final return instruction")); |
201 | 102k | } |
202 | | |
203 | | |
204 | 6.93M | static void validate_literal_operand(Validator *val, MVMuint32 flags) { |
205 | 6.93M | MVMuint32 type = flags & MVM_operand_type_mask; |
206 | 6.93M | MVMuint32 size; |
207 | 6.93M | |
208 | 6.93M | switch (type) { |
209 | 0 | case MVM_operand_int8: size = 1; break; |
210 | 3.04M | case MVM_operand_int16: size = 2; break; |
211 | 1.09k | case MVM_operand_int32: size = 4; break; |
212 | 23.3k | case MVM_operand_int64: size = 8; break; |
213 | 0 | case MVM_operand_num32: size = 4; break; |
214 | 469 | case MVM_operand_num64: size = 8; break; |
215 | 492k | case MVM_operand_callsite: size = 2; break; |
216 | 669k | case MVM_operand_coderef: size = 2; break; |
217 | 1.50M | case MVM_operand_str: size = 4; break; |
218 | 1.20M | 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 | 6.93M | } |
227 | 6.93M | |
228 | 6.93M | ensure_bytes(val, size); |
229 | 6.93M | |
230 | 6.93M | switch (type) { |
231 | 492k | case MVM_operand_callsite: { |
232 | 492k | MVMuint16 index = GET_UI16(val->cur_op, 0); |
233 | 492k | MVMuint32 count = val->cu->body.orig_callsites; |
234 | 492k | if (index >= count) |
235 | 0 | fail(val, MSG(val, "callsite index %" PRIu16 |
236 | 0 | " out of range 0..%" PRIu32), index, count - 1); |
237 | 492k | break; |
238 | 492k | } |
239 | 492k | |
240 | 669k | case MVM_operand_coderef: { |
241 | 669k | MVMuint16 index = GET_UI16(val->cur_op, 0); |
242 | 669k | MVMuint32 count = val->cu->body.orig_frames; |
243 | 669k | if (index >= count) |
244 | 0 | fail(val, MSG(val, "coderef index %" PRIu16 |
245 | 0 | " out of range 0..%" PRIu32), index, count - 1); |
246 | 669k | break; |
247 | 492k | } |
248 | 492k | |
249 | 1.50M | case MVM_operand_str: { |
250 | 1.50M | MVMuint32 index = GET_UI32(val->cur_op, 0); |
251 | 1.50M | MVMuint32 count = val->cu->body.orig_strings; |
252 | 1.50M | if (index >= count) |
253 | 0 | fail(val, MSG(val, "string index %" PRIu32 |
254 | 0 | " out of range 0..%" PRIu32), index, count - 1); |
255 | 1.50M | break; |
256 | 492k | } |
257 | 492k | |
258 | 1.20M | case MVM_operand_ins: { |
259 | 1.20M | MVMuint32 offset = GET_UI32(val->cur_op, 0); |
260 | 1.20M | 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.20M | val->labels[offset] |= MVM_BC_branch_target; |
264 | 1.20M | } |
265 | 6.93M | } |
266 | 6.93M | |
267 | 6.93M | val->cur_op += size; |
268 | 6.93M | } |
269 | | |
270 | | |
271 | 16.3M | static void validate_reg_operand(Validator *val, MVMuint32 flags) { |
272 | 16.3M | MVMuint32 operand_type = flags & MVM_operand_type_mask; |
273 | 16.3M | MVMuint32 reg_type; |
274 | 16.3M | MVMuint16 reg; |
275 | 16.3M | |
276 | 16.3M | ensure_bytes(val, 2); |
277 | 16.3M | |
278 | 16.3M | reg = GET_REG(val->cur_op, 0); |
279 | 16.3M | 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 | 16.3M | |
283 | 16.3M | reg_type = val->loc_types[reg] << 3; |
284 | 16.3M | |
285 | 16.3M | if (operand_type == MVM_operand_type_var) { |
286 | 1.97M | if (!val->reg_type_var) { |
287 | 1.02M | val->reg_type_var = reg_type; |
288 | 1.02M | goto next_operand; |
289 | 1.02M | } |
290 | 1.97M | |
291 | 950k | operand_type = val->reg_type_var; |
292 | 950k | } |
293 | 16.3M | |
294 | 15.3M | 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 | 15.3M | |
298 | 16.3M | next_operand: |
299 | 16.3M | val->cur_op += 2; |
300 | 16.3M | } |
301 | | |
302 | | |
303 | 231k | static void validate_lex_operand(Validator *val, MVMuint32 flags) { |
304 | 231k | MVMuint32 operand_type = flags & MVM_operand_type_mask; |
305 | 231k | MVMuint16 lex_index, frame_index, i; |
306 | 231k | MVMuint32 lex_count, lex_type; |
307 | 231k | MVMStaticFrame *frame = val->frame; |
308 | 231k | |
309 | 231k | /* Two steps forward, two steps back to keep the error reporting happy, |
310 | 231k | and to make the endian conversion within ensure_bytes correct. |
311 | 231k | (Both are using val->cur_op, and want it to have different values.) */ |
312 | 231k | ensure_bytes(val, 2); |
313 | 231k | lex_index = GET_UI16(val->cur_op, 0); |
314 | 231k | val->cur_op += 2; |
315 | 231k | ensure_bytes(val, 2); |
316 | 231k | val->cur_op -= 2; |
317 | 231k | frame_index = GET_UI16(val->cur_op, 2); |
318 | 231k | |
319 | 376k | for (i = frame_index; i; i--) { |
320 | 145k | frame = frame->body.outer; |
321 | 145k | if (!frame) |
322 | 0 | fail(val, MSG(val, "lexical operand requires %" PRIu16 |
323 | 0 | " more enclosing scopes"), i); |
324 | 145k | } |
325 | 231k | |
326 | 231k | if (!frame->body.fully_deserialized) |
327 | 1 | MVM_bytecode_finish_frame(val->tc, frame->body.cu, frame, 0); |
328 | 231k | |
329 | 231k | lex_count = frame->body.num_lexicals; |
330 | 231k | 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 | 231k | |
334 | 231k | lex_type = frame->body.lexical_types[lex_index] << 3; |
335 | 231k | |
336 | 231k | if (operand_type == MVM_operand_type_var) { |
337 | 231k | if (!val->reg_type_var) { |
338 | 76.8k | val->reg_type_var = lex_type; |
339 | 76.8k | goto next_operand; |
340 | 76.8k | } |
341 | 231k | |
342 | 154k | operand_type = val->reg_type_var; |
343 | 154k | } |
344 | 231k | |
345 | 154k | 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 | 154k | |
349 | 231k | next_operand: |
350 | 231k | val->cur_op += 4; |
351 | 231k | } |
352 | | |
353 | | |
354 | 23.3M | static void validate_operand(Validator *val, MVMuint32 flags) { |
355 | 23.3M | MVMuint32 rw = flags & MVM_operand_rw_mask; |
356 | 23.3M | |
357 | 23.3M | switch (rw) { |
358 | 6.72M | case MVM_operand_literal: |
359 | 6.72M | validate_literal_operand(val, flags); |
360 | 6.72M | break; |
361 | 6.72M | |
362 | 16.3M | case MVM_operand_read_reg: |
363 | 16.3M | case MVM_operand_write_reg: |
364 | 16.3M | validate_reg_operand(val, flags); |
365 | 16.3M | break; |
366 | 16.3M | |
367 | 231k | case MVM_operand_read_lex: |
368 | 231k | case MVM_operand_write_lex: |
369 | 231k | validate_lex_operand(val, flags); |
370 | 231k | break; |
371 | 231k | |
372 | 0 | default: |
373 | 0 | fail(val, MSG(val, "invalid instruction rw flag %i"), rw); |
374 | 23.3M | } |
375 | 23.3M | } |
376 | | |
377 | | |
378 | 11.3M | static void validate_operands(Validator *val) { |
379 | 11.3M | const MVMuint8 *operands = val->cur_info->operands; |
380 | 11.3M | |
381 | 11.3M | val->reg_type_var = 0; |
382 | 11.3M | |
383 | 11.3M | switch (val->cur_info->opcode) { |
384 | 10.3k | case MVM_OP_jumplist: { |
385 | 10.3k | MVMint64 count; |
386 | 10.3k | |
387 | 10.3k | validate_literal_operand(val, operands[0]); |
388 | 10.3k | count = MVM_BC_get_I64(val->cur_op, -8); |
389 | 10.3k | if (count < 0 || count > UINT32_MAX) |
390 | 0 | fail(val, MSG(val, "illegal jumplist label count %" PRIi64), |
391 | 0 | count); |
392 | 10.3k | |
393 | 10.3k | validate_reg_operand(val, operands[1]); |
394 | 10.3k | |
395 | 10.3k | break; |
396 | 10.3k | } |
397 | 102k | case MVM_OP_checkarity: { |
398 | 102k | validate_literal_operand(val, operands[0]); |
399 | 102k | |
400 | 102k | validate_literal_operand(val, operands[1]); |
401 | 102k | val->acceptable_max_arity = GET_UI16(val->cur_op, -2); |
402 | 102k | val->checkarity_seen = 1; |
403 | 102k | |
404 | 102k | break; |
405 | 10.3k | } |
406 | 10.3k | |
407 | 11.2M | default: { |
408 | 11.2M | int i; |
409 | 11.2M | |
410 | 11.2M | if (val->cur_mark[1] == 'p') { |
411 | 140k | /* First of all, bail out if no checkarity was seen yet. */ |
412 | 140k | if (!val->checkarity_seen) { |
413 | 0 | fail(val, MSG(val, "param op without checkarity op seen.")); |
414 | 0 | } |
415 | 140k | |
416 | 140k | /* For the p-marked ops, which is a subset of param_* ops, |
417 | 140k | * we check the second argument against the value checkarity |
418 | 140k | * checked against. */ |
419 | 430k | for (i = 0; i < val->cur_info->num_operands; i++) { |
420 | 290k | validate_operand(val, val->cur_info->operands[i]); |
421 | 290k | |
422 | 290k | /* This is the argument we want to check */ |
423 | 290k | if (i == 1) { |
424 | 140k | MVMint16 value = GET_UI16(val->cur_op, -2); |
425 | 140k | if (value > val->acceptable_max_arity) { |
426 | 0 | fail(val, MSG(val, "tried to take arg number %d after checkarity with %d"), value, val->acceptable_max_arity); |
427 | 0 | } |
428 | 140k | } |
429 | 290k | } |
430 | 140k | } |
431 | 11.0M | else { |
432 | 34.1M | for (i = 0; i < val->cur_info->num_operands; i++) |
433 | 23.0M | validate_operand(val, val->cur_info->operands[i]); |
434 | 11.0M | } |
435 | 11.2M | } |
436 | 11.3M | } |
437 | 11.3M | } |
438 | | |
439 | | |
440 | 10.3k | static void validate_sequence(Validator *val) { |
441 | 10.3k | int seq_id = val->cur_mark[1]; |
442 | 10.3k | |
443 | 10.3k | switch (seq_id) { |
444 | 10.3k | case 'j': { |
445 | 10.3k | ensure_op(val, MVM_OP_jumplist); |
446 | 10.3k | validate_operands(val); |
447 | 10.3k | val->remaining_jumplabels = (MVMuint32)MVM_BC_get_I64(val->cur_op, -10); |
448 | 10.3k | break; |
449 | 10.3k | } |
450 | 10.3k | |
451 | 0 | default: |
452 | 0 | fail(val, MSG(val, "unknown instruction sequence '%c'"), seq_id); |
453 | 10.3k | } |
454 | 10.3k | |
455 | 90.5k | while (val->cur_op < val->bc_end) { |
456 | 90.5k | int type, id; |
457 | 90.5k | |
458 | 90.5k | read_op(val); |
459 | 90.5k | type = val->cur_mark[0]; |
460 | 90.5k | id = val->cur_mark[1]; |
461 | 90.5k | |
462 | 90.5k | switch (type) { |
463 | 90.5k | case MARK_special: |
464 | 90.5k | if (id == seq_id) |
465 | 90.5k | break; |
466 | 90.5k | /* FALLTHROUGH */ |
467 | 90.5k | |
468 | 0 | case MARK_regular: |
469 | 0 | case MARK_sequence: |
470 | 0 | case MARK_head: |
471 | 0 | unread_op(val); |
472 | 0 | goto terminate_seq; |
473 | 0 |
|
474 | 0 | default: |
475 | 0 | fail_illegal_mark(val); |
476 | 90.5k | } |
477 | 90.5k | |
478 | 90.5k | switch (seq_id) { |
479 | 90.5k | case 'j': |
480 | 90.5k | ensure_op(val, MVM_OP_goto); |
481 | 90.5k | validate_operands(val); |
482 | 90.5k | |
483 | 90.5k | val->remaining_jumplabels--; |
484 | 90.5k | if (val->remaining_jumplabels == 0) |
485 | 10.3k | goto terminate_seq; |
486 | 80.2k | break; |
487 | 90.5k | } |
488 | 90.5k | } |
489 | 10.3k | |
490 | 10.3k | terminate_seq: |
491 | 10.3k | switch (seq_id) { |
492 | 10.3k | case 'j': |
493 | 10.3k | ensure_no_remaining_jumplabels(val); |
494 | 10.3k | break; |
495 | 10.3k | } |
496 | 10.3k | } |
497 | | |
498 | | |
499 | 1.11M | static void validate_arg(Validator *val) { |
500 | 1.11M | MVMCallsiteEntry flags; |
501 | 1.11M | |
502 | 1.11M | val->remaining_args--; |
503 | 1.11M | |
504 | 1.11M | if (val->expected_named_arg) { |
505 | 73.9k | flags = val->expected_named_arg; |
506 | 73.9k | val->expected_named_arg = 0; |
507 | 73.9k | } |
508 | 1.04M | else { |
509 | 1.04M | MVMuint16 index = val->cur_arg++; |
510 | 1.04M | MVMuint16 count = val->cur_call->arg_count; |
511 | 1.04M | |
512 | 1.04M | if (index >= count) |
513 | 0 | fail (val, MSG(val, "argument index %" PRIu16 |
514 | 0 | " not in range 0..%" PRIu32), index, count - 1); |
515 | 1.04M | |
516 | 1.04M | flags = val->cur_call->arg_flags[index]; |
517 | 1.04M | |
518 | 1.04M | switch (flags & ~MVM_CALLSITE_ARG_MASK) { |
519 | 968k | case 0: /* positionals */ |
520 | 968k | case MVM_CALLSITE_ARG_FLAT: |
521 | 968k | val->remaining_positionals--; |
522 | 968k | break; |
523 | 968k | |
524 | 2.96k | case MVM_CALLSITE_ARG_FLAT_NAMED: |
525 | 2.96k | /* Nothing to do for this case. */ |
526 | 2.96k | break; |
527 | 968k | |
528 | 73.9k | case MVM_CALLSITE_ARG_NAMED: |
529 | 73.9k | val->expected_named_arg = flags & MVM_CALLSITE_ARG_MASK; |
530 | 73.9k | goto named_arg; |
531 | 968k | |
532 | 0 | default: |
533 | 0 | fail(val, MSG(val, "invalid argument flags (%i) at index %" |
534 | 0 | PRIu16), (int)(flags & ~MVM_CALLSITE_ARG_MASK), index); |
535 | 1.04M | } |
536 | 1.04M | } |
537 | 1.11M | |
538 | 1.04M | goto regular_arg; |
539 | 1.11M | |
540 | 73.9k | named_arg: |
541 | 73.9k | if (val->cur_info->opcode != MVM_OP_argconst_s) |
542 | 0 | fail(val, MSG(val, "expected instruction 'argconst_s' but got '%s'"), |
543 | 0 | val->cur_info->name); |
544 | 73.9k | return; |
545 | 1.11M | |
546 | 1.04M | regular_arg: |
547 | 1.04M | switch (val->cur_info->opcode) { |
548 | 703k | case MVM_OP_arg_o: |
549 | 703k | if(!(flags & MVM_CALLSITE_ARG_OBJ)) |
550 | 0 | goto fail_arg; |
551 | 703k | break; |
552 | 703k | |
553 | 264k | case MVM_OP_arg_s: |
554 | 264k | if(!(flags & MVM_CALLSITE_ARG_STR)) |
555 | 0 | goto fail_arg; |
556 | 264k | break; |
557 | 264k | |
558 | 0 | case MVM_OP_argconst_s: |
559 | 0 | if(!(flags & MVM_CALLSITE_ARG_STR)) |
560 | 0 | goto fail_arg; |
561 | 0 | break; |
562 | 0 |
|
563 | 76.2k | case MVM_OP_arg_i: |
564 | 76.2k | if(!(flags & MVM_CALLSITE_ARG_INT)) |
565 | 0 | goto fail_arg; |
566 | 76.2k | break; |
567 | 76.2k | |
568 | 813 | case MVM_OP_arg_n: |
569 | 813 | if(!(flags & MVM_CALLSITE_ARG_NUM)) |
570 | 0 | goto fail_arg; |
571 | 813 | break; |
572 | 813 | |
573 | 0 | default: |
574 | 0 | fail(val, MSG(val, |
575 | 0 | "unexpected instruction '%s' during argument preparation"), |
576 | 0 | val->cur_info->name); |
577 | 0 |
|
578 | 0 | fail_arg: |
579 | 0 | fail(val, MSG(val, "invalid argument (%i) for instruction %s"), |
580 | 0 | (int)flags, val->cur_info->name); |
581 | 1.04M | } |
582 | 1.04M | } |
583 | | |
584 | | |
585 | 492k | static void validate_block(Validator *val) { |
586 | 492k | int block_id = val->cur_mark[1]; |
587 | 492k | |
588 | 492k | switch (block_id) { |
589 | 492k | case 'a': { |
590 | 492k | MVMuint16 index; |
591 | 492k | |
592 | 492k | ensure_op(val, MVM_OP_prepargs); |
593 | 492k | validate_operands(val); |
594 | 492k | index = GET_UI16(val->cur_op, -2); |
595 | 492k | val->cur_call = val->cu->body.callsites[index]; |
596 | 492k | val->cur_arg = 0; |
597 | 492k | val->expected_named_arg = 0; |
598 | 492k | val->remaining_args = val->cur_call->arg_count; |
599 | 492k | val->remaining_positionals = val->cur_call->num_pos; |
600 | 492k | |
601 | 492k | break; |
602 | 492k | } |
603 | 492k | |
604 | 0 | default: |
605 | 0 | fail(val, MSG(val, "unknown instruction block '%c'"), block_id); |
606 | 492k | } |
607 | 492k | |
608 | 1.61M | while (val->cur_op < val->bc_end) { |
609 | 1.61M | int type, id; |
610 | 1.61M | |
611 | 1.61M | read_op(val); |
612 | 1.61M | type = val->cur_mark[0]; |
613 | 1.61M | id = val->cur_mark[1]; |
614 | 1.61M | |
615 | 1.61M | if (id != block_id) |
616 | 0 | fail(val, MSG(val, "expected instruction marked '%c' but got '%c'"), |
617 | 0 | block_id, id); |
618 | 1.61M | |
619 | 1.61M | switch (type) { |
620 | 1.11M | case MARK_body: break; |
621 | 492k | case MARK_tail: goto terminate_block; |
622 | 0 | default: fail_illegal_mark(val); |
623 | 1.61M | } |
624 | 1.61M | |
625 | 1.11M | switch (block_id) { |
626 | 1.11M | case 'a': |
627 | 1.11M | validate_operands(val); |
628 | 1.11M | validate_arg(val); |
629 | 1.11M | break; |
630 | 1.11M | } |
631 | 1.11M | } |
632 | 492k | |
633 | 492k | terminate_block: |
634 | 492k | switch (block_id) { |
635 | 492k | case 'a': |
636 | 492k | validate_operands(val); |
637 | 492k | ensure_no_remaining_positionals(val); |
638 | 492k | break; |
639 | 492k | } |
640 | 492k | } |
641 | | |
642 | | |
643 | | /* Validate that a static frame's bytecode is executable by the interpreter. */ |
644 | | void MVM_validate_static_frame(MVMThreadContext *tc, |
645 | 102k | MVMStaticFrame *static_frame) { |
646 | 102k | MVMStaticFrameBody *fb = &static_frame->body; |
647 | 102k | Validator val[1]; |
648 | 102k | |
649 | 102k | val->tc = tc; |
650 | 102k | val->cu = fb->cu; |
651 | 102k | val->frame = static_frame; |
652 | 102k | val->loc_count = fb->num_locals; |
653 | 102k | val->loc_types = fb->local_types; |
654 | 102k | val->bc_size = fb->bytecode_size; |
655 | 102k | val->src_cur_op = fb->bytecode; |
656 | 102k | val->src_bc_end = fb->bytecode + fb->bytecode_size; |
657 | 102k | val->labels = MVM_calloc(1, fb->bytecode_size); |
658 | 102k | val->cur_info = NULL; |
659 | 102k | val->cur_mark = NULL; |
660 | 102k | val->cur_instr = 0; |
661 | 102k | val->cur_call = NULL; |
662 | 102k | val->cur_arg = 0; |
663 | 102k | |
664 | 102k | val->acceptable_max_arity = 0; |
665 | 102k | val->checkarity_seen = 0; |
666 | 102k | |
667 | 102k | val->expected_named_arg = 0; |
668 | 102k | val->remaining_positionals = 0; |
669 | 102k | val->remaining_jumplabels = 0; |
670 | 102k | val->reg_type_var = 0; |
671 | 102k | |
672 | 102k | #ifdef MVM_BIGENDIAN |
673 | | assert(fb->bytecode == fb->orig_bytecode); |
674 | | val->bc_start = MVM_malloc(fb->bytecode_size); |
675 | | memset(val->bc_start, 0xDB, fb->bytecode_size); |
676 | | fb->bytecode = val->bc_start; |
677 | | #else |
678 | 102k | val->bc_start = fb->bytecode; |
679 | 102k | #endif |
680 | 102k | val->bc_end = val->bc_start + fb->bytecode_size; |
681 | 102k | val->cur_op = val->bc_start; |
682 | 102k | |
683 | 9.74M | while (val->cur_op < val->bc_end) { |
684 | 9.64M | read_op(val); |
685 | 9.64M | if (val->cur_mark && val->cur_mark[0] == 's') |
686 | 0 | fail(val, MSG(val, "Illegal appearance of spesh op")); |
687 | 9.64M | |
688 | 9.64M | switch (val->cur_mark[0]) { |
689 | 9.13M | case MARK_regular: |
690 | 9.13M | case MARK_special: |
691 | 9.13M | validate_operands(val); |
692 | 9.13M | break; |
693 | 9.13M | |
694 | 10.3k | case MARK_sequence: |
695 | 10.3k | validate_sequence(val); |
696 | 10.3k | break; |
697 | 9.13M | |
698 | 492k | case MARK_head: |
699 | 492k | validate_block(val); |
700 | 492k | break; |
701 | 9.13M | |
702 | 0 | default: |
703 | 0 | fail_illegal_mark(val); |
704 | 9.64M | } |
705 | 9.64M | } |
706 | 102k | |
707 | 102k | validate_branch_targets(val); |
708 | 102k | validate_final_return(val); |
709 | 102k | |
710 | 102k | /* Validation successful. Clear up instruction offsets. */ |
711 | 102k | MVM_free(val->labels); |
712 | 102k | } |