compiler: Check owned switch case decoding
86f2433501f0bb61c392ee71e2113f36e7541d98c9cad0fd4b83c3954a0238cf
1 parent
76b7420c
lib/std/lang/il/binary/reader.rad
+15 -7
| 341 | 341 | case super::INSTR_SWITCH => { |
|
| 342 | 342 | let vval = try val(input); |
|
| 343 | 343 | let vdefaultTarget = try target(input); |
|
| 344 | 344 | let vdefaultArgs = try values(input); |
|
| 345 | 345 | let n = try count(input, 16); |
|
| 346 | - | let vcases = try storage(input, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), n) |
|
| 346 | + | let caseStorage = try storage(input, @sizeOf(il::SwitchCase), @alignOf(il::SwitchCase), n) |
|
| 347 | 347 | as *mut [il::SwitchCase]; |
|
| 348 | - | for i in 0..n { |
|
| 349 | - | let value = try integer(input, 8) as i64; |
|
| 350 | - | let block = try target(input); |
|
| 351 | - | let args = try values(input); |
|
| 352 | - | set vcases[i] = il::SwitchCase { value, target: block, args }; |
|
| 353 | - | } |
|
| 348 | + | let vcases = try fillCases(input, caseStorage); |
|
| 354 | 349 | return il::Instr::Switch { val: vval, defaultTarget: vdefaultTarget, defaultArgs: vdefaultArgs, cases: (&mut vcases[..]) as *unsafe mut [il::SwitchCase] }; |
|
| 355 | 350 | }, |
|
| 356 | 351 | else => return try fixedInstr(input, tag), |
|
| 357 | 352 | } |
|
| 358 | 353 | } |
|
| 359 | 354 | ||
| 355 | + | /// Fill owned switch cases with signed values and checked block targets. |
|
| 356 | + | fn fillCases 'input (input: &mut Reader 'input, cases: *mut [il::SwitchCase]) -> *mut [il::SwitchCase] throws (binary::Error) { |
|
| 357 | + | for i in 0..cases.len { |
|
| 358 | + | let value = try integer(input, 8) as i64; |
|
| 359 | + | let block = try target(input); |
|
| 360 | + | unsafe { |
|
| 361 | + | let args = try values(input); |
|
| 362 | + | set cases[i] = il::SwitchCase { value, target: block, args }; |
|
| 363 | + | } |
|
| 364 | + | } |
|
| 365 | + | return cases; |
|
| 366 | + | } |
|
| 367 | + | ||
| 360 | 368 | /// Read an initializer with a repetition count. |
|
| 361 | 369 | export unsafe fn dataValue 'input (input: &mut Reader 'input) -> il::DataValue throws (binary::Error) { |
|
| 362 | 370 | let tag = try integer(input, 1) as u8; |
|
| 363 | 371 | let item = il::DataItem::Str(try bytes(input)) if tag == super::DATA_STR |
|
| 364 | 372 | else try fixedDataItem(input, tag); |
lib/std/lang/il/binary/tests.rad
+30 -0
| 350 | 350 | typ, handle: il::Val::Undef, offset: il::Val::Undef, value: il::Val::Undef, |
|
| 351 | 351 | }, &write[..]); |
|
| 352 | 352 | } |
|
| 353 | 353 | } |
|
| 354 | 354 | ||
| 355 | + | /// Switch decoding stops at an invalid target or argument in either case. |
|
| 356 | + | @test unsafe fn invalidSwitchCases() throws (testing::TestError) { |
|
| 357 | + | let namesTable: [*[u8]; 0] = []; |
|
| 358 | + | let mut encoded: [u8; 48] = [0; 48]; |
|
| 359 | + | set encoded[0] = binary::INSTR_SWITCH; |
|
| 360 | + | set encoded[1] = 4; |
|
| 361 | + | set encoded[10] = 2; |
|
| 362 | + | set encoded[26] = 1; |
|
| 363 | + | set encoded[30] = 4; |
|
| 364 | + | set encoded[43] = 1; |
|
| 365 | + | set encoded[47] = 4; |
|
| 366 | + | for offset in [22, 39, 30, 47] { |
|
| 367 | + | let original = encoded[offset]; |
|
| 368 | + | set encoded[offset] = 255; |
|
| 369 | + | let mut arena = alloc::new(&mut MEMORY[..]); |
|
| 370 | + | let source: 'input = &encoded[..], names = &namesTable[..] in { |
|
| 371 | + | let mut input = reader::new(source, &mut arena, names); |
|
| 372 | + | set input.blocks = 1; |
|
| 373 | + | let mut failed = false; |
|
| 374 | + | try reader::instr(&mut input) catch err { |
|
| 375 | + | assert err == binary::Error::Invalid; |
|
| 376 | + | set failed = true; |
|
| 377 | + | }; |
|
| 378 | + | assert failed; |
|
| 379 | + | assert input.offset == offset + (4 if offset == 22 or offset == 39 else 1); |
|
| 380 | + | } |
|
| 381 | + | set encoded[offset] = original; |
|
| 382 | + | } |
|
| 383 | + | } |
|
| 384 | + | ||
| 355 | 385 | /// Byte decoding retains exact allocation and cursor state on storage failure. |
|
| 356 | 386 | @test unsafe fn byteStorage() throws (testing::TestError) { |
|
| 357 | 387 | let encoded: [u8; 11] = [0, 0, 0, 0, 3, 0, 0, 0, 65, 0, 255]; |
|
| 358 | 388 | let namesTable: [*[u8]; 0] = []; |
|
| 359 | 389 | for capacity in 0..4 { |