From 3f07d1c7b8417ebf336a758a2b2b4190ed43d577 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 6 May 2025 16:53:14 +0200 Subject: [PATCH] Fix No index OOB check for `[:^n]` #2123 --- releasenotes.md | 1 + src/compiler/llvm_codegen_expr.c | 7 +- test/test_suite/arrays/slice_end_assert.c3t | 186 ++++++++++++++++++++ 3 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 test/test_suite/arrays/slice_end_assert.c3t diff --git a/releasenotes.md b/releasenotes.md index 3fd13e6cc..d5e4140bc 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -9,6 +9,7 @@ - Assert triggered when casting from `int[2]` to `uint[2]` #2115 - Assert when a macro with compile time value is discarded, e.g. `foo();` where `foo()` returns an untyped list. #2117 - Fix stringify for compound initializers #2120. +- Fix No index OOB check for `[:^n]` #2123 ### Stdlib changes - Added `String.quick_ztr` and `String.is_zstr` diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index a6c7733f8..694ade6cb 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -2704,11 +2704,15 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r // This will trap any bad negative index, so we're fine. if (safe_mode_enabled()) { + BEValue excess; if (is_len_range) { + llvm_emit_int_comp(c, &excess, &start_index, &end_index, BINARYOP_GT); + BEValue actual_end_len = end_index; + actual_end_len.value = llvm_emit_sub_int(c, end_index.type, end_index.value, start_index.value, slice->span); + llvm_emit_panic_if_true(c, &excess, "Negative slice length", slice->span, "Negative value (%d) given for slice length.", &actual_end_len, NULL); if (len.value) { - BEValue excess; llvm_emit_int_comp(c, &excess, &len, &end_index, BINARYOP_LT); BEValue actual_end_index = end_index; actual_end_index.value = llvm_emit_sub_int(c, end_index.type, end_index.value, llvm_const_int(c, type_isz, 1), slice->span); @@ -2717,7 +2721,6 @@ static void llvm_emit_slice_values(GenContext *c, Expr *slice, BEValue *parent_r } else { - BEValue excess; llvm_emit_int_comp(c, &excess, &start_index, &end_index, BINARYOP_GT); llvm_emit_panic_if_true(c, &excess, "Negative size", slice->span, "Negative size (start %d is less than end %d)", &start_index, &end_index); diff --git a/test/test_suite/arrays/slice_end_assert.c3t b/test/test_suite/arrays/slice_end_assert.c3t new file mode 100644 index 000000000..08849aa61 --- /dev/null +++ b/test/test_suite/arrays/slice_end_assert.c3t @@ -0,0 +1,186 @@ +// #target: macos-x64 +// #safe: yes +module test; +fn int main() +{ + String x = "ABCD"; + int z = 5; + String y = x[1:^8]; + x[0:^z]; + return 0; +} + +/* #expect: test.ll + +define i32 @main() #0 { +entry: + %x = alloca %"char[]", align 8 + %z = alloca i32, align 4 + %y = alloca %"char[]", align 8 + %taddr = alloca i64, align 8 + %taddr1 = alloca i64, align 8 + %varargslots = alloca [2 x %any], align 16 + %indirectarg = alloca %"any[]", align 8 + %taddr5 = alloca i64, align 8 + %varargslots6 = alloca [1 x %any], align 16 + %indirectarg8 = alloca %"any[]", align 8 + %taddr12 = alloca i64, align 8 + %taddr13 = alloca i64, align 8 + %varargslots14 = alloca [2 x %any], align 16 + %indirectarg17 = alloca %"any[]", align 8 + %taddr22 = alloca i64, align 8 + %taddr23 = alloca i64, align 8 + %varargslots24 = alloca [2 x %any], align 16 + %indirectarg27 = alloca %"any[]", align 8 + %taddr34 = alloca i64, align 8 + %varargslots35 = alloca [1 x %any], align 16 + %indirectarg37 = alloca %"any[]", align 8 + %taddr42 = alloca i64, align 8 + %taddr43 = alloca i64, align 8 + %varargslots44 = alloca [2 x %any], align 16 + %indirectarg47 = alloca %"any[]", align 8 + store %"char[]" { ptr @.str, i64 4 }, ptr %x, align 8 + store i32 5, ptr %z, align 4 + %0 = load %"char[]", ptr %x, align 8 + %1 = extractvalue %"char[]" %0, 0 + %2 = extractvalue %"char[]" %0, 1 + %gt = icmp sgt i64 1, %2 + %3 = call i1 @llvm.expect.i1(i1 %gt, i1 false) + br i1 %3, label %panic, label %checkok + +checkok: ; preds = %entry + %sub = sub i64 %2, 8 + %add = add i64 1, %sub + %gt2 = icmp sgt i64 1, %add + %sub3 = sub i64 %add, 1 + %4 = call i1 @llvm.expect.i1(i1 %gt2, i1 false) + br i1 %4, label %panic4, label %checkok9 + +checkok9: ; preds = %checkok + %lt = icmp slt i64 %2, %add + %sub10 = sub i64 %add, 1 + %5 = call i1 @llvm.expect.i1(i1 %lt, i1 false) + br i1 %5, label %panic11, label %checkok18 + +checkok18: ; preds = %checkok9 + %size = sub i64 %add, 1 + %ptradd19 = getelementptr inbounds i8, ptr %1, i64 1 + %6 = insertvalue %"char[]" undef, ptr %ptradd19, 0 + %7 = insertvalue %"char[]" %6, i64 %size, 1 + store %"char[]" %7, ptr %y, align 8 + %8 = load %"char[]", ptr %x, align 8 + %9 = extractvalue %"char[]" %8, 0 + %10 = extractvalue %"char[]" %8, 1 + %gt20 = icmp sgt i64 0, %10 + %11 = call i1 @llvm.expect.i1(i1 %gt20, i1 false) + br i1 %11, label %panic21, label %checkok28 + +checkok28: ; preds = %checkok18 + %12 = load i32, ptr %z, align 4 + %sext = sext i32 %12 to i64 + %sub29 = sub i64 %10, %sext + %add30 = add i64 0, %sub29 + %gt31 = icmp sgt i64 0, %add30 + %sub32 = sub i64 %add30, 0 + %13 = call i1 @llvm.expect.i1(i1 %gt31, i1 false) + br i1 %13, label %panic33, label %checkok38 + +checkok38: ; preds = %checkok28 + %lt39 = icmp slt i64 %10, %add30 + %sub40 = sub i64 %add30, 1 + %14 = call i1 @llvm.expect.i1(i1 %lt39, i1 false) + br i1 %14, label %panic41, label %checkok48 + +checkok48: ; preds = %checkok38 + %size49 = sub i64 %add30, 0 + %15 = insertvalue %"char[]" undef, ptr %9, 0 + %16 = insertvalue %"char[]" %15, i64 %size49, 1 + ret i32 0 + +panic: ; preds = %entry + store i64 %2, ptr %taddr, align 8 + %17 = insertvalue %any undef, ptr %taddr, 0 + %18 = insertvalue %any %17, i64 ptrtoint (ptr @"$ct.long" to i64), 1 + store i64 1, ptr %taddr1, align 8 + %19 = insertvalue %any undef, ptr %taddr1, 0 + %20 = insertvalue %any %19, i64 ptrtoint (ptr @"$ct.long" to i64), 1 + store %any %18, ptr %varargslots, align 16 + %ptradd = getelementptr inbounds i8, ptr %varargslots, i64 16 + store %any %20, ptr %ptradd, align 16 + %21 = insertvalue %"any[]" undef, ptr %varargslots, 0 + %"$$temp" = insertvalue %"any[]" %21, i64 2, 1 + store %"any[]" %"$$temp", ptr %indirectarg, align 8 + call void @std.core.builtin.panicf(ptr @.panic_msg, i64 61, ptr @.file, i64 19, ptr @.func, i64 4, i32 6, ptr byval(%"any[]") align 8 %indirectarg) #2 + unreachable + +panic4: ; preds = %checkok + store i64 %sub3, ptr %taddr5, align 8 + %22 = insertvalue %any undef, ptr %taddr5, 0 + %23 = insertvalue %any %22, i64 ptrtoint (ptr @"$ct.long" to i64), 1 + store %any %23, ptr %varargslots6, align 16 + %24 = insertvalue %"any[]" undef, ptr %varargslots6, 0 + %"$$temp7" = insertvalue %"any[]" %24, i64 1, 1 + store %"any[]" %"$$temp7", ptr %indirectarg8, align 8 + call void @std.core.builtin.panicf(ptr @.panic_msg.1, i64 43, ptr @.file, i64 19, ptr @.func, i64 4, i32 6, ptr byval(%"any[]") align 8 %indirectarg8) #2 + unreachable + +panic11: ; preds = %checkok9 + store i64 %sub10, ptr %taddr12, align 8 + %25 = insertvalue %any undef, ptr %taddr12, 0 + %26 = insertvalue %any %25, i64 ptrtoint (ptr @"$ct.long" to i64), 1 + store i64 %2, ptr %taddr13, align 8 + %27 = insertvalue %any undef, ptr %taddr13, 0 + %28 = insertvalue %any %27, i64 ptrtoint (ptr @"$ct.long" to i64), 1 + store %any %26, ptr %varargslots14, align 16 + %ptradd15 = getelementptr inbounds i8, ptr %varargslots14, i64 16 + store %any %28, ptr %ptradd15, align 16 + %29 = insertvalue %"any[]" undef, ptr %varargslots14, 0 + %"$$temp16" = insertvalue %"any[]" %29, i64 2, 1 + store %"any[]" %"$$temp16", ptr %indirectarg17, align 8 + call void @std.core.builtin.panicf(ptr @.panic_msg.2, i64 60, ptr @.file, i64 19, ptr @.func, i64 4, i32 6, ptr byval(%"any[]") align 8 %indirectarg17) #2 + unreachable + +panic21: ; preds = %checkok18 + store i64 %10, ptr %taddr22, align 8 + %30 = insertvalue %any undef, ptr %taddr22, 0 + %31 = insertvalue %any %30, i64 ptrtoint (ptr @"$ct.long" to i64), 1 + store i64 0, ptr %taddr23, align 8 + %32 = insertvalue %any undef, ptr %taddr23, 0 + %33 = insertvalue %any %32, i64 ptrtoint (ptr @"$ct.long" to i64), 1 + store %any %31, ptr %varargslots24, align 16 + %ptradd25 = getelementptr inbounds i8, ptr %varargslots24, i64 16 + store %any %33, ptr %ptradd25, align 16 + %34 = insertvalue %"any[]" undef, ptr %varargslots24, 0 + %"$$temp26" = insertvalue %"any[]" %34, i64 2, 1 + store %"any[]" %"$$temp26", ptr %indirectarg27, align 8 + call void @std.core.builtin.panicf(ptr @.panic_msg, i64 61, ptr @.file, i64 19, ptr @.func, i64 4, i32 7, ptr byval(%"any[]") align 8 %indirectarg27) #2 + unreachable + +panic33: ; preds = %checkok28 + store i64 %sub32, ptr %taddr34, align 8 + %35 = insertvalue %any undef, ptr %taddr34, 0 + %36 = insertvalue %any %35, i64 ptrtoint (ptr @"$ct.long" to i64), 1 + store %any %36, ptr %varargslots35, align 16 + %37 = insertvalue %"any[]" undef, ptr %varargslots35, 0 + %"$$temp36" = insertvalue %"any[]" %37, i64 1, 1 + store %"any[]" %"$$temp36", ptr %indirectarg37, align 8 + call void @std.core.builtin.panicf(ptr @.panic_msg.1, i64 43, ptr @.file, i64 19, ptr @.func, i64 4, i32 7, ptr byval(%"any[]") align 8 %indirectarg37) #2 + unreachable + +panic41: ; preds = %checkok38 + store i64 %sub40, ptr %taddr42, align 8 + %38 = insertvalue %any undef, ptr %taddr42, 0 + %39 = insertvalue %any %38, i64 ptrtoint (ptr @"$ct.long" to i64), 1 + store i64 %10, ptr %taddr43, align 8 + %40 = insertvalue %any undef, ptr %taddr43, 0 + %41 = insertvalue %any %40, i64 ptrtoint (ptr @"$ct.long" to i64), 1 + store %any %39, ptr %varargslots44, align 16 + %ptradd45 = getelementptr inbounds i8, ptr %varargslots44, i64 16 + store %any %41, ptr %ptradd45, align 16 + %42 = insertvalue %"any[]" undef, ptr %varargslots44, 0 + %"$$temp46" = insertvalue %"any[]" %42, i64 2, 1 + store %"any[]" %"$$temp46", ptr %indirectarg47, align 8 + call void @std.core.builtin.panicf(ptr @.panic_msg.2, i64 60, ptr @.file, i64 19, ptr @.func, i64 4, i32 7, ptr byval(%"any[]") align 8 %indirectarg47) #2 + unreachable +} +