compiler: Validate initializer extents in checked code
442ab96cb5a291b79efbd7069e5b511a4fb873f0c477b5f3b61f824ed7f01504
1 parent
8d09b8cb
lib/std/lang/il/binary/decodeTests.rad
+44 -0
| 288 | 288 | try rejected(&package, LIMITS); |
|
| 289 | 289 | set package.symbols = &["p", ""]; |
|
| 290 | 290 | try rejected(&package, LIMITS); |
|
| 291 | 291 | } |
|
| 292 | 292 | ||
| 293 | + | /// Initializer extents include every item kind and cumulative size boundaries. |
|
| 294 | + | @test unsafe fn initializerExtents() throws (testing::TestError) { |
|
| 295 | + | for typ in [il::Type::W8, il::Type::W16, il::Type::W32, il::Type::W64] { |
|
| 296 | + | try checkInitializerExtents(il::DataItem::Val { typ, val: 0 }, il::typeSize(typ)); |
|
| 297 | + | } |
|
| 298 | + | try checkInitializerExtents(il::DataItem::Sym("p"), 8); |
|
| 299 | + | try checkInitializerExtents(il::DataItem::Fn("p"), 8); |
|
| 300 | + | try checkInitializerExtents(il::DataItem::Str("abc"), 3); |
|
| 301 | + | try checkInitializerExtents(il::DataItem::Str(""), 0); |
|
| 302 | + | try checkInitializerExtents(il::DataItem::Undef, 1); |
|
| 303 | + | } |
|
| 304 | + | ||
| 305 | + | /// Test one repeated item followed by a byte at exact-fit and shorter sizes. |
|
| 306 | + | unsafe fn checkInitializerExtents(item: il::DataItem, width: u32) throws (testing::TestError) { |
|
| 307 | + | /// Stable initialized values retained by the fixture's data definition. |
|
| 308 | + | static values: [il::DataValue; 2] = [il::DataValue { item: il::DataItem::Undef, count: 1 }; 2]; |
|
| 309 | + | for count in [0 as u32, 1, 3, 0xffffffff] { |
|
| 310 | + | set values[0] = il::DataValue { item, count }; |
|
| 311 | + | let extent = width as u64 * count as u64 + 1; |
|
| 312 | + | let size = extent as u32 if extent <= 0xffffffff else 0xffffffff; |
|
| 313 | + | for declared in [size, size - 1] { |
|
| 314 | + | let definition = il::Data { |
|
| 315 | + | name: "p", size: declared, alignment: 1, readOnly: false, isZeroInit: false, |
|
| 316 | + | values: &values[..], |
|
| 317 | + | }; |
|
| 318 | + | let package = binary::Package { |
|
| 319 | + | symbols: &["p"], name: "p", dependencies: &[], exports: &[], entry: nil, |
|
| 320 | + | program: il::Program { data: retainData(&[definition]), fns: &[] }, |
|
| 321 | + | }; |
|
| 322 | + | let mut bytes: [u8; 256] = [0; 256]; |
|
| 323 | + | let length = try program::encode(&mut bytes[..], &package) catch { throw testing::TestError::Failed; }; |
|
| 324 | + | let mut arena = alloc::new(&mut MEMORY[..]); |
|
| 325 | + | set arena.offset = 8; |
|
| 326 | + | let mut failed = false; |
|
| 327 | + | try program::decode(&bytes[..length], &mut arena, LIMITS) catch err { |
|
| 328 | + | assert err == binary::Error::Invalid; |
|
| 329 | + | assert arena.offset == 8; |
|
| 330 | + | set failed = true; |
|
| 331 | + | }; |
|
| 332 | + | assert failed == (extent > declared as u64); |
|
| 333 | + | } |
|
| 334 | + | } |
|
| 335 | + | } |
|
| 336 | + | ||
| 293 | 337 | /// Check function block limits, parameter indices, and predecessor indices. |
|
| 294 | 338 | @test unsafe fn invalidFunctions() throws (testing::TestError) { |
|
| 295 | 339 | let mut instrs = [il::Instr::Ret { val: nil }]; |
|
| 296 | 340 | let mut block = il::Block { |
|
| 297 | 341 | label: "entry", params: &[], instrs: &mut instrs[..], locs: &[], preds: &[], loopDepth: 0, |
lib/std/lang/il/binary/program.rad
+25 -19
| 113 | 113 | set params[i] = il::Param { value, type: t }; |
|
| 114 | 114 | } |
|
| 115 | 115 | return (¶ms[..]) as *unsafe [il::Param]; |
|
| 116 | 116 | } |
|
| 117 | 117 | ||
| 118 | + | /// Extend an initializer extent without exceeding its declared storage. |
|
| 119 | + | fn extendDataExtent(extent: u64, value: il::DataValue, size: u32) -> u64 throws (binary::Error) { |
|
| 120 | + | let mut width: u32 = 0; |
|
| 121 | + | match value.item { |
|
| 122 | + | case il::DataItem::Val { typ, .. } => { |
|
| 123 | + | set width = il::typeSize(typ); |
|
| 124 | + | }, |
|
| 125 | + | case il::DataItem::Sym(_), il::DataItem::Fn(_) => { |
|
| 126 | + | set width = 8; |
|
| 127 | + | }, |
|
| 128 | + | case il::DataItem::Str(text) => { |
|
| 129 | + | set width = text.len; |
|
| 130 | + | }, |
|
| 131 | + | case il::DataItem::Undef => { |
|
| 132 | + | set width = 1; |
|
| 133 | + | }, |
|
| 134 | + | } |
|
| 135 | + | let amount = width as u64 * value.count as u64; |
|
| 136 | + | if extent > size as u64 or amount > size as u64 - extent { |
|
| 137 | + | throw binary::Error::Invalid; |
|
| 138 | + | } |
|
| 139 | + | return extent + amount; |
|
| 140 | + | } |
|
| 141 | + | ||
| 118 | 142 | /// Read global data and check initializer extents against declared storage. |
|
| 119 | 143 | unsafe fn readData 'input (input: &mut reader::Reader 'input) -> *[il::Data] throws (binary::Error) { |
|
| 120 | 144 | let n = try reader::count(input, 18); |
|
| 121 | 145 | let items = try reader::storage(input, @sizeOf(il::Data), @alignOf(il::Data), n) |
|
| 122 | 146 | as *mut [il::Data]; |
| 133 | 157 | let values = try reader::storage(input, @sizeOf(il::DataValue), @alignOf(il::DataValue), count) |
|
| 134 | 158 | as *mut [il::DataValue]; |
|
| 135 | 159 | let mut extent: u64 = 0; |
|
| 136 | 160 | for j in 0..count { |
|
| 137 | 161 | let value = try reader::dataValue(input); |
|
| 138 | - | let mut width: u32 = 0; |
|
| 139 | - | match value.item { |
|
| 140 | - | case il::DataItem::Val { typ, .. } => { |
|
| 141 | - | set width = il::typeSize(typ); |
|
| 142 | - | }, |
|
| 143 | - | case il::DataItem::Sym(_), il::DataItem::Fn(_) => { |
|
| 144 | - | set width = 8; |
|
| 145 | - | }, |
|
| 146 | - | case il::DataItem::Str(text) => { |
|
| 147 | - | set width = text.len; |
|
| 148 | - | }, |
|
| 149 | - | case il::DataItem::Undef => { |
|
| 150 | - | set width = 1; |
|
| 151 | - | }, |
|
| 152 | - | } |
|
| 153 | - | set extent += width as u64 * value.count as u64; |
|
| 154 | - | if extent > size as u64 { |
|
| 155 | - | throw binary::Error::Invalid; |
|
| 156 | - | } |
|
| 162 | + | set extent = try extendDataExtent(extent, value, size); |
|
| 157 | 163 | set values[j] = value; |
|
| 158 | 164 | } |
|
| 159 | 165 | set items[i] = il::Data { name, size, alignment, readOnly, isZeroInit, values }; |
|
| 160 | 166 | } |
|
| 161 | 167 | return items; |