diff --git a/lib/std/debug.zig b/lib/std/debug.zig index ed327fc6de..5a8f556517 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1213,7 +1213,7 @@ pub const DebugInfo = struct { const obj_di = try self.allocator.create(ModuleDebugInfo); errdefer self.allocator.destroy(obj_di); - obj_di.* = openCoffDebugInfo(self.allocator, name_buffer[0..:0]) catch |err| switch (err) { + obj_di.* = openCoffDebugInfo(self.allocator, name_buffer[0 .. len + 4 :0]) catch |err| switch (err) { error.FileNotFound => return error.MissingDebugInfo, else => return err, }; diff --git a/lib/std/fs.zig b/lib/std/fs.zig index feb74be2c8..1c7472c3f6 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1522,7 +1522,7 @@ pub fn openSelfExe() OpenSelfExeError!File { var buf: [MAX_PATH_BYTES]u8 = undefined; const self_exe_path = try selfExePath(&buf); buf[self_exe_path.len] = 0; - return openFileAbsoluteZ(self_exe_path[0..self_exe_path.len :0].ptr, .{}); + return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, .{}); } test "openSelfExe" { diff --git a/src/codegen.cpp b/src/codegen.cpp index 980a8a4aca..5214d49a30 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5498,6 +5498,8 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutableGen *executable, Ir } static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrInstGenSlice *instruction) { + Error err; + LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr); ZigType *array_ptr_type = instruction->ptr->value->type; assert(array_ptr_type->id == ZigTypeIdPointer); @@ -5506,15 +5508,16 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); + // The result is either a slice or a pointer to an array ZigType *result_type = instruction->base.value->type; - if (!type_has_bits(g, result_type)) { - return nullptr; - } // This is not whether the result type has a sentinel, but whether there should be a sentinel check, // e.g. if they used [a..b :s] syntax. ZigValue *sentinel = instruction->sentinel; + LLVMValueRef slice_start_ptr = nullptr; + LLVMValueRef len_value = nullptr; + if (array_type->id == ZigTypeIdArray || (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) { @@ -5528,111 +5531,86 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI } else { end_val = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, array_type->data.array.len, false); } + if (want_runtime_safety) { + // Safety check: start <= end if (instruction->start->value->special == ConstValSpecialRuntime || instruction->end) { add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); } - if (instruction->end) { - LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, - array_type->data.array.len, false); - add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); - if (sentinel != nullptr) { - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->llvm_type), - end_val, - }; - LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); - add_sentinel_check(g, sentinel_elem_ptr, sentinel); - } + // Safety check: the last element of the slice (the sentinel if + // requested) must be inside the array + // XXX: Overflow is not checked here... + const size_t full_len = array_type->data.array.len + + (array_type->data.array.sentinel != nullptr); + LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, + full_len, false); + + LLVMValueRef check_end_val = end_val; + if (sentinel != nullptr) { + LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); + check_end_val = LLVMBuildNUWAdd(g->builder, end_val, usize_one, ""); } - } - if (!type_has_bits(g, array_type)) { - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); - - // TODO if runtime safety is on, store 0xaaaaaaa in ptr field - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); - return tmp_struct_ptr; + add_bounds_check(g, check_end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); } - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->llvm_type), - start_val, - }; - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); - if (result_type->id == ZigTypeIdPointer) { - ir_assert(instruction->result_loc == nullptr, &instruction->base); - LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); - return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); - } else { - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + bool value_has_bits; + if ((err = type_has_bits2(g, array_type, &value_has_bits))) + codegen_report_errors_and_exit(g); - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); + if (value_has_bits) { + if (want_runtime_safety && sentinel != nullptr) { + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->llvm_type), + end_val, + }; + LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); + add_sentinel_check(g, sentinel_elem_ptr, sentinel); + } - return tmp_struct_ptr; + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->llvm_type), + start_val, + }; + slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); } + + len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); } else if (array_type->id == ZigTypeIdPointer) { assert(array_type->data.pointer.ptr_len != PtrLenSingle); LLVMValueRef start_val = ir_llvm_value(g, instruction->start); LLVMValueRef end_val = ir_llvm_value(g, instruction->end); if (want_runtime_safety) { + // Safety check: start <= end add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); - if (sentinel != nullptr) { + } + + bool value_has_bits; + if ((err = type_has_bits2(g, array_type, &value_has_bits))) + codegen_report_errors_and_exit(g); + + if (value_has_bits) { + if (want_runtime_safety && sentinel != nullptr) { LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &end_val, 1, ""); add_sentinel_check(g, sentinel_elem_ptr, sentinel); } + + slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); } - if (!type_has_bits(g, array_type)) { - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); - return tmp_struct_ptr; - } - - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); - if (result_type->id == ZigTypeIdPointer) { - ir_assert(instruction->result_loc == nullptr, &instruction->base); - LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); - return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); - } - - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - - size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - - size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); - - return tmp_struct_ptr; - + len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); } else if (array_type->id == ZigTypeIdStruct) { assert(array_type->data.structure.special == StructSpecialSlice); assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); - size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; - assert(ptr_index != SIZE_MAX); - size_t len_index = array_type->data.structure.fields[slice_len_index]->gen_index; - assert(len_index != SIZE_MAX); + const size_t gen_len_index = array_type->data.structure.fields[slice_len_index]->gen_index; + assert(gen_len_index != SIZE_MAX); LLVMValueRef prev_end = nullptr; if (!instruction->end || want_runtime_safety) { - LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, ""); + LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, gen_len_index, ""); prev_end = gen_load_untyped(g, src_len_ptr, 0, false, ""); } @@ -5644,41 +5622,104 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI end_val = prev_end; } - LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); - LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); + ZigType *ptr_field_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; if (want_runtime_safety) { assert(prev_end); + // Safety check: start <= end add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); - if (instruction->end) { - add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end); - if (sentinel != nullptr) { - LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, ""); - add_sentinel_check(g, sentinel_elem_ptr, sentinel); - } + // Safety check: the sentinel counts as one more element + // XXX: Overflow is not checked here... + LLVMValueRef check_prev_end = prev_end; + if (ptr_field_type->data.pointer.sentinel != nullptr) { + LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); + check_prev_end = LLVMBuildNUWAdd(g->builder, prev_end, usize_one, ""); } + LLVMValueRef check_end_val = end_val; + if (sentinel != nullptr) { + LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); + check_end_val = LLVMBuildNUWAdd(g->builder, end_val, usize_one, ""); + } + + add_bounds_check(g, check_end_val, LLVMIntEQ, nullptr, LLVMIntULE, check_prev_end); } - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); - if (result_type->id == ZigTypeIdPointer) { - ir_assert(instruction->result_loc == nullptr, &instruction->base); - LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); - return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); - } else { - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + bool ptr_has_bits; + if ((err = type_has_bits2(g, ptr_field_type, &ptr_has_bits))) + codegen_report_errors_and_exit(g); - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); + if (ptr_has_bits) { + const size_t gen_ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; + assert(gen_ptr_index != SIZE_MAX); - return tmp_struct_ptr; + LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, gen_ptr_index, ""); + LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); + + if (sentinel != nullptr) { + LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, ""); + add_sentinel_check(g, sentinel_elem_ptr, sentinel); + } + + slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); } + + len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); } else { zig_unreachable(); } + + bool result_has_bits; + if ((err = type_has_bits2(g, result_type, &result_has_bits))) + codegen_report_errors_and_exit(g); + + // Nothing to do, we're only interested in the bound checks emitted above + if (!result_has_bits) + return nullptr; + + // The starting pointer for the slice may be null in case of zero-sized + // arrays, the length value is always defined. + assert(len_value != nullptr); + + // The slice decays into a pointer to an array, the size is tracked in the + // type itself + if (result_type->id == ZigTypeIdPointer) { + ir_assert(instruction->result_loc == nullptr, &instruction->base); + LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); + + if (slice_start_ptr != nullptr) { + return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); + } + + return LLVMGetUndef(result_ptr_type); + } + + ir_assert(instruction->result_loc != nullptr, &instruction->base); + // Create a new slice + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + + ZigType *slice_ptr_type = result_type->data.structure.fields[slice_ptr_index]->type_entry; + + // The slice may not have a pointer at all if it points to a zero-sized type + const size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; + if (gen_ptr_index != SIZE_MAX) { + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); + if (slice_start_ptr != nullptr) { + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + } else if (want_runtime_safety) { + gen_undef_init(g, slice_ptr_type->abi_align, slice_ptr_type, ptr_field_ptr); + } else { + gen_store_untyped(g, LLVMGetUndef(get_llvm_type(g, slice_ptr_type)), ptr_field_ptr, 0, false); + } + } + + const size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; + assert(gen_len_index != SIZE_MAX); + + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + + return tmp_struct_ptr; } static LLVMValueRef get_trap_fn_val(CodeGen *g) { diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index f0c83049da..6bbc7dc19d 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -1,6 +1,75 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { + { + const check_panic_msg = + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ if (std.mem.eql(u8, message, "index out of bounds")) { + \\ std.process.exit(126); // good + \\ } + \\ std.process.exit(0); // test failed + \\} + ; + + cases.addRuntimeSafety("slicing operator with sentinel", + \\const std = @import("std"); + ++ check_panic_msg ++ + \\pub fn main() void { + \\ var buf = [4]u8{'a','b','c',0}; + \\ const slice = buf[0..4 :0]; + \\} + ); + cases.addRuntimeSafety("slicing operator with sentinel", + \\const std = @import("std"); + ++ check_panic_msg ++ + \\pub fn main() void { + \\ var buf = [4]u8{'a','b','c',0}; + \\ const slice = buf[0..:0]; + \\} + ); + cases.addRuntimeSafety("slicing operator with sentinel", + \\const std = @import("std"); + ++ check_panic_msg ++ + \\pub fn main() void { + \\ var buf_zero = [0]u8{}; + \\ const slice = buf_zero[0..0 :0]; + \\} + ); + cases.addRuntimeSafety("slicing operator with sentinel", + \\const std = @import("std"); + ++ check_panic_msg ++ + \\pub fn main() void { + \\ var buf_zero = [0]u8{}; + \\ const slice = buf_zero[0..:0]; + \\} + ); + cases.addRuntimeSafety("slicing operator with sentinel", + \\const std = @import("std"); + ++ check_panic_msg ++ + \\pub fn main() void { + \\ var buf_sentinel = [2:0]u8{'a','b'}; + \\ @ptrCast(*[3]u8, &buf_sentinel)[2] = 0; + \\ const slice = buf_sentinel[0..3 :0]; + \\} + ); + cases.addRuntimeSafety("slicing operator with sentinel", + \\const std = @import("std"); + ++ check_panic_msg ++ + \\pub fn main() void { + \\ var buf_slice: []u8 = &[3]u8{ 'a', 'b', 0 }; + \\ const slice = buf_slice[0..3 :0]; + \\} + ); + cases.addRuntimeSafety("slicing operator with sentinel", + \\const std = @import("std"); + ++ check_panic_msg ++ + \\pub fn main() void { + \\ var buf_slice: []u8 = &[3]u8{ 'a', 'b', 0 }; + \\ const slice = buf_slice[0.. :0]; + \\} + ); + } + cases.addRuntimeSafety("shift left by huge amount", \\const std = @import("std"); \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {