compiler: Borrow binary decoder symbol tables safely

3e51617aad2f307942e0386a03f63f6a5b81f2e8cd4484680d6f6f35759069e7
Alexis Sellier committed ago 1 parent ed2fbb6c
lib/std/lang/il/binary/decodeTests.rad +9 -6
52 52
        };
53 53
        try testing::expect(failed);
54 54
        try testing::expect(arena.offset == 8);
55 55
    }
56 56
    set arena.offset = 8;
57 -
    let source: 'input = &buffer[..0] in {
58 -
        let mut input = reader::new(source, &mut arena, &[]);
57 +
    let namesTable: [*[u8]; 0] = [];
58 +
    let source: 'input = &buffer[..0], names = &namesTable[..] in {
59 +
        let mut input = reader::new(source, &mut arena, names);
59 60
        let mut exhausted = false;
60 61
        try reader::storage(&mut input, 0xffffffff, 8, 0xffffffff) catch err {
61 62
            try testing::expect(err == binary::Error::Storage);
62 63
            set exhausted = true;
63 64
        };
151 152
152 153
/// Reject an invalid encoded instruction without a panic.
153 154
unsafe fn badInstruction(bytes: *[u8], expected: binary::Error) throws (testing::TestError) {
154 155
    let memory = &mut MEMORY[..64];
155 156
    let mut arena = alloc::new(&mut memory[..]);
156 -
    let source: 'input = &bytes[..] in {
157 -
        let mut input = reader::new(source, &mut arena, &["p"]);
157 +
    let namesTable: [*[u8]; 1] = ["p"];
158 +
    let source: 'input = &bytes[..], names = &namesTable[..] in {
159 +
        let mut input = reader::new(source, &mut arena, names);
158 160
        set input.registers = 2;
159 161
        set input.blocks = 1;
160 162
        let mut failed = false;
161 163
        try reader::instr(&mut input) catch err {
162 164
            try testing::expect(err == expected);
297 299
@test unsafe fn malformedInitializers() throws (testing::TestError) {
298 300
    let memory = &mut MEMORY[..8];
299 301
    let mut arena = alloc::new(&mut memory[..]);
300 302
    let fixtures: [*[u8]; 2] = [&[255], &[0, 3]];
301 303
    for bytes in &fixtures[..] {
302 -
        let source: 'input = &bytes[..] in {
303 -
            let mut input = reader::new(source, &mut arena, &[]);
304 +
        let namesTable: [*[u8]; 0] = [];
305 +
        let source: 'input = &bytes[..], names = &namesTable[..] in {
306 +
            let mut input = reader::new(source, &mut arena, names);
304 307
            let mut failed = false;
305 308
            try reader::dataValue(&mut input) catch err {
306 309
                try testing::expect(err == binary::Error::Invalid);
307 310
                set failed = true;
308 311
            };
lib/std/lang/il/binary/program.rad +32 -27
216 216
                throw binary::Error::Invalid;
217 217
            }
218 218
        }
219 219
        set symbols[i] = name;
220 220
    }
221 -
    set input.symbols = (&symbols[..]) as *unsafe [*[u8]];
222 -
    let name = try reader::symbol(input);
223 -
    let depCount = try reader::count(input, 4);
224 -
    let dependencies = try reader::storage(input, @sizeOf(*[u8]), @alignOf(*[u8]), depCount)
225 -
        as *mut [*[u8]];
226 -
    for i in 0..depCount {
227 -
        set dependencies[i] = try reader::symbol(input);
228 -
    }
229 -
    let exportCount = try reader::count(input, 5);
230 -
    let exports = try reader::storage(input, @sizeOf(binary::Export), @alignOf(binary::Export), exportCount)
231 -
        as *mut [binary::Export];
232 -
    for i in 0..exportCount {
233 -
        let symbol = try reader::symbol(input);
234 -
        let kind = binary::ExportKind::Data if try reader::flag(input) else binary::ExportKind::Function;
235 -
        set exports[i] = binary::Export { name: symbol, kind };
236 -
    }
237 -
    let mut entry: ?*[u8] = nil;
238 -
    if try reader::flag(input) {
239 -
        set entry = try reader::symbol(input);
240 -
    }
241 -
    let data = try readData(input);
242 -
    let fns = try readFunctions(input, limits);
243 -
    if input.offset <> input.bytes.len {
244 -
        throw binary::Error::Invalid;
221 +
    let source: 'tables = &input.bytes[..], names = &symbols[..] where 'input: 'tables in {
222 +
        let mut cursor = reader::new(source, input.arena, names);
223 +
        set cursor.offset = input.offset;
224 +
        let name = try reader::symbol(&mut cursor);
225 +
        let depCount = try reader::count(&mut cursor, 4);
226 +
        let dependencies = try reader::storage(&mut cursor, @sizeOf(*[u8]), @alignOf(*[u8]), depCount)
227 +
            as *mut [*[u8]];
228 +
        for i in 0..depCount {
229 +
            set dependencies[i] = try reader::symbol(&mut cursor);
230 +
        }
231 +
        let exportCount = try reader::count(&mut cursor, 5);
232 +
        let exports = try reader::storage(&mut cursor, @sizeOf(binary::Export), @alignOf(binary::Export), exportCount)
233 +
            as *mut [binary::Export];
234 +
        for i in 0..exportCount {
235 +
            let symbol = try reader::symbol(&mut cursor);
236 +
            let kind = binary::ExportKind::Data if try reader::flag(&mut cursor) else binary::ExportKind::Function;
237 +
            set exports[i] = binary::Export { name: symbol, kind };
238 +
        }
239 +
        let mut entry: ?*[u8] = nil;
240 +
        if try reader::flag(&mut cursor) {
241 +
            set entry = try reader::symbol(&mut cursor);
242 +
        }
243 +
        let data = try readData(&mut cursor);
244 +
        let fns = try readFunctions(&mut cursor, limits);
245 +
        if cursor.offset <> cursor.bytes.len {
246 +
            throw binary::Error::Invalid;
247 +
        }
248 +
        set input.offset = cursor.offset;
249 +
        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 } };
245 250
    }
246 -
    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 } };
247 251
}
248 252
249 253
/// Decode one package. Restore the arena offset on every failure.
250 254
/// The caller must retain the decoded allocations until the last package use.
251 255
export unsafe fn decode(bytes: &[u8], arena: &mut alloc::Arena, limits: binary::Limits)
252 256
    -> binary::Package throws (binary::Error)
253 257
{
254 258
    let saved = alloc::save(arena);
255 -
    let source: 'input = &bytes[..] in {
256 -
        let mut input = reader::new(source, &mut *arena, &[]);
259 +
    let namesTable: [*[u8]; 0] = [];
260 +
    let source: 'input = &bytes[..], names = &namesTable[..] in {
261 +
        let mut input = reader::new(source, &mut *arena, names);
257 262
        return try readPackage(&mut input, limits) catch err {
258 263
            alloc::restore(arena, saved);
259 264
            throw err;
260 265
        };
261 266
    }
lib/std/lang/il/binary/reader.rad +6 -6
4 4
use std::lang::il;
5 5
use std::lang::il::binary;
6 6
use std::lang::alloc;
7 7
8 8
/// Input cursor and reconstruction storage.
9 -
/// Input bytes are borrowed for the reader's region. Allocation and symbol
10 -
/// access require valid raw storage.
9 +
/// Input bytes and the symbol table are borrowed for the reader's region.
10 +
/// Allocation requires valid raw storage.
11 11
export record Reader: 'input + Copy {
12 12
    /// Encoded bytes.
13 13
    bytes: &'input [u8],
14 14
    /// Number of bytes consumed.
15 15
    offset: u32,
16 16
    /// Storage for decoded sequences and byte strings.
17 17
    arena: *unsafe mut alloc::Arena,
18 18
    /// Symbol names in wire-index order.
19 -
    symbols: *unsafe [*[u8]],
19 +
    symbols: &'input [*[u8]],
20 20
    /// Exclusive bound for SSA register numbers.
21 21
    registers: u32,
22 22
    /// Number of blocks in the current function.
23 23
    blocks: u32,
24 24
}
25 25
26 26
/// Create a cursor. Set function bounds before reading instructions.
27 -
export fn new 'input (bytes: &'input [u8], arena: *unsafe mut alloc::Arena, symbols: *unsafe [*[u8]]) -> Reader 'input {
27 +
export fn new 'input (bytes: &'input [u8], arena: *unsafe mut alloc::Arena, symbols: &'input [*[u8]]) -> Reader 'input {
28 28
    return Reader 'input { bytes, offset: 0, arena, symbols, registers: 0, blocks: 0 };
29 29
}
30 30
31 31
/// Read an unsigned integer with width 1, 2, 4, or 8.
32 32
export fn integer 'input (input: &mut Reader 'input, width: u32) -> u64 throws (binary::Error) {
83 83
    set input.offset += n;
84 84
    return result;
85 85
}
86 86
87 87
/// Resolve a checked symbol-table index.
88 -
export unsafe fn symbol 'input (input: &mut Reader 'input) -> *[u8] throws (binary::Error) {
88 +
export fn symbol 'input (input: &mut Reader 'input) -> *[u8] throws (binary::Error) {
89 89
    let index = try integer(input, 4) as u32;
90 90
    if index >= input.symbols.len {
91 91
        throw binary::Error::Symbol;
92 92
    }
93 93
    return input.symbols[index];
122 122
    }
123 123
    return n;
124 124
}
125 125
126 126
/// Read a tagged value with checked register and symbol indices.
127 -
export unsafe fn val 'input (input: &mut Reader 'input) -> il::Val throws (binary::Error) {
127 +
export fn val 'input (input: &mut Reader 'input) -> il::Val throws (binary::Error) {
128 128
    let tag = try integer(input, 1) as u8;
129 129
    match tag {
130 130
        case super::VALUE_REG => return il::Val::Reg(try reg(input)),
131 131
        case super::VALUE_IMM => return il::Val::Imm(try integer(input, 8) as i64),
132 132
        case super::VALUE_DATASYM => return il::Val::DataSym(try symbol(input)),
lib/std/lang/il/binary/tests.rad +43 -6
41 41
/// A regional input buffer supports safe scalar decoding.
42 42
@test unsafe fn scalarReader() throws (testing::TestError) {
43 43
    let memory = &mut MEMORY[..];
44 44
    let mut arena = alloc::new(memory);
45 45
    let bytes: [u8; 8] = [1, 0, 0, 0, 2, 0, 0, 0];
46 -
    let source: 'input = &bytes[..] in {
47 -
        let mut input = reader::new(source, &mut arena, &[]);
46 +
    let namesTable: [*[u8]; 0] = [];
47 +
    let source: 'input = &bytes[..], names = &namesTable[..] in {
48 +
        let mut input = reader::new(source, &mut arena, names);
48 49
        try checkScalarReader(&mut input);
49 50
    }
50 51
}
51 52
53 +
/// Read valid symbol values and reject invalid or truncated wire indices.
54 +
fn checkSymbolReader 'input (input: &mut reader::Reader 'input) throws (testing::TestError) {
55 +
    let name = try reader::symbol(input) catch { throw testing::TestError::Failed; };
56 +
    try testing::expectBytesEq(name, "data");
57 +
    let data = try reader::val(input) catch { throw testing::TestError::Failed; };
58 +
    let case il::Val::DataSym(dataName) = data else throw testing::TestError::Failed;
59 +
    try testing::expectBytesEq(dataName, "data");
60 +
    let function = try reader::val(input) catch { throw testing::TestError::Failed; };
61 +
    let case il::Val::FnAddr(functionName) = function else throw testing::TestError::Failed;
62 +
    try testing::expectBytesEq(functionName, "fn");
63 +
    let mut invalid = false;
64 +
    try reader::symbol(input) catch err {
65 +
        assert err == binary::Error::Symbol;
66 +
        set invalid = true;
67 +
    };
68 +
    assert invalid;
69 +
    let mut truncated = false;
70 +
    try reader::val(input) catch err {
71 +
        assert err == binary::Error::Truncated;
72 +
        set truncated = true;
73 +
    };
74 +
    assert truncated;
75 +
}
76 +
77 +
/// Symbol and value decoding use checked table storage from a safe function.
78 +
@test unsafe fn symbolReader() throws (testing::TestError) {
79 +
    let memory = &mut MEMORY[..];
80 +
    let mut arena = alloc::new(memory);
81 +
    let bytes: [u8; 18] = [0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 1, 0, 0, 0, 2, 0, 0, 0];
82 +
    let namesTable: [*[u8]; 2] = ["data", "fn"];
83 +
    let source: 'input = &bytes[..], names = &namesTable[..] in {
84 +
        let mut input = reader::new(source, &mut arena, names);
85 +
        try checkSymbolReader(&mut input);
86 +
    }
87 +
}
88 +
52 89
/// Check little-endian encoding for every integer width.
53 90
@test fn integers() throws (testing::TestError) {
54 91
    let mut buffer: [u8; 15] = [0; 15];
55 92
    let namesTable: [*[u8]; 0] = [];
56 93
    let storage: 'buffer = &mut buffer[..], names = &namesTable[..] in {
94 131
                try testing::expect(short.offset <= capacity);
95 132
            }
96 133
        }
97 134
        let memory = &mut MEMORY[..512];
98 135
        let mut arena = alloc::new(&mut memory[..]);
99 -
        let source: 'input = &expected[..] in {
100 -
            let mut input = reader::new(source, &mut arena, &["data", "fn"]);
136 +
        let source: 'input = &expected[..], inputNames = &namesTable[..] in {
137 +
            let mut input = reader::new(source, &mut arena, inputNames);
101 138
            set input.registers = 16;
102 139
            set input.blocks = 4;
103 140
            let decoded = try reader::instr(&mut input) catch {
104 141
                throw testing::TestError::Failed;
105 142
            };
109 146
                throw testing::TestError::Failed;
110 147
            };
111 148
            try testing::expectBytesEq(&out.bytes[..out.offset], expected);
112 149
            for length in 0..expected.len {
113 150
                alloc::reset(&mut arena);
114 -
                set input = reader::new(&source[..length], &mut arena, &["data", "fn"]);
151 +
                set input = reader::new(&source[..length], &mut arena, inputNames);
115 152
                set input.registers = 16;
116 153
                set input.blocks = 4;
117 154
                let mut failed = false;
118 155
                try reader::instr(&mut input) catch err {
119 156
                    try testing::expect(err == binary::Error::Truncated);
232 269
            1, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
233 270
            2, 0, 0, 0, 0, 3, 1, 0, 0, 0, 4,
234 271
        ]);
235 272
        let memory = &mut MEMORY[..256];
236 273
        let mut arena = alloc::new(&mut memory[..]);
237 -
        let mut input = reader::new(&out.bytes[..out.offset], &mut arena, &["data", "fn"]);
274 +
        let mut input = reader::new(&out.bytes[..out.offset], &mut arena, &out.symbols[..]);
238 275
        set input.registers = 0x12345679;
239 276
        let empty = try reader::values(&mut input) catch {
240 277
            throw testing::TestError::Failed;
241 278
        };
242 279
        let decoded = try reader::values(&mut input) catch {