authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-29 14:29:50+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-29 15:47:02+02:00
log6337c04244d9c27cc6535340347d4c127f4742eb
tree3e70b1474b28a0f10428255648b59ab64a71b35d
parent6f9c7e33b956686ebfd4690c7f85a602d0ac9ffe

Sema: improve panic for slice start index being greater than end index

Closes #13689

3 files changed, 86 insertions(+), 12 deletions(-)

lib/std/builtin.zig+8-3
......@@ -863,10 +863,9 @@ pub fn panicOutOfBounds(index: usize, len: usize) noreturn {
863863 std.debug.panicExtra(null, @returnAddress(), "index out of bounds: index {d}, len {d}", .{ index, len });
864864}
865865
866pub noinline fn returnError(st: *StackTrace) void {
866pub fn panicStartGreaterThanEnd(start: usize, end: usize) noreturn {
867867 @setCold(true);
868 @setRuntimeSafety(false);
869 addErrRetTraceAddr(st, @returnAddress());
868 std.debug.panicExtra(null, @returnAddress(), "start index {d} is larger than end index {d}", .{ start, end });
870869}
871870
872871pub const panic_messages = struct {
......@@ -889,6 +888,12 @@ pub const panic_messages = struct {
889888 pub const invalid_enum_value = "invalid enum value";
890889};
891890
891pub noinline fn returnError(st: *StackTrace) void {
892 @setCold(true);
893 @setRuntimeSafety(false);
894 addErrRetTraceAddr(st, @returnAddress());
895}
896
892897pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void {
893898 if (st.index < st.instruction_addresses.len)
894899 st.instruction_addresses[st.index] = addr;
src/Sema.zig+55-9
......@@ -12437,7 +12437,7 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1243712437 else
1243812438 try sema.resolveInst(.zero);
1243912439
12440 return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src);
12440 return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true);
1244112441}
1244212442
1244312443fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -12460,7 +12460,7 @@ fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
1246012460 else
1246112461 try sema.resolveInst(.zero);
1246212462
12463 return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src);
12463 return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true);
1246412464}
1246512465
1246612466fn zirArithmetic(
......@@ -12480,7 +12480,7 @@ fn zirArithmetic(
1248012480 const lhs = try sema.resolveInst(extra.lhs);
1248112481 const rhs = try sema.resolveInst(extra.rhs);
1248212482
12483 return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src);
12483 return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src, true);
1248412484}
1248512485
1248612486fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -13776,6 +13776,7 @@ fn analyzeArithmetic(
1377613776 src: LazySrcLoc,
1377713777 lhs_src: LazySrcLoc,
1377813778 rhs_src: LazySrcLoc,
13779 want_safety: bool,
1377913780) CompileError!Air.Inst.Ref {
1378013781 const lhs_ty = sema.typeOf(lhs);
1378113782 const rhs_ty = sema.typeOf(rhs);
......@@ -14204,7 +14205,7 @@ fn analyzeArithmetic(
1420414205 };
1420514206
1420614207 try sema.requireRuntimeBlock(block, src, rs.src);
14207 if (block.wantSafety()) {
14208 if (block.wantSafety() and want_safety) {
1420814209 if (scalar_tag == .Int) {
1420914210 const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) {
1421014211 .add => .add_with_overflow,
......@@ -22334,6 +22335,47 @@ fn panicIndexOutOfBounds(
2233422335 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
2233522336}
2233622337
22338fn panicStartLargerThanEnd(
22339 sema: *Sema,
22340 parent_block: *Block,
22341 src: LazySrcLoc,
22342 start: Air.Inst.Ref,
22343 end: Air.Inst.Ref,
22344) !void {
22345 assert(!parent_block.is_comptime);
22346 const ok = try parent_block.addBinOp(.cmp_lte, start, end);
22347 const gpa = sema.gpa;
22348
22349 var fail_block: Block = .{
22350 .parent = parent_block,
22351 .sema = sema,
22352 .src_decl = parent_block.src_decl,
22353 .namespace = parent_block.namespace,
22354 .wip_capture_scope = parent_block.wip_capture_scope,
22355 .instructions = .{},
22356 .inlining = parent_block.inlining,
22357 .is_comptime = false,
22358 };
22359
22360 defer fail_block.instructions.deinit(gpa);
22361
22362 {
22363 const this_feature_is_implemented_in_the_backend =
22364 sema.mod.comp.bin_file.options.use_llvm;
22365
22366 if (!this_feature_is_implemented_in_the_backend) {
22367 // TODO implement this feature in all the backends and then delete this branch
22368 _ = try fail_block.addNoOp(.breakpoint);
22369 _ = try fail_block.addNoOp(.unreach);
22370 } else {
22371 const panic_fn = try sema.getBuiltin("panicStartGreaterThanEnd");
22372 const args: [2]Air.Inst.Ref = .{ start, end };
22373 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
22374 }
22375 }
22376 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
22377}
22378
2233722379fn panicSentinelMismatch(
2233822380 sema: *Sema,
2233922381 parent_block: *Block,
......@@ -28028,7 +28070,11 @@ fn analyzeSlice(
2802828070 }
2802928071 }
2803028072
28031 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);
28073 if (block.wantSafety() and !block.is_comptime) {
28074 // requirement: start <= end
28075 try sema.panicStartLargerThanEnd(block, src, start, end);
28076 }
28077 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
2803228078 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
2803328079
2803428080 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
......@@ -28063,10 +28109,10 @@ fn analyzeSlice(
2806328109 const actual_len = if (slice_ty.sentinel() == null)
2806428110 slice_len_inst
2806528111 else
28066 try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
28112 try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
2806728113
2806828114 const actual_end = if (slice_sentinel != null)
28069 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src)
28115 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
2807028116 else
2807128117 end;
2807228118
......@@ -28131,11 +28177,11 @@ fn analyzeSlice(
2813128177 if (slice_ty.sentinel() == null) break :blk slice_len_inst;
2813228178
2813328179 // we have to add one because slice lengths don't include the sentinel
28134 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
28180 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
2813528181 } else null;
2813628182 if (opt_len_inst) |len_inst| {
2813728183 const actual_end = if (slice_sentinel != null)
28138 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src)
28184 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
2813928185 else
2814028186 end;
2814128187 try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte);
test/cases/safety/slice start index greater than end index.zig created+23
......@@ -0,0 +1,23 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "start index 10 is larger than end index 1")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11pub fn main() !void {
12 var a: usize = 1;
13 var b: usize = 10;
14 var buf: [16]u8 = undefined;
15
16 const slice = buf[b..a];
17 _ = slice;
18 return error.TestFailed;
19}
20
21// run
22// backend=llvm
23// target=native