| 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 | 219 | } |
| 220 | 220 | |
| 221 | 221 | /// TODO multithreaded awareness |
| 222 | var panicking: u8 = 0; // TODO make this a bool | |
| 222 | var panicking: u8 = 0; | |
| 223 | 223 | |
| 224 | 224 | pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn { |
| 225 | 225 | @setCold(true); |
| ... | ... | @@ -230,21 +230,25 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c |
| 230 | 230 | resetSegfaultHandler(); |
| 231 | 231 | } |
| 232 | 232 | |
| 233 | if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { | |
| 234 | // Panicked during a panic. | |
| 235 | ||
| 236 | // TODO detect if a different thread caused the panic, because in that case | |
| 237 | // we would want to return here instead of calling abort, so that the thread | |
| 238 | // which first called panic can finish printing a stack trace. | |
| 239 | os.abort(); | |
| 240 | } | |
| 241 | const stderr = getStderrStream(); | |
| 242 | stderr.print(format ++ "\n", args) catch os.abort(); | |
| 243 | if (trace) |t| { | |
| 244 | dumpStackTrace(t.*); | |
| 233 | switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) { | |
| 234 | 0 => { | |
| 235 | const stderr = getStderrStream(); | |
| 236 | stderr.print(format ++ "\n", args) catch os.abort(); | |
| 237 | if (trace) |t| { | |
| 238 | dumpStackTrace(t.*); | |
| 239 | } | |
| 240 | dumpCurrentStackTrace(first_trace_addr); | |
| 241 | }, | |
| 242 | 1 => { | |
| 243 | // TODO detect if a different thread caused the panic, because in that case | |
| 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 | 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 | 364 | } |
| 365 | 365 | |
| 366 | 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 | 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 | 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 | 1779 | PanicMsgIdResumedFnPendingAwait, |
| 1780 | 1780 | PanicMsgIdBadNoAsyncCall, |
| 1781 | 1781 | PanicMsgIdResumeNotSuspendedFn, |
| 1782 | PanicMsgIdBadSentinel, | |
| 1782 | 1783 | |
| 1783 | 1784 | PanicMsgIdCount, |
| 1784 | 1785 | }; |
src/codegen.cpp+43-3| ... | ... | @@ -941,6 +941,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 941 | 941 | return buf_create_from_str("async function called with noasync suspended"); |
| 942 | 942 | case PanicMsgIdResumeNotSuspendedFn: |
| 943 | 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 | 947 | zig_unreachable(); |
| 946 | 948 | } |
| ... | ... | @@ -1419,6 +1421,22 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, |
| 1419 | 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 | 1440 | static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) { |
| 1423 | 1441 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type)); |
| 1424 | 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 | 5262 | |
| 5245 | 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 | 5268 | if (array_type->id == ZigTypeIdArray || |
| 5248 | 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 | 5286 | LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, |
| 5266 | 5287 | array_type->data.array.len, false); |
| 5267 | 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 | 5300 | if (!type_has_bits(array_type)) { |
| ... | ... | @@ -5297,6 +5327,10 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst |
| 5297 | 5327 | |
| 5298 | 5328 | if (want_runtime_safety) { |
| 5299 | 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 | 5336 | if (type_has_bits(array_type)) { |
| ... | ... | @@ -5337,18 +5371,24 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst |
| 5337 | 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 | 5377 | if (want_runtime_safety) { |
| 5341 | 5378 | assert(prev_end); |
| 5342 | 5379 | add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); |
| 5343 | 5380 | if (instruction->end) { |
| 5344 | 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 | 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 | 5392 | gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); |
| 5353 | 5393 | |
| 5354 | 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 | 25122 | if (array_type->data.pointer.ptr_len == PtrLenC) { |
| 25123 | 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 | 25127 | if (!end) { |
| 25127 | 25128 | ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); |
| 25128 | 25129 | return ira->codegen->invalid_instruction; |
| 25129 | 25130 | } |
| 25130 | 25131 | } |
| 25131 | 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 | 25135 | elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type; |
| 25134 | 25136 | } else { |
| 25135 | 25137 | ir_add_error(ira, &instruction->base, |
test/runtime_safety.zig+48-3| ... | ... | @@ -1,12 +1,57 @@ |
| 1 | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 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 | 48 | cases.addRuntimeSafety("intToPtr with misaligned address", |
| 49 | \\const std = @import("std"); | |
| 5 | 50 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { |
| 6 | \\ if (@import("std").mem.eql(u8, message, "incorrect alignment")) { | |
| 7 | \\ @import("std").os.exit(126); // good | |
| 51 | \\ if (std.mem.eql(u8, message, "incorrect alignment")) { | |
| 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 | 56 | \\pub fn main() void { |
| 12 | 57 | \\ var x: usize = 5; |