compiler: Check owned package header decoding
76b7420cc81f9b807a14780a806af17c6e88c1cd41d81831e9fdd35c0f0018d6
1 parent
97e4033f
lib/std/lang/il/binary/decodeTests.rad
+28 -0
| 271 | 271 | }; |
|
| 272 | 272 | try testing::expect(trailing); |
|
| 273 | 273 | try testing::expect(arena.offset == 8); |
|
| 274 | 274 | } |
|
| 275 | 275 | ||
| 276 | + | /// Reject invalid dependency and export entries after partial table decoding. |
|
| 277 | + | @test unsafe fn malformedHeaderTables() throws (testing::TestError) { |
|
| 278 | + | let package = binary::Package { |
|
| 279 | + | symbols: &["p"], name: "p", dependencies: &["p", "p"], |
|
| 280 | + | exports: &[ |
|
| 281 | + | binary::Export { name: "p", kind: binary::ExportKind::Function }, |
|
| 282 | + | binary::Export { name: "p", kind: binary::ExportKind::Data }, |
|
| 283 | + | ], |
|
| 284 | + | entry: nil, program: il::Program { data: &[], fns: &[] }, |
|
| 285 | + | }; |
|
| 286 | + | let mut buffer: [u8; 64] = [0; 64]; |
|
| 287 | + | let length = try program::encode(&mut buffer[..], &package) catch { throw testing::TestError::Failed; }; |
|
| 288 | + | for offset in [25, 29, 37, 42, 41, 46] { |
|
| 289 | + | let original = buffer[offset]; |
|
| 290 | + | set buffer[offset] = 2; |
|
| 291 | + | let mut arena = alloc::new(&mut MEMORY[..]); |
|
| 292 | + | set arena.offset = 8; |
|
| 293 | + | let mut failed = false; |
|
| 294 | + | try program::decode(&buffer[..length], &mut arena, LIMITS) catch err { |
|
| 295 | + | assert err == (binary::Error::Invalid if offset == 41 or offset == 46 else binary::Error::Symbol); |
|
| 296 | + | set failed = true; |
|
| 297 | + | }; |
|
| 298 | + | assert failed; |
|
| 299 | + | assert arena.offset == 8; |
|
| 300 | + | set buffer[offset] = original; |
|
| 301 | + | } |
|
| 302 | + | } |
|
| 303 | + | ||
| 276 | 304 | /// Encode a malformed package and check rejection with arena rollback. |
|
| 277 | 305 | unsafe fn rejected(package: &binary::Package, limits: binary::Limits) throws (testing::TestError) { |
|
| 278 | 306 | let mut buffer: [u8; 512] = [0; 512]; |
|
| 279 | 307 | let length = try program::encode(&mut buffer[..], package) catch { |
|
| 280 | 308 | throw testing::TestError::Failed; |
lib/std/lang/il/binary/program.rad
+43 -23
| 299 | 299 | let version = try reader::integer(input, 4) as u32; |
|
| 300 | 300 | if magic <> binary::MAGIC or version <> binary::VERSION { |
|
| 301 | 301 | throw binary::Error::Invalid; |
|
| 302 | 302 | } |
|
| 303 | 303 | let symbolCount = try reader::count(input, 4); |
|
| 304 | - | let symbols = try reader::storage(input, @sizeOf(*[u8]), @alignOf(*[u8]), symbolCount) |
|
| 304 | + | let symbolStorage = try reader::storage(input, @sizeOf(*[u8]), @alignOf(*[u8]), symbolCount) |
|
| 305 | 305 | as *mut [*[u8]]; |
|
| 306 | - | for i in 0..symbolCount { |
|
| 307 | - | let name = try reader::bytes(input); |
|
| 308 | - | if name.len == 0 { |
|
| 309 | - | throw binary::Error::Invalid; |
|
| 310 | - | } |
|
| 311 | - | for j in 0..i { |
|
| 312 | - | if mem::eq(name, symbols[j]) { |
|
| 313 | - | throw binary::Error::Invalid; |
|
| 314 | - | } |
|
| 315 | - | } |
|
| 316 | - | set symbols[i] = name; |
|
| 317 | - | } |
|
| 306 | + | let symbols = try fillSymbols(input, symbolStorage); |
|
| 318 | 307 | let source: 'tables = &input.bytes[..], names = &symbols[..] where 'input: 'tables in { |
|
| 319 | 308 | let mut cursor = reader::new(source, input.arena, names); |
|
| 320 | 309 | set cursor.offset = input.offset; |
|
| 321 | 310 | let name = try reader::symbol(&mut cursor); |
|
| 322 | 311 | let depCount = try reader::count(&mut cursor, 4); |
|
| 323 | - | let dependencies = try reader::storage(&mut cursor, @sizeOf(*[u8]), @alignOf(*[u8]), depCount) |
|
| 312 | + | let dependencyStorage = try reader::storage(&mut cursor, @sizeOf(*[u8]), @alignOf(*[u8]), depCount) |
|
| 324 | 313 | as *mut [*[u8]]; |
|
| 325 | - | for i in 0..depCount { |
|
| 326 | - | set dependencies[i] = try reader::symbol(&mut cursor); |
|
| 327 | - | } |
|
| 314 | + | let dependencies = try fillDependencies(&mut cursor, dependencyStorage); |
|
| 328 | 315 | let exportCount = try reader::count(&mut cursor, 5); |
|
| 329 | - | let exports = try reader::storage(&mut cursor, @sizeOf(binary::Export), @alignOf(binary::Export), exportCount) |
|
| 316 | + | let exportStorage = try reader::storage(&mut cursor, @sizeOf(binary::Export), @alignOf(binary::Export), exportCount) |
|
| 330 | 317 | as *mut [binary::Export]; |
|
| 331 | - | for i in 0..exportCount { |
|
| 332 | - | let symbol = try reader::symbol(&mut cursor); |
|
| 333 | - | let kind = binary::ExportKind::Data if try reader::flag(&mut cursor) else binary::ExportKind::Function; |
|
| 334 | - | set exports[i] = binary::Export { name: symbol, kind }; |
|
| 335 | - | } |
|
| 318 | + | let exports = try fillExports(&mut cursor, exportStorage); |
|
| 336 | 319 | let mut entry: ?*[u8] = nil; |
|
| 337 | 320 | if try reader::flag(&mut cursor) { |
|
| 338 | 321 | set entry = try reader::symbol(&mut cursor); |
|
| 339 | 322 | } |
|
| 340 | 323 | let data = try readData(&mut cursor); |
| 345 | 328 | set input.offset = cursor.offset; |
|
| 346 | 329 | return binary::Package { symbols: (&symbols[..]) as *unsafe [*[u8]], name, dependencies: (&dependencies[..]) as *unsafe [*[u8]], exports: (&exports[..]) as *unsafe [binary::Export], entry, program: il::Program { data, fns } }; |
|
| 347 | 330 | } |
|
| 348 | 331 | } |
|
| 349 | 332 | ||
| 333 | + | /// Fill owned symbol storage with distinct nonempty names. |
|
| 334 | + | fn fillSymbols 'input (input: &mut reader::Reader 'input, symbols: *mut [*[u8]]) -> *[*[u8]] throws (binary::Error) { |
|
| 335 | + | for i in 0..symbols.len { |
|
| 336 | + | unsafe { |
|
| 337 | + | set symbols[i] = try reader::bytes(input); |
|
| 338 | + | } |
|
| 339 | + | let name = symbols[i]; |
|
| 340 | + | if name.len == 0 { |
|
| 341 | + | throw binary::Error::Invalid; |
|
| 342 | + | } |
|
| 343 | + | for j in 0..i { |
|
| 344 | + | if mem::eq(name, symbols[j]) { |
|
| 345 | + | throw binary::Error::Invalid; |
|
| 346 | + | } |
|
| 347 | + | } |
|
| 348 | + | } |
|
| 349 | + | return symbols; |
|
| 350 | + | } |
|
| 351 | + | ||
| 352 | + | /// Fill owned dependency storage from checked symbol indices. |
|
| 353 | + | fn fillDependencies 'input (input: &mut reader::Reader 'input, dependencies: *mut [*[u8]]) -> *[*[u8]] throws (binary::Error) { |
|
| 354 | + | for i in 0..dependencies.len { |
|
| 355 | + | set dependencies[i] = try reader::symbol(input); |
|
| 356 | + | } |
|
| 357 | + | return dependencies; |
|
| 358 | + | } |
|
| 359 | + | ||
| 360 | + | /// Fill owned exports from checked symbol indices and kind flags. |
|
| 361 | + | fn fillExports 'input (input: &mut reader::Reader 'input, exports: *mut [binary::Export]) -> *[binary::Export] throws (binary::Error) { |
|
| 362 | + | for i in 0..exports.len { |
|
| 363 | + | let symbol = try reader::symbol(input); |
|
| 364 | + | let kind = binary::ExportKind::Data if try reader::flag(input) else binary::ExportKind::Function; |
|
| 365 | + | set exports[i] = binary::Export { name: symbol, kind }; |
|
| 366 | + | } |
|
| 367 | + | return exports; |
|
| 368 | + | } |
|
| 369 | + | ||
| 350 | 370 | /// Decode one package. Restore the arena offset on every failure. |
|
| 351 | 371 | /// The caller must retain the decoded allocations until the last package use. |
|
| 352 | 372 | export unsafe fn decode(bytes: &[u8], arena: &mut alloc::Arena, limits: binary::Limits) |
|
| 353 | 373 | -> binary::Package throws (binary::Error) |
|
| 354 | 374 | { |