authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-03-20 17:02:35-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-03-20 17:02:35-07:00
log2583b389eaf5f7aaa0eb79b51126506c1e172d15
tree2ff98baf24f1738e4401c88207a5a8c8edeffa1f
parentab22844176f422aeb4f523ba38f21b12d78760b3

frontend: comptime array slice-by-length OOB detection


3 files changed, 45 insertions(+), 11 deletions(-)

src/Sema.zig+28-9
...@@ -33285,15 +33285,34 @@ fn analyzeSlice(...@@ -33285,15 +33285,34 @@ fn analyzeSlice(
33285 }33285 }
3328633286
33287 bounds_check: {33287 bounds_check: {
33288 const actual_len = if (array_ty.zigTypeTag(mod) == .Array)33288 const actual_len = l: {
33289 try mod.intRef(Type.usize, array_ty.arrayLenIncludingSentinel(mod))33289 if (array_ty.zigTypeTag(mod) == .Array) {
33290 else if (slice_ty.isSlice(mod)) l: {33290 const len = array_ty.arrayLenIncludingSentinel(mod);
33291 const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice);33291 // If the end is comptime-known, we can emit a
33292 break :l if (slice_ty.sentinel(mod) == null)33292 // compile error if it would be out-of-bounds even
33293 slice_len_inst33293 // with a start value of 0.
33294 else33294 if (uncasted_end_opt != .none) {
33295 try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);33295 if (try sema.resolveDefinedValue(block, end_src, uncasted_end_opt)) |end_val| {
33296 } else break :bounds_check;33296 const end_int = end_val.getUnsignedInt(mod).?;
33297 if (end_int > len) return sema.fail(
33298 block,
33299 end_src,
33300 "slice end index {d} exceeds array length of type '{}'",
33301 .{ end_int, array_ty.fmt(mod) },
33302 );
33303 }
33304 }
33305 break :l try mod.intRef(Type.usize, len);
33306 }
33307 if (slice_ty.isSlice(mod)) {
33308 const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice);
33309 break :l if (slice_ty.sentinel(mod) == null)
33310 slice_len_inst
33311 else
33312 try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
33313 }
33314 break :bounds_check;
33315 };
3329733316
33298 const actual_end = if (slice_sentinel != null)33317 const actual_end = if (slice_sentinel != null)
33299 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)33318 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
test/cases/compile_errors/out of bounds array slice by length.zig created+15
...@@ -0,0 +1,15 @@
1export fn b() void {
2 var buf: [5]u8 = undefined;
3 _ = buf[foo(6)..][0..10];
4 return error.TestFailed;
5}
6
7fn foo(a: u32) u32 {
8 return a;
9}
10
11// error
12// backend=stage2
13// target=native
14//
15// :3:26: error: slice end index 10 exceeds array length of type '[5]u8'
test/cases/safety/out of bounds array slice by length.zig +2-2
...@@ -2,14 +2,14 @@ const std = @import("std");...@@ -2,14 +2,14 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "index out of bounds: index 16, len 5")) {5 if (std.mem.eql(u8, message, "index out of bounds: index 9, len 5")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
9}9}
10pub fn main() !void {10pub fn main() !void {
11 var buf: [5]u8 = undefined;11 var buf: [5]u8 = undefined;
12 _ = buf[foo(6)..][0..10];12 _ = buf[foo(6)..][0..3];
13 return error.TestFailed;13 return error.TestFailed;
14}14}
15fn foo(a: u32) u32 {15fn foo(a: u32) u32 {