compiler: Check owned package header decoding

76b7420cc81f9b807a14780a806af17c6e88c1cd41d81831e9fdd35c0f0018d6
Alexis Sellier committed ago 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
{