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
signature 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(
67816781 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});
67826782 }
67836783
6784 // TODO insert safety check to make sure the value fits in the dest type
6785 _ = runtime_safety;
6786
67876784 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
67886799 return sema.addConstant(dest_ty, opv);
67896800 }
67906801
67916802 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 }
67926832 return block.addTyOp(.intcast, dest_ty, operand);
67936833}
67946834
......@@ -16166,6 +16206,7 @@ pub const PanicId = enum {
1616616206 incorrect_alignment,
1616716207 invalid_error_code,
1616816208 index_out_of_bounds,
16209 cast_truncated_data,
1616916210};
1617016211
1617116212fn addSafetyCheck(
......@@ -16288,6 +16329,7 @@ fn safetyPanic(
1628816329 .incorrect_alignment => "incorrect alignment",
1628916330 .invalid_error_code => "invalid error code",
1629016331 .index_out_of_bounds => "attempt to index out of bounds",
16332 .cast_truncated_data => "integer cast truncated bits",
1629116333 };
1629216334
1629316335 const msg_inst = msg_inst: {
......@@ -19964,6 +20006,14 @@ fn analyzeSlice(
1996420006 slice_ty = ptr_ptr_child_ty;
1996520007 array_ty = ptr_ptr_child_ty;
1996620008 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 }
1996720017 },
1996820018 .Slice => {
1996920019 ptr_sentinel = ptr_ptr_child_ty.sentinel();
......@@ -20162,6 +20212,12 @@ fn analyzeSlice(
2016220212
2016320213 try sema.requireRuntimeBlock(block, src);
2016420214 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
2016520221 // requirement: end <= len
2016620222 const opt_len_inst = if (array_ty.zigTypeTag() == .Array)
2016720223 try sema.addIntUnsigned(Type.usize, array_ty.arrayLenIncludingSentinel())
test/behavior/eval.zig+1
......@@ -443,6 +443,7 @@ fn copyWithPartialInline(s: []u32, b: []u8) void {
443443test "binary math operator in partially inlined function" {
444444 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
445445 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
446 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
446447
447448 var s: [4]u32 = undefined;
448449 var b: [16]u8 = undefined;
test/behavior/fn.zig+1
......@@ -315,6 +315,7 @@ test "function pointers" {
315315 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
316316 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
317317 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
318 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
318319
319320 const fns = [_]*const @TypeOf(fn1){
320321 &fn1,
test/behavior/for.zig+1
......@@ -69,6 +69,7 @@ test "basic for loop" {
6969 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
7070 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7171 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
72 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
7273
7374 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" {
4646 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
4747 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
4848 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
49 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
4950
5051 var buff: i128 = -1;
5152 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 {
233233
234234test "C pointer" {
235235 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
236 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
236237
237238 var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf";
238239 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