| author | |
| committer | |
| log | 8918cb06fca10309dc67ac881894528eac33a8fc |
| tree | e4c77bb22a05484b2843b8117e96d949204e81b0 |
| parent | 26f3c2d0614f4fb37752b1931cb0b43aed2696d2 |
| signature |
* add runtime safety for slicing pointers, arrays, and slices.
* slicing without a sentinel value results in non-sentineled slice
* improved `std.debug.panic` handling of panic-during-panic6 files changed, 117 insertions(+), 25 deletions(-)
lib/std/debug.zig+19-15| ... | @@ -219,7 +219,7 @@ pub fn panic(comptime format: []const u8, args: var) noreturn { | ... | @@ -219,7 +219,7 @@ pub fn panic(comptime format: []const u8, args: var) noreturn { |
| 219 | } | 219 | } |
| 220 | 220 | ||
| 221 | /// TODO multithreaded awareness | 221 | /// TODO multithreaded awareness |
| 222 | var panicking: u8 = 0; // TODO make this a bool | 222 | var panicking: u8 = 0; |
| 223 | 223 | ||
| 224 | pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn { | 224 | pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn { |
| 225 | @setCold(true); | 225 | @setCold(true); |
| ... | @@ -230,21 +230,25 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c | ... | @@ -230,21 +230,25 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c |
| 230 | resetSegfaultHandler(); | 230 | resetSegfaultHandler(); |
| 231 | } | 231 | } |
| 232 | 232 | ||
| 233 | if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { | 233 | switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) { |
| 234 | // Panicked during a panic. | 234 | 0 => { |
| 235 | 235 | const stderr = getStderrStream(); | |
| 236 | // TODO detect if a different thread caused the panic, because in that case | 236 | stderr.print(format ++ "\n", args) catch os.abort(); |
| 237 | // we would want to return here instead of calling abort, so that the thread | 237 | if (trace) |t| { |
| 238 | // which first called panic can finish printing a stack trace. | 238 | dumpStackTrace(t.*); |
| 239 | os.abort(); | 239 | } |
| 240 | } | 240 | dumpCurrentStackTrace(first_trace_addr); |
| 241 | const stderr = getStderrStream(); | 241 | }, |
| 242 | stderr.print(format ++ "\n", args) catch os.abort(); | 242 | 1 => { |
| 243 | if (trace) |t| { | 243 | // TODO detect if a different thread caused the panic, because in that case |
| 244 | dumpStackTrace(t.*); | 244 | // we would want to return here instead of calling abort, so that the thread |
| 245 | // which first called panic can finish printing a stack trace. | ||
| 246 | warn("Panicked during a panic. Aborting.\n", .{}); | ||
| 247 | }, | ||
| 248 | else => { | ||
| 249 | // Panicked while printing "Panicked during a panic." | ||
| 250 | }, | ||
| 245 | } | 251 | } |
| 246 | dumpCurrentStackTrace(first_trace_addr); | ||
| 247 | |||
| 248 | os.abort(); | 252 | os.abort(); |
| 249 | } | 253 | } |
| 250 | 254 |
lib/std/mem.zig+2-2| ... | @@ -364,11 +364,11 @@ pub fn len(comptime T: type, ptr: [*:0]const T) usize { | ... | @@ -364,11 +364,11 @@ pub fn len(comptime T: type, ptr: [*:0]const T) usize { |
| 364 | } | 364 | } |
| 365 | 365 | ||
| 366 | pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T { | 366 | pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T { |
| 367 | return ptr[0..len(T, ptr)]; | 367 | return ptr[0..len(T, ptr) :0]; |
| 368 | } | 368 | } |
| 369 | 369 | ||
| 370 | pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T { | 370 | pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T { |
| 371 | return ptr[0..len(T, ptr)]; | 371 | return ptr[0..len(T, ptr) :0]; |
| 372 | } | 372 | } |
| 373 | 373 | ||
| 374 | /// Returns true if all elements in a slice are equal to the scalar value provided | 374 | /// Returns true if all elements in a slice are equal to the scalar value provided |
src/all_types.hpp+1| ... | @@ -1779,6 +1779,7 @@ enum PanicMsgId { | ... | @@ -1779,6 +1779,7 @@ enum PanicMsgId { |
| 1779 | PanicMsgIdResumedFnPendingAwait, | 1779 | PanicMsgIdResumedFnPendingAwait, |
| 1780 | PanicMsgIdBadNoAsyncCall, | 1780 | PanicMsgIdBadNoAsyncCall, |
| 1781 | PanicMsgIdResumeNotSuspendedFn, | 1781 | PanicMsgIdResumeNotSuspendedFn, |
| 1782 | PanicMsgIdBadSentinel, | ||
| 1782 | 1783 | ||
| 1783 | PanicMsgIdCount, | 1784 | PanicMsgIdCount, |
| 1784 | }; | 1785 | }; |
src/codegen.cpp+43-3| ... | @@ -941,6 +941,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { | ... | @@ -941,6 +941,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 941 | return buf_create_from_str("async function called with noasync suspended"); | 941 | return buf_create_from_str("async function called with noasync suspended"); |
| 942 | case PanicMsgIdResumeNotSuspendedFn: | 942 | case PanicMsgIdResumeNotSuspendedFn: |
| 943 | return buf_create_from_str("resumed a non-suspended function"); | 943 | return buf_create_from_str("resumed a non-suspended function"); |
| 944 | case PanicMsgIdBadSentinel: | ||
| 945 | return buf_create_from_str("sentinel mismatch"); | ||
| 944 | } | 946 | } |
| 945 | zig_unreachable(); | 947 | zig_unreachable(); |
| 946 | } | 948 | } |
| ... | @@ -1419,6 +1421,22 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, | ... | @@ -1419,6 +1421,22 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, |
| 1419 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | 1421 | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 1420 | } | 1422 | } |
| 1421 | 1423 | ||
| 1424 | static void add_sentinel_check(CodeGen *g, LLVMValueRef sentinel_elem_ptr, ZigValue *sentinel) { | ||
| 1425 | LLVMValueRef expected_sentinel = gen_const_val(g, sentinel, ""); | ||
| 1426 | |||
| 1427 | LLVMValueRef actual_sentinel = gen_load_untyped(g, sentinel_elem_ptr, 0, false, ""); | ||
| 1428 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, actual_sentinel, expected_sentinel, ""); | ||
| 1429 | |||
| 1430 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SentinelFail"); | ||
| 1431 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SentinelOk"); | ||
| 1432 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | ||
| 1433 | |||
| 1434 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | ||
| 1435 | gen_safety_crash(g, PanicMsgIdBadSentinel); | ||
| 1436 | |||
| 1437 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | ||
| 1438 | } | ||
| 1439 | |||
| 1422 | static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) { | 1440 | static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) { |
| 1423 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type)); | 1441 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type)); |
| 1424 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, ""); | 1442 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, ""); |
| ... | @@ -5244,6 +5262,9 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst | ... | @@ -5244,6 +5262,9 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst |
| 5244 | 5262 | ||
| 5245 | bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); | 5263 | bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); |
| 5246 | 5264 | ||
| 5265 | ZigType *res_slice_ptr_type = instruction->base.value->type->data.structure.fields[slice_ptr_index]->type_entry; | ||
| 5266 | ZigValue *sentinel = res_slice_ptr_type->data.pointer.sentinel; | ||
| 5267 | |||
| 5247 | if (array_type->id == ZigTypeIdArray || | 5268 | if (array_type->id == ZigTypeIdArray || |
| 5248 | (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) | 5269 | (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) |
| 5249 | { | 5270 | { |
| ... | @@ -5265,6 +5286,15 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst | ... | @@ -5265,6 +5286,15 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst |
| 5265 | LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, | 5286 | LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, |
| 5266 | array_type->data.array.len, false); | 5287 | array_type->data.array.len, false); |
| 5267 | add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); | 5288 | add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); |
| 5289 | |||
| 5290 | if (sentinel != nullptr) { | ||
| 5291 | LLVMValueRef indices[] = { | ||
| 5292 | LLVMConstNull(g->builtin_types.entry_usize->llvm_type), | ||
| 5293 | end_val, | ||
| 5294 | }; | ||
| 5295 | LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); | ||
| 5296 | add_sentinel_check(g, sentinel_elem_ptr, sentinel); | ||
| 5297 | } | ||
| 5268 | } | 5298 | } |
| 5269 | } | 5299 | } |
| 5270 | if (!type_has_bits(array_type)) { | 5300 | if (!type_has_bits(array_type)) { |
| ... | @@ -5297,6 +5327,10 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst | ... | @@ -5297,6 +5327,10 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst |
| 5297 | 5327 | ||
| 5298 | if (want_runtime_safety) { | 5328 | if (want_runtime_safety) { |
| 5299 | add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); | 5329 | add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); |
| 5330 | if (sentinel != nullptr) { | ||
| 5331 | LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &end_val, 1, ""); | ||
| 5332 | add_sentinel_check(g, sentinel_elem_ptr, sentinel); | ||
| 5333 | } | ||
| 5300 | } | 5334 | } |
| 5301 | 5335 | ||
| 5302 | if (type_has_bits(array_type)) { | 5336 | if (type_has_bits(array_type)) { |
| ... | @@ -5337,18 +5371,24 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst | ... | @@ -5337,18 +5371,24 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst |
| 5337 | end_val = prev_end; | 5371 | end_val = prev_end; |
| 5338 | } | 5372 | } |
| 5339 | 5373 | ||
| 5374 | LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); | ||
| 5375 | LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); | ||
| 5376 | |||
| 5340 | if (want_runtime_safety) { | 5377 | if (want_runtime_safety) { |
| 5341 | assert(prev_end); | 5378 | assert(prev_end); |
| 5342 | add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); | 5379 | add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); |
| 5343 | if (instruction->end) { | 5380 | if (instruction->end) { |
| 5344 | add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end); | 5381 | add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end); |
| 5382 | |||
| 5383 | if (sentinel != nullptr) { | ||
| 5384 | LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, ""); | ||
| 5385 | add_sentinel_check(g, sentinel_elem_ptr, sentinel); | ||
| 5386 | } | ||
| 5345 | } | 5387 | } |
| 5346 | } | 5388 | } |
| 5347 | 5389 | ||
| 5348 | LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); | ||
| 5349 | LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); | ||
| 5350 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, ""); | 5390 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, ""); |
| 5351 | LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, (unsigned)len_index, ""); | 5391 | LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); |
| 5352 | gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); | 5392 | gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); |
| 5353 | 5393 | ||
| 5354 | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, ""); | 5394 | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, ""); |
src/ir.cpp+4-2| ... | @@ -25122,14 +25122,16 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction | ... | @@ -25122,14 +25122,16 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction |
| 25122 | if (array_type->data.pointer.ptr_len == PtrLenC) { | 25122 | if (array_type->data.pointer.ptr_len == PtrLenC) { |
| 25123 | array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown); | 25123 | array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown); |
| 25124 | } | 25124 | } |
| 25125 | non_sentinel_slice_ptr_type = array_type; | 25125 | ZigType *maybe_sentineled_slice_ptr_type = array_type; |
| 25126 | non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); | ||
| 25126 | if (!end) { | 25127 | if (!end) { |
| 25127 | ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); | 25128 | ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); |
| 25128 | return ira->codegen->invalid_instruction; | 25129 | return ira->codegen->invalid_instruction; |
| 25129 | } | 25130 | } |
| 25130 | } | 25131 | } |
| 25131 | } else if (is_slice(array_type)) { | 25132 | } else if (is_slice(array_type)) { |
| 25132 | non_sentinel_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; | 25133 | ZigType *maybe_sentineled_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; |
| 25134 | non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); | ||
| 25133 | elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type; | 25135 | elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type; |
| 25134 | } else { | 25136 | } else { |
| 25135 | ir_add_error(ira, &instruction->base, | 25137 | ir_add_error(ira, &instruction->base, |
test/runtime_safety.zig+48-3| ... | @@ -1,12 +1,57 @@ | ... | @@ -1,12 +1,57 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: *tests.CompareOutputContext) void { | 3 | pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 4 | cases.addRuntimeSafety("pointer slice sentinel mismatch", | ||
| 5 | \\const std = @import("std"); | ||
| 6 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | ||
| 7 | \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { | ||
| 8 | \\ std.process.exit(126); // good | ||
| 9 | \\ } | ||
| 10 | \\ std.process.exit(0); // test failed | ||
| 11 | \\} | ||
| 12 | \\pub fn main() void { | ||
| 13 | \\ var buf: [4]u8 = undefined; | ||
| 14 | \\ const ptr = buf[0..].ptr; | ||
| 15 | \\ const slice = ptr[0..3 :0]; | ||
| 16 | \\} | ||
| 17 | ); | ||
| 18 | |||
| 19 | cases.addRuntimeSafety("slice slice sentinel mismatch", | ||
| 20 | \\const std = @import("std"); | ||
| 21 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | ||
| 22 | \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { | ||
| 23 | \\ std.process.exit(126); // good | ||
| 24 | \\ } | ||
| 25 | \\ std.process.exit(0); // test failed | ||
| 26 | \\} | ||
| 27 | \\pub fn main() void { | ||
| 28 | \\ var buf: [4]u8 = undefined; | ||
| 29 | \\ const slice = buf[0..]; | ||
| 30 | \\ const slice2 = slice[0..3 :0]; | ||
| 31 | \\} | ||
| 32 | ); | ||
| 33 | |||
| 34 | cases.addRuntimeSafety("array slice sentinel mismatch", | ||
| 35 | \\const std = @import("std"); | ||
| 36 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | ||
| 37 | \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { | ||
| 38 | \\ std.process.exit(126); // good | ||
| 39 | \\ } | ||
| 40 | \\ std.process.exit(0); // test failed | ||
| 41 | \\} | ||
| 42 | \\pub fn main() void { | ||
| 43 | \\ var buf: [4]u8 = undefined; | ||
| 44 | \\ const slice = buf[0..3 :0]; | ||
| 45 | \\} | ||
| 46 | ); | ||
| 47 | |||
| 4 | cases.addRuntimeSafety("intToPtr with misaligned address", | 48 | cases.addRuntimeSafety("intToPtr with misaligned address", |
| 49 | \\const std = @import("std"); | ||
| 5 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | 50 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { |
| 6 | \\ if (@import("std").mem.eql(u8, message, "incorrect alignment")) { | 51 | \\ if (std.mem.eql(u8, message, "incorrect alignment")) { |
| 7 | \\ @import("std").os.exit(126); // good | 52 | \\ std.os.exit(126); // good |
| 8 | \\ } | 53 | \\ } |
| 9 | \\ @import("std").os.exit(0); // test failed | 54 | \\ std.os.exit(0); // test failed |
| 10 | \\} | 55 | \\} |
| 11 | \\pub fn main() void { | 56 | \\pub fn main() void { |
| 12 | \\ var x: usize = 5; | 57 | \\ var x: usize = 5; |