compiler: Decode inline instruction operands in checked code

1f3e014e2e782eb35876b9a3d30c129ef1167f23ce2d54f77253e3e4e5746edd
Alexis Sellier committed ago 1 parent 2f90db37
lib/std/lang/il/binary/reader.rad +47 -40
193 193
        case super::CMP_ULT => return il::CmpOp::Ult,
194 194
        else => throw binary::Error::Invalid,
195 195
    }
196 196
}
197 197
198 -
/// Read one instruction and reconstruct its operand sequences.
199 -
export unsafe fn instr 'input (input: &mut Reader 'input) -> il::Instr throws (binary::Error) {
200 -
    let tag = try integer(input, 1) as u8;
198 +
/// Read an instruction with inline operands.
199 +
fn fixedInstr 'input (input: &mut Reader 'input, tag: u8) -> il::Instr throws (binary::Error) {
201 200
    match tag {
202 201
        case super::INSTR_RESERVE => {
203 202
            let vdst = try reg(input);
204 203
            let vsize = try val(input);
205 204
            let valignment = try integer(input, 4) as u32;
255 254
            if tag == super::INSTR_SEXT {
256 255
                return il::Instr::Sext { typ: vtyp, dst: vdst, val: vval };
257 256
            }
258 257
            return il::Instr::Zext { typ: vtyp, dst: vdst, val: vval };
259 258
        },
259 +
        case super::INSTR_RET => {
260 +
            let mut vval: ?il::Val = nil;
261 +
            if try flag(input) {
262 +
                set vval = try val(input);
263 +
            }
264 +
            return il::Instr::Ret { val: vval };
265 +
        },
266 +
        case super::INSTR_UNREACHABLE => {
267 +
            return il::Instr::Unreachable;
268 +
        },
269 +
        case super::INSTR_ECALL => {
270 +
            let vdst = try reg(input);
271 +
            let vnum = try val(input);
272 +
            let va0 = try val(input);
273 +
            let va1 = try val(input);
274 +
            let va2 = try val(input);
275 +
            let va3 = try val(input);
276 +
            return il::Instr::Ecall { dst: vdst, num: vnum, a0: va0, a1: va1, a2: va2, a3: va3 };
277 +
        },
278 +
        case super::INSTR_DEVICE_READ => {
279 +
            let t = try typ(input);
280 +
            let dst = try reg(input);
281 +
            let handle = try val(input); let offset = try val(input);
282 +
            return il::Instr::DeviceRead { typ: t, dst, handle, offset };
283 +
        },
284 +
        case super::INSTR_DEVICE_WRITE => {
285 +
            let t = try typ(input);
286 +
            let handle = try val(input); let offset = try val(input); let value = try val(input);
287 +
            return il::Instr::DeviceWrite { typ: t, handle, offset, value };
288 +
        },
289 +
        case super::INSTR_EBREAK => {
290 +
            return il::Instr::Ebreak;
291 +
        },
292 +
        case super::INSTR_MEMORYFENCE => {
293 +
            return il::Instr::MemoryFence;
294 +
        },
295 +
        else => throw binary::Error::Invalid,
296 +
    }
297 +
}
298 +
299 +
/// Read one instruction and reconstruct its operand sequences.
300 +
export unsafe fn instr 'input (input: &mut Reader 'input) -> il::Instr throws (binary::Error) {
301 +
    let tag = try integer(input, 1) as u8;
302 +
    match tag {
260 303
        case super::INSTR_CALL => {
261 304
            let vretTy = try typ(input);
262 305
            let mut vdst: ?il::Reg = nil;
263 306
            if try flag(input) {
264 307
                set vdst = try reg(input);
265 308
            }
266 309
            let vfunc = try val(input);
267 310
            let vargs = try values(input);
268 311
            return il::Instr::Call { retTy: vretTy, dst: vdst, func: vfunc, args: vargs };
269 312
        },
270 -
        case super::INSTR_RET => {
271 -
            let mut vval: ?il::Val = nil;
272 -
            if try flag(input) {
273 -
                set vval = try val(input);
274 -
            }
275 -
            return il::Instr::Ret { val: vval };
276 -
        },
277 313
        case super::INSTR_JMP => {
278 314
            let vtarget = try target(input);
279 315
            let vargs = try values(input);
280 316
            return il::Instr::Jmp { target: vtarget, args: vargs };
281 317
        },
303 339
                let args = try values(input);
304 340
                set vcases[i] = il::SwitchCase { value, target: block, args };
305 341
            }
306 342
            return il::Instr::Switch { val: vval, defaultTarget: vdefaultTarget, defaultArgs: vdefaultArgs, cases: (&mut vcases[..]) as *unsafe mut [il::SwitchCase] };
307 343
        },
308 -
        case super::INSTR_UNREACHABLE => {
309 -
            return il::Instr::Unreachable;
310 -
        },
311 -
        case super::INSTR_ECALL => {
312 -
            let vdst = try reg(input);
313 -
            let vnum = try val(input);
314 -
            let va0 = try val(input);
315 -
            let va1 = try val(input);
316 -
            let va2 = try val(input);
317 -
            let va3 = try val(input);
318 -
            return il::Instr::Ecall { dst: vdst, num: vnum, a0: va0, a1: va1, a2: va2, a3: va3 };
319 -
        },
320 -
        case super::INSTR_DEVICE_READ => {
321 -
            let t = try typ(input);
322 -
            let dst = try reg(input);
323 -
            let handle = try val(input); let offset = try val(input);
324 -
            return il::Instr::DeviceRead { typ: t, dst, handle, offset };
325 -
        },
326 -
        case super::INSTR_DEVICE_WRITE => {
327 -
            let t = try typ(input);
328 -
            let handle = try val(input); let offset = try val(input); let value = try val(input);
329 -
            return il::Instr::DeviceWrite { typ: t, handle, offset, value };
330 -
        },
331 -
        case super::INSTR_EBREAK => {
332 -
            return il::Instr::Ebreak;
333 -
        },
334 -
        case super::INSTR_MEMORYFENCE => {
335 -
            return il::Instr::MemoryFence;
336 -
        },
337 -
        else => throw binary::Error::Invalid,
344 +
        else => return try fixedInstr(input, tag),
338 345
    }
339 346
}
340 347
341 348
/// Read an initializer with a repetition count.
342 349
export unsafe fn dataValue 'input (input: &mut Reader 'input) -> il::DataValue throws (binary::Error) {
lib/std/lang/il/binary/tests.rad +37 -1
129 129
                };
130 130
                try testing::expect(failed);
131 131
                try testing::expect(short.offset <= capacity);
132 132
            }
133 133
        }
134 -
        let memory = &mut MEMORY[..512];
134 +
        // Inline operands use only the input bytes and symbol table.
135 +
        let mut arenaSize: u32 = 0;
136 +
        match item {
137 +
            case il::Instr::Call { .. }, il::Instr::Jmp { .. },
138 +
                 il::Instr::Br { .. }, il::Instr::Switch { .. } => set arenaSize = 512,
139 +
            else => {}
140 +
        }
141 +
        let memory = &mut MEMORY[..arenaSize];
135 142
        let mut arena = alloc::new(&mut memory[..]);
136 143
        let source: 'input = &expected[..], inputNames = &namesTable[..] in {
137 144
            let mut input = reader::new(source, &mut arena, inputNames);
138 145
            set input.registers = 16;
139 146
            set input.blocks = 4;
161 168
        }
162 169
163 170
    }
164 171
}
165 172
173 +
/// Malformed inline operands retain bounded cursors and empty arena storage.
174 +
@test unsafe fn invalidFixedInstructions() throws (testing::TestError) {
175 +
    let packets: [*[u8]; 6] = [
176 +
        &[255],
177 +
        &[6, 255],
178 +
        &[1, 3],
179 +
        &[5, 16, 0, 0, 0, 4],
180 +
        &[11, 2],
181 +
        &[5, 0, 0, 0, 0, 2, 0, 0, 0, 0],
182 +
    ];
183 +
    let namesTable: [*[u8]; 0] = [];
184 +
    for packet, index in packets {
185 +
        let mut arena = alloc::new(&mut MEMORY[..0]);
186 +
        let source: 'input = &packet[..], names = &namesTable[..] in {
187 +
            let mut input = reader::new(source, &mut arena, names);
188 +
            set input.registers = 16;
189 +
            let mut failed = false;
190 +
            try reader::instr(&mut input) catch err {
191 +
                let expected = binary::Error::Symbol if index == 5 else binary::Error::Invalid;
192 +
                assert err == expected;
193 +
                set failed = true;
194 +
            };
195 +
            assert failed;
196 +
            assert input.offset <= source.len;
197 +
            assert arena.offset == 0;
198 +
        }
199 +
    }
200 +
}
201 +
166 202
/// Check all instruction tags and their field order.
167 203
@test unsafe fn instructions() throws (testing::TestError) {
168 204
    let mut args: [il::Val; 1] = [il::Val::Undef];
169 205
    let mut cases: [il::SwitchCase; 1] = [il::SwitchCase {
170 206
        value: -1, target: 2, args: &mut args[..],