Skip to content

Commit 59de248

Browse files
committed
runtime safety check for casting null to pointer
see #1059
1 parent d4d2718 commit 59de248

File tree

6 files changed

+67
-27
lines changed

6 files changed

+67
-27
lines changed

src/all_types.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,7 @@ enum PanicMsgId {
14881488
PanicMsgIdBadUnionField,
14891489
PanicMsgIdBadEnumValue,
14901490
PanicMsgIdFloatToInt,
1491+
PanicMsgIdPtrCastNull,
14911492

14921493
PanicMsgIdCount,
14931494
};
@@ -3001,12 +3002,14 @@ struct IrInstructionPtrCastSrc {
30013002

30023003
IrInstruction *dest_type;
30033004
IrInstruction *ptr;
3005+
bool safety_check_on;
30043006
};
30053007

30063008
struct IrInstructionPtrCastGen {
30073009
IrInstruction base;
30083010

30093011
IrInstruction *ptr;
3012+
bool safety_check_on;
30103013
};
30113014

30123015
struct IrInstructionBitCast {

src/analyze.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6902,3 +6902,12 @@ const char *container_string(ContainerKind kind) {
69026902
}
69036903
zig_unreachable();
69046904
}
6905+
6906+
bool ptr_allows_addr_zero(ZigType *ptr_type) {
6907+
if (ptr_type->id == ZigTypeIdPointer) {
6908+
return ptr_type->data.pointer.allow_zero;
6909+
} else if (ptr_type->id == ZigTypeIdOptional) {
6910+
return true;
6911+
}
6912+
return false;
6913+
}

src/analyze.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ void find_libc_lib_path(CodeGen *g);
4545

4646
bool type_has_bits(ZigType *type_entry);
4747
bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry);
48+
bool ptr_allows_addr_zero(ZigType *ptr_type);
4849

4950
ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code);
5051

src/codegen.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
950950
return buf_create_from_str("invalid enum value");
951951
case PanicMsgIdFloatToInt:
952952
return buf_create_from_str("integer part of floating point value out of bounds");
953+
case PanicMsgIdPtrCastNull:
954+
return buf_create_from_str("cast causes pointer to be null");
953955
}
954956
zig_unreachable();
955957
}
@@ -3028,7 +3030,22 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable,
30283030
return nullptr;
30293031
}
30303032
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
3031-
return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
3033+
LLVMValueRef result_ptr = LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
3034+
bool want_safety_check = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base);
3035+
if (!want_safety_check || ptr_allows_addr_zero(wanted_type))
3036+
return result_ptr;
3037+
3038+
LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(result_ptr));
3039+
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntNE, result_ptr, zero, "");
3040+
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastFail");
3041+
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastOk");
3042+
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
3043+
3044+
LLVMPositionBuilderAtEnd(g->builder, fail_block);
3045+
gen_safety_crash(g, PanicMsgIdPtrCastNull);
3046+
3047+
LLVMPositionBuilderAtEnd(g->builder, ok_block);
3048+
return result_ptr;
30323049
}
30333050

30343051
static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable,

src/ir.cpp

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
172172
static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
173173
ConstExprValue *out_val, ConstExprValue *ptr_val);
174174
static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr,
175-
ZigType *dest_type, IrInstruction *dest_type_src);
175+
ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on);
176176
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed);
177177
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs);
178178
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
@@ -2202,12 +2202,13 @@ static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNo
22022202
}
22032203

22042204
static IrInstruction *ir_build_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
2205-
IrInstruction *dest_type, IrInstruction *ptr)
2205+
IrInstruction *dest_type, IrInstruction *ptr, bool safety_check_on)
22062206
{
22072207
IrInstructionPtrCastSrc *instruction = ir_build_instruction<IrInstructionPtrCastSrc>(
22082208
irb, scope, source_node);
22092209
instruction->dest_type = dest_type;
22102210
instruction->ptr = ptr;
2211+
instruction->safety_check_on = safety_check_on;
22112212

22122213
ir_ref_instruction(dest_type, irb->current_basic_block);
22132214
ir_ref_instruction(ptr, irb->current_basic_block);
@@ -2216,12 +2217,13 @@ static IrInstruction *ir_build_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNod
22162217
}
22172218

22182219
static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction,
2219-
ZigType *ptr_type, IrInstruction *ptr)
2220+
ZigType *ptr_type, IrInstruction *ptr, bool safety_check_on)
22202221
{
22212222
IrInstructionPtrCastGen *instruction = ir_build_instruction<IrInstructionPtrCastGen>(
22222223
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
22232224
instruction->base.value.type = ptr_type;
22242225
instruction->ptr = ptr;
2226+
instruction->safety_check_on = safety_check_on;
22252227

22262228
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
22272229

@@ -4505,7 +4507,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
45054507
if (arg1_value == irb->codegen->invalid_instruction)
45064508
return arg1_value;
45074509

4508-
IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value);
4510+
IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value, true);
45094511
return ir_lval_wrap(irb, scope, ptr_cast, lval);
45104512
}
45114513
case BuiltinFnIdBitCast:
@@ -6740,7 +6742,8 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode
67406742
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
67416743

67426744
// TODO relies on Zig not re-ordering fields
6743-
IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst);
6745+
IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst,
6746+
false);
67446747
IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
67456748
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
67466749
IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
@@ -6818,7 +6821,8 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode
68186821
get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
68196822

68206823
// TODO relies on Zig not re-ordering fields
6821-
IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst);
6824+
IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst,
6825+
false);
68226826
IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
68236827
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
68246828
IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
@@ -7363,7 +7367,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
73637367

73647368
u8_ptr_type = ir_build_const_type(irb, coro_scope, node,
73657369
get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false));
7366-
IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr);
7370+
IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type,
7371+
coro_promise_ptr, false);
73677372
coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr);
73687373
coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
73697374
IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node);
@@ -7387,7 +7392,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
73877392
ir_build_return(irb, coro_scope, node, undef);
73887393

73897394
ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block);
7390-
IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr);
7395+
IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr,
7396+
false);
73917397
irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);
73927398

73937399
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
@@ -7465,9 +7471,10 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
74657471
get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8,
74667472
false, false, PtrLenUnknown, 0, 0, 0));
74677473
IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr);
7468-
IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, result_ptr);
7469-
IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
7470-
irb->exec->coro_result_field_ptr);
7474+
IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
7475+
result_ptr, false);
7476+
IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node,
7477+
u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr, false);
74717478
IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node,
74727479
fn_entry->type_entry->data.fn.fn_type_id.return_type);
74737480
IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst);
@@ -7517,7 +7524,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
75177524
IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node,
75187525
get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8,
75197526
false, false, PtrLenUnknown, 0, 0, 0));
7520-
IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe);
7527+
IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
7528+
coro_mem_ptr_maybe, false);
75217529
IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false);
75227530
IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var);
75237531
IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr);
@@ -8644,15 +8652,6 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
86448652
return err_set_type;
86458653
}
86468654

8647-
static bool ptr_allows_addr_zero(ZigType *ptr_type) {
8648-
if (ptr_type->id == ZigTypeIdPointer) {
8649-
return ptr_type->data.pointer.allow_zero;
8650-
} else if (ptr_type->id == ZigTypeIdOptional) {
8651-
return true;
8652-
}
8653-
return false;
8654-
}
8655-
86568655
static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted_type,
86578656
ZigType *actual_type, AstNode *source_node, bool wanted_is_mutable)
86588657
{
@@ -11310,7 +11309,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1131011309
actual_type->data.pointer.host_int_bytes == dest_ptr_type->data.pointer.host_int_bytes &&
1131111310
get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, dest_ptr_type))
1131211311
{
11313-
return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr);
11312+
return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr, true);
1131411313
}
1131511314
}
1131611315

@@ -11352,7 +11351,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1135211351
actual_type->data.pointer.child_type, source_node,
1135311352
!wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
1135411353
{
11355-
return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr);
11354+
return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr, true);
1135611355
}
1135711356

1135811357
// cast from integer to C pointer
@@ -20616,7 +20615,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
2061620615
}
2061720616

2061820617
static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr,
20619-
ZigType *dest_type, IrInstruction *dest_type_src)
20618+
ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on)
2062020619
{
2062120620
Error err;
2062220621

@@ -20685,7 +20684,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
2068520684
return ira->codegen->invalid_instruction;
2068620685
}
2068720686

20688-
IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr);
20687+
IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on);
2068920688

2069020689
if (type_has_bits(dest_type) && !type_has_bits(src_type)) {
2069120690
ErrorMsg *msg = ir_add_error(ira, source_instr,
@@ -20722,7 +20721,8 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct
2072220721
if (type_is_invalid(src_type))
2072320722
return ira->codegen->invalid_instruction;
2072420723

20725-
return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value);
20724+
return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value,
20725+
instruction->safety_check_on);
2072620726
}
2072720727

2072820728
static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *val, size_t len) {

test/runtime_safety.zig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
const tests = @import("tests.zig");
22

33
pub fn addCases(cases: *tests.CompareOutputContext) void {
4+
cases.addRuntimeSafety("pointer casting null to non-optional pointer",
5+
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
6+
\\ @import("std").os.exit(126);
7+
\\}
8+
\\pub fn main() void {
9+
\\ var c_ptr: [*c]u8 = 0;
10+
\\ var zig_ptr: *u8 = c_ptr;
11+
\\}
12+
);
13+
414
cases.addRuntimeSafety("@intToEnum - no matching tag value",
515
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
616
\\ @import("std").os.exit(126);

0 commit comments

Comments
 (0)