authorgravatar for alex_naskos@hotmail.comAlexandros Naskos <alex_naskos@hotmail.com> 2020-02-29 08:36:42+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-02-29 01:36:42-05:00
log1b41f2d77ea14de27b0655a35a727d664cfc4ca4
treed3e5996e16424fa205a0f8072670254a04147545
parenta5a53a182af3715edd9474fe94cad2223a6c7fb9
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

C pointer slices are no longer allowzero (#4462)

* Slices from C pointers are no longer allowzero but instead insert a runtime assertion. * Added a test, fixed code for cases with non-allowzero C pointers * Create new type when flipping allow_zero, sometimes we get a cached value back from adjust_ptr_len. * Added comments, changed panic message * Added runtime safety test.

4 files changed, 57 insertions(+), 4 deletions(-)

src/codegen.cpp+1-1
......@@ -973,7 +973,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
973973 case PanicMsgIdExactDivisionRemainder:
974974 return buf_create_from_str("exact division produced remainder");
975975 case PanicMsgIdUnwrapOptionalFail:
976 return buf_create_from_str("attempt to unwrap null");
976 return buf_create_from_str("attempt to use null value");
977977 case PanicMsgIdUnreachable:
978978 return buf_create_from_str("reached unreachable code");
979979 case PanicMsgIdInvalidErrorCode:
src/ir.cpp+32-3
......@@ -20412,6 +20412,17 @@ static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) {
2041220412 ptr_type->data.pointer.allow_zero);
2041320413}
2041420414
20415static ZigType *adjust_ptr_allow_zero(CodeGen *g, ZigType *ptr_type, bool allow_zero) {
20416 assert(ptr_type->id == ZigTypeIdPointer);
20417 return get_pointer_to_type_extra(g,
20418 ptr_type->data.pointer.child_type,
20419 ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
20420 ptr_type->data.pointer.ptr_len,
20421 ptr_type->data.pointer.explicit_alignment,
20422 ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes,
20423 allow_zero);
20424}
20425
2041520426static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemPtr *elem_ptr_instruction) {
2041620427 Error err;
2041720428 IrInstGen *array_ptr = elem_ptr_instruction->array_ptr->child;
......@@ -25966,6 +25977,8 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2596625977 ZigType *non_sentinel_slice_ptr_type;
2596725978 ZigType *elem_type;
2596825979
25980 bool generate_non_null_assert = false;
25981
2596925982 if (array_type->id == ZigTypeIdArray) {
2597025983 elem_type = array_type->data.array.child_type;
2597125984 bool is_comptime_const = ptr_ptr->value->special == ConstValSpecialStatic &&
......@@ -25993,6 +26006,14 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2599326006 elem_type = array_type->data.pointer.child_type;
2599426007 if (array_type->data.pointer.ptr_len == PtrLenC) {
2599526008 array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown);
26009
26010 // C pointers are allowzero by default.
26011 // However, we want to be able to slice them without generating an allowzero slice (see issue #4401).
26012 // To achieve this, we generate a runtime safety check and make the slice type non-allowzero.
26013 if (array_type->data.pointer.allow_zero) {
26014 array_type = adjust_ptr_allow_zero(ira->codegen, array_type, false);
26015 generate_non_null_assert = true;
26016 }
2599626017 }
2599726018 ZigType *maybe_sentineled_slice_ptr_type = array_type;
2599826019 non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr);
......@@ -26264,7 +26285,6 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2626426285
2626526286 IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc,
2626626287 return_type, nullptr, true, true);
26267
2626826288 if (result_loc != nullptr) {
2626926289 if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) {
2627026290 return result_loc;
......@@ -26277,8 +26297,17 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2627726297 return ira->codegen->invalid_inst_gen;
2627826298 }
2627926299
26280 return ir_build_slice_gen(ira, &instruction->base.base, return_type,
26281 ptr_ptr, casted_start, end, instruction->safety_check_on, result_loc);
26300 if (generate_non_null_assert) {
26301 IrInstGen *ptr_val = ir_get_deref(ira, &instruction->base.base, ptr_ptr, nullptr);
26302
26303 if (type_is_invalid(ptr_val->value->type))
26304 return ira->codegen->invalid_inst_gen;
26305
26306 ir_build_assert_non_null(ira, &instruction->base.base, ptr_val);
26307 }
26308
26309 return ir_build_slice_gen(ira, &instruction->base.base, return_type, ptr_ptr,
26310 casted_start, end, instruction->safety_check_on, result_loc);
2628226311}
2628326312
2628426313static IrInstGen *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstSrcHasField *instruction) {
test/runtime_safety.zig+13
......@@ -745,4 +745,17 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
745745 \\ (await p) catch unreachable;
746746 \\}
747747 );
748
749 // Slicing a C pointer returns a non-allowzero slice, thus we need to emit
750 // a safety check to ensure the pointer is not null.
751 cases.addRuntimeSafety("slicing null C pointer",
752 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
753 \\ @import("std").os.exit(126);
754 \\}
755 \\
756 \\pub fn main() void {
757 \\ var ptr: [*c]const u32 = null;
758 \\ var slice = ptr[0..3];
759 \\}
760 );
748761}
test/stage1/behavior/slice.zig+11
......@@ -43,6 +43,17 @@ test "C pointer" {
4343 expectEqualSlices(u8, "kjdhfkjdhf", slice);
4444}
4545
46test "C pointer slice access" {
47 var buf: [10]u32 = [1]u32{42} ** 10;
48 const c_ptr = @ptrCast([*c]const u32, &buf);
49
50 comptime expectEqual([]const u32, @TypeOf(c_ptr[0..1]));
51
52 for (c_ptr[0..5]) |*cl| {
53 expectEqual(@as(u32, 42), cl.*);
54 }
55}
56
4657fn sliceSum(comptime q: []const u8) i32 {
4758 comptime var result = 0;
4859 inline for (q) |item| {