Coverage Report

Created: 2017-04-15 07:07

/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
}