authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-27 15:37:09-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-27 15:37:09-04:00
log052079c99455d01312d377d72fa1b8b5c0b22aad
tree173d2502fca52124fc6ab1616392547da6679e09
parent0501962b4c01ee511aa0ae4b41cca4af7209bd8e
parent3c918184385f9ffc80693fb2683b4a9b574f9b66
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11302 from mitchellh/slice-null-cptr

stage2: runtime safety checks for slicing a null C pointer and @intCast truncating bits

7 files changed, 74 insertions(+), 3 deletions(-)

src/Sema.zig+59-3
...@@ -6781,14 +6781,54 @@ fn intCast(...@@ -6781,14 +6781,54 @@ fn intCast(
6781 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});6781 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});
6782 }6782 }
67836783
6784 // TODO insert safety check to make sure the value fits in the dest type
6785 _ = runtime_safety;
6786
6787 if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| {6784 if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| {
6785 // requirement: intCast(u0, input) iff input == 0
6786 if (runtime_safety and block.wantSafety()) {
6787 try sema.requireRuntimeBlock(block, operand_src);
6788 const target = sema.mod.getTarget();
6789 const wanted_info = dest_ty.intInfo(target);
6790 const wanted_bits = wanted_info.bits;
6791
6792 if (wanted_bits == 0) {
6793 const zero_inst = try sema.addConstant(sema.typeOf(operand), Value.zero);
6794 const is_in_range = try block.addBinOp(.cmp_eq, operand, zero_inst);
6795 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
6796 }
6797 }
6798
6788 return sema.addConstant(dest_ty, opv);6799 return sema.addConstant(dest_ty, opv);
6789 }6800 }
67906801
6791 try sema.requireRuntimeBlock(block, operand_src);6802 try sema.requireRuntimeBlock(block, operand_src);
6803 if (runtime_safety and block.wantSafety()) {
6804 const target = sema.mod.getTarget();
6805 const operand_ty = sema.typeOf(operand);
6806 const actual_info = operand_ty.intInfo(target);
6807 const wanted_info = dest_ty.intInfo(target);
6808 const actual_bits = actual_info.bits;
6809 const wanted_bits = wanted_info.bits;
6810
6811 // requirement: signed to unsigned >= 0
6812 if (actual_info.signedness == .signed and
6813 wanted_info.signedness == .unsigned)
6814 {
6815 const zero_inst = try sema.addConstant(sema.typeOf(operand), Value.zero);
6816 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst);
6817 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
6818 }
6819
6820 // requirement: unsigned int value fits into target type
6821 if (actual_bits > wanted_bits or
6822 (actual_bits == wanted_bits and
6823 actual_info.signedness == .unsigned and
6824 wanted_info.signedness == .signed))
6825 {
6826 const max_int = try dest_ty.maxInt(sema.arena, target);
6827 const max_int_inst = try sema.addConstant(operand_ty, max_int);
6828 const is_in_range = try block.addBinOp(.cmp_lte, operand, max_int_inst);
6829 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
6830 }
6831 }
6792 return block.addTyOp(.intcast, dest_ty, operand);6832 return block.addTyOp(.intcast, dest_ty, operand);
6793}6833}
67946834
...@@ -16166,6 +16206,7 @@ pub const PanicId = enum {...@@ -16166,6 +16206,7 @@ pub const PanicId = enum {
16166 incorrect_alignment,16206 incorrect_alignment,
16167 invalid_error_code,16207 invalid_error_code,
16168 index_out_of_bounds,16208 index_out_of_bounds,
16209 cast_truncated_data,
16169};16210};
1617016211
16171fn addSafetyCheck(16212fn addSafetyCheck(
...@@ -16288,6 +16329,7 @@ fn safetyPanic(...@@ -16288,6 +16329,7 @@ fn safetyPanic(
16288 .incorrect_alignment => "incorrect alignment",16329 .incorrect_alignment => "incorrect alignment",
16289 .invalid_error_code => "invalid error code",16330 .invalid_error_code => "invalid error code",
16290 .index_out_of_bounds => "attempt to index out of bounds",16331 .index_out_of_bounds => "attempt to index out of bounds",
16332 .cast_truncated_data => "integer cast truncated bits",
16291 };16333 };
1629216334
16293 const msg_inst = msg_inst: {16335 const msg_inst = msg_inst: {
...@@ -19964,6 +20006,14 @@ fn analyzeSlice(...@@ -19964,6 +20006,14 @@ fn analyzeSlice(
19964 slice_ty = ptr_ptr_child_ty;20006 slice_ty = ptr_ptr_child_ty;
19965 array_ty = ptr_ptr_child_ty;20007 array_ty = ptr_ptr_child_ty;
19966 elem_ty = ptr_ptr_child_ty.childType();20008 elem_ty = ptr_ptr_child_ty.childType();
20009
20010 if (ptr_ptr_child_ty.ptrSize() == .C) {
20011 if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| {
20012 if (ptr_val.isNull()) {
20013 return sema.fail(block, ptr_src, "slice of null pointer", .{});
20014 }
20015 }
20016 }
19967 },20017 },
19968 .Slice => {20018 .Slice => {
19969 ptr_sentinel = ptr_ptr_child_ty.sentinel();20019 ptr_sentinel = ptr_ptr_child_ty.sentinel();
...@@ -20162,6 +20212,12 @@ fn analyzeSlice(...@@ -20162,6 +20212,12 @@ fn analyzeSlice(
2016220212
20163 try sema.requireRuntimeBlock(block, src);20213 try sema.requireRuntimeBlock(block, src);
20164 if (block.wantSafety()) {20214 if (block.wantSafety()) {
20215 // requirement: slicing C ptr is non-null
20216 if (ptr_ptr_child_ty.isCPtr()) {
20217 const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
20218 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
20219 }
20220
20165 // requirement: end <= len20221 // requirement: end <= len
20166 const opt_len_inst = if (array_ty.zigTypeTag() == .Array)20222 const opt_len_inst = if (array_ty.zigTypeTag() == .Array)
20167 try sema.addIntUnsigned(Type.usize, array_ty.arrayLenIncludingSentinel())20223 try sema.addIntUnsigned(Type.usize, array_ty.arrayLenIncludingSentinel())
test/behavior/eval.zig+1
...@@ -443,6 +443,7 @@ fn copyWithPartialInline(s: []u32, b: []u8) void {...@@ -443,6 +443,7 @@ fn copyWithPartialInline(s: []u32, b: []u8) void {
443test "binary math operator in partially inlined function" {443test "binary math operator in partially inlined function" {
444 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO444 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
445 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO445 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
446 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
446447
447 var s: [4]u32 = undefined;448 var s: [4]u32 = undefined;
448 var b: [16]u8 = undefined;449 var b: [16]u8 = undefined;
test/behavior/fn.zig+1
...@@ -315,6 +315,7 @@ test "function pointers" {...@@ -315,6 +315,7 @@ test "function pointers" {
315 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO315 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
316 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO316 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
317 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO317 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
318 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
318319
319 const fns = [_]*const @TypeOf(fn1){320 const fns = [_]*const @TypeOf(fn1){
320 &fn1,321 &fn1,
test/behavior/for.zig+1
...@@ -69,6 +69,7 @@ test "basic for loop" {...@@ -69,6 +69,7 @@ test "basic for loop" {
69 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;69 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
70 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;70 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
71 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;71 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
72 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
7273
73 const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3;74 const expected_result = [_]u8{ 9, 8, 7, 6, 0, 1, 2, 3 } ** 3;
7475
test/behavior/int128.zig+1
...@@ -46,6 +46,7 @@ test "int128" {...@@ -46,6 +46,7 @@ test "int128" {
46 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO46 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
47 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO47 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
48 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO48 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
49 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
4950
50 var buff: i128 = -1;51 var buff: i128 = -1;
51 try expect(buff < 0 and (buff + 1) == 0);52 try expect(buff < 0 and (buff + 1) == 0);
test/behavior/slice.zig+1
...@@ -233,6 +233,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 {...@@ -233,6 +233,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 {
233233
234test "C pointer" {234test "C pointer" {
235 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;235 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
236 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
236237
237 var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf";238 var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf";
238 var len: u32 = 10;239 var len: u32 = 10;
test/compile_errors/stage2/slice_of_null_pointer.zig created+10
...@@ -0,0 +1,10 @@
1comptime {
2 var x: [*c]u8 = null;
3 var runtime_len: usize = 0;
4 var y = x[0..runtime_len];
5 _ = y;
6}
7
8// slice of null C pointer
9//
10// :4:14: error: slice of null pointer