From 2583b389eaf5f7aaa0eb79b51126506c1e172d15 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 20 Mar 2024 17:02:35 -0700 Subject: [PATCH] frontend: comptime array slice-by-length OOB detection --- src/Sema.zig | 37 ++++++++++++++----- .../out of bounds array slice by length.zig | 15 ++++++++ .../out of bounds array slice by length.zig | 4 +- 3 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 test/cases/compile_errors/out of bounds array slice by length.zig diff --git a/src/Sema.zig b/src/Sema.zig index 6194e6c1bc7b..764aa95ebe11 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -33285,15 +33285,34 @@ fn analyzeSlice( } bounds_check: { - const actual_len = if (array_ty.zigTypeTag(mod) == .Array) - try mod.intRef(Type.usize, array_ty.arrayLenIncludingSentinel(mod)) - else if (slice_ty.isSlice(mod)) l: { - const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); - break :l if (slice_ty.sentinel(mod) == null) - slice_len_inst - else - try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); - } else break :bounds_check; + const actual_len = l: { + if (array_ty.zigTypeTag(mod) == .Array) { + const len = array_ty.arrayLenIncludingSentinel(mod); + // If the end is comptime-known, we can emit a + // compile error if it would be out-of-bounds even + // with a start value of 0. + if (uncasted_end_opt != .none) { + if (try sema.resolveDefinedValue(block, end_src, uncasted_end_opt)) |end_val| { + const end_int = end_val.getUnsignedInt(mod).?; + if (end_int > len) return sema.fail( + block, + end_src, + "slice end index {d} exceeds array length of type '{}'", + .{ end_int, array_ty.fmt(mod) }, + ); + } + } + break :l try mod.intRef(Type.usize, len); + } + if (slice_ty.isSlice(mod)) { + const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice); + break :l if (slice_ty.sentinel(mod) == null) + slice_len_inst + else + try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); + } + break :bounds_check; + }; const actual_end = if (slice_sentinel != null) try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) diff --git a/test/cases/compile_errors/out of bounds array slice by length.zig b/test/cases/compile_errors/out of bounds array slice by length.zig new file mode 100644 index 000000000000..75ed52eb7a78 --- /dev/null +++ b/test/cases/compile_errors/out of bounds array slice by length.zig @@ -0,0 +1,15 @@ +export fn b() void { + var buf: [5]u8 = undefined; + _ = buf[foo(6)..][0..10]; + return error.TestFailed; +} + +fn foo(a: u32) u32 { + return a; +} + +// error +// backend=stage2 +// target=native +// +// :3:26: error: slice end index 10 exceeds array length of type '[5]u8' diff --git a/test/cases/safety/out of bounds array slice by length.zig b/test/cases/safety/out of bounds array slice by length.zig index 62768abebf60..0c1de92fff20 100644 --- a/test/cases/safety/out of bounds array slice by length.zig +++ b/test/cases/safety/out of bounds array slice by length.zig @@ -2,14 +2,14 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { _ = stack_trace; - if (std.mem.eql(u8, message, "index out of bounds: index 16, len 5")) { + if (std.mem.eql(u8, message, "index out of bounds: index 9, len 5")) { std.process.exit(0); } std.process.exit(1); } pub fn main() !void { var buf: [5]u8 = undefined; - _ = buf[foo(6)..][0..10]; + _ = buf[foo(6)..][0..3]; return error.TestFailed; } fn foo(a: u32) u32 {