Coverage Report

Created: 2018-06-21 18:56

/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.09M
#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.2M
static void ensure_bytes(Validator *val, MVMuint32 count) {
78
40.2M
    if (val->src_cur_op + count > val->src_bc_end)
79
0
        fail(val, MSG(val, "truncated stream"));
80
40.2M
#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.2M
    val->src_cur_op += count;
90
40.2M
#endif
91
40.2M
}
92
93
94
679k
static void ensure_op(Validator *val, MVMuint16 opcode) {
95
679k
    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
679k
}
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
564k
static void ensure_no_remaining_positionals(Validator *val) {
110
564k
    if (val->remaining_positionals != 0)
111
0
        fail(val, MSG(val, "callsite expects %" PRIu16 " more positionals"),
112
0
                val->remaining_positionals);
113
564k
}
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.0M
    for (pos = 0, instr = (MVMuint32)-1; pos < val->bc_size; pos++) {
186
86.9M
        MVMuint32 flag = val->labels[pos];
187
86.9M
188
86.9M
        if (flag & MVM_BC_op_boundary)
189
13.0M
            instr++;
190
86.9M
191
86.9M
        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
86.9M
    }
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.95M
static void validate_literal_operand(Validator *val, MVMuint32 flags) {
205
7.95M
    MVMuint32 type = flags & MVM_operand_type_mask;
206
7.95M
    MVMuint32 size;
207
7.95M
208
7.95M
    switch (type) {
209
0
        case MVM_operand_int8:     size = 1; break;
210
3.50M
        case MVM_operand_int16:    size = 2; break;
211
1.42k
        case MVM_operand_int32:    size = 4; break;
212
26.1k
        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
564k
        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.95M
    }
227
7.95M
228
7.95M
    ensure_bytes(val, size);
229
7.95M
230
7.95M
    switch (type) {
231
564k
        case MVM_operand_callsite: {
232
564k
            MVMuint16 index = GET_UI16(val->cur_op, 0);
233
564k
            MVMuint32 count = val->cu->body.orig_callsites;
234
564k
            if (index >= count)
235
0
                fail(val, MSG(val, "callsite index %" PRIu16
236
0
                        " out of range 0..%" PRIu32), index, count - 1);
237
564k
            break;
238
564k
        }
239
564k
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
564k
        }
248
564k
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
564k
        }
257
564k
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.95M
    }
266
7.95M
267
7.95M
    val->cur_op += size;
268
7.95M
}
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.29M
        if (!val->reg_type_var) {
287
1.19M
            val->reg_type_var = reg_type;
288
1.19M
            goto next_operand;
289
1.19M
        }
290
2.29M
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
164k
        frame = frame->body.outer;
321
164k
        if (!frame)
322
0
            fail(val, MSG(val, "lexical operand requires %" PRIu16
323
0
                    " more enclosing scopes"), i);
324
164k
    }
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.5k
            val->reg_type_var = lex_type;
339
90.5k
            goto next_operand;
340
90.5k
        }
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.0M
static void validate_operand(Validator *val, MVMuint32 flags) {
355
25.0M
    MVMuint32 rw = flags & MVM_operand_rw_mask;
356
25.0M
357
25.0M
    switch (rw) {
358
6.62M
        case MVM_operand_literal:
359
6.62M
            validate_literal_operand(val, flags);
360
6.62M
            break;
361
6.62M
362
18.1M
        case MVM_operand_read_reg:
363
18.1M
        case MVM_operand_write_reg:
364
18.1M
            validate_reg_operand(val, flags);
365
18.1M
            break;
366
18.1M
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.0M
    }
375
25.0M
}
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
544k
        case MVM_OP_wval:
407
544k
        case MVM_OP_wval_wide: {
408
544k
            validate_reg_operand(val, operands[0]);
409
544k
            validate_literal_operand(val, operands[1]);
410
544k
            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
544k
            validate_literal_operand(val, operands[2]);
413
544k
            break;
414
544k
        }
415
544k
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
492k
                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.7M
                    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.4k
                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
803k
        case MVM_OP_arg_o:
558
803k
            if(!(flags & MVM_CALLSITE_ARG_OBJ))
559
0
                goto fail_arg;
560
803k
            break;
561
803k
562
309k
        case MVM_OP_arg_s:
563
309k
            if(!(flags & MVM_CALLSITE_ARG_STR))
564
0
                goto fail_arg;
565
309k
            break;
566
309k
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.4k
        case MVM_OP_arg_i:
573
89.4k
            if(!(flags & MVM_CALLSITE_ARG_INT))
574
0
                goto fail_arg;
575
89.4k
            break;
576
89.4k
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
564k
static void validate_block(Validator *val) {
595
564k
    int block_id = val->cur_mark[1];
596
564k
597
564k
    switch (block_id) {
598
564k
        case 'a': {
599
564k
            MVMuint16 index;
600
564k
601
564k
            ensure_op(val, MVM_OP_prepargs);
602
564k
            validate_operands(val);
603
564k
            index = GET_UI16(val->cur_op, -2);
604
564k
            val->cur_call  = val->cu->body.callsites[index];
605
564k
            val->cur_arg   = 0;
606
564k
            val->expected_named_arg = 0;
607
564k
            val->remaining_args = val->cur_call->arg_count;
608
564k
            val->remaining_positionals = val->cur_call->num_pos;
609
564k
610
564k
            break;
611
564k
        }
612
564k
613
0
        default:
614
0
            fail(val, MSG(val, "unknown instruction block '%c'"), block_id);
615
564k
    }
616
564k
617
1.85M
    while (val->cur_op < val->bc_end) {
618
1.85M
        int type, id;
619
1.85M
620
1.85M
        read_op(val);
621
1.85M
        type = val->cur_mark[0];
622
1.85M
        id   = val->cur_mark[1];
623
1.85M
624
1.85M
        if (id != block_id)
625
0
            fail(val, MSG(val, "expected instruction marked '%c' but got '%c'"),
626
0
                    block_id, id);
627
1.85M
628
1.85M
        switch (type) {
629
1.29M
            case MARK_body: break;
630
564k
            case MARK_tail: goto terminate_block;
631
0
            default:        fail_illegal_mark(val);
632
1.85M
        }
633
1.85M
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
564k
642
564k
terminate_block:
643
564k
    switch (block_id) {
644
564k
        case 'a':
645
564k
            validate_operands(val);
646
564k
            ensure_no_remaining_positionals(val);
647
564k
            break;
648
564k
    }
649
564k
}
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
564k
            case MARK_head:
708
564k
                validate_block(val);
709
564k
                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
}