From 25198810c88d474c63ff537e3647f12e7df6297c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 21 Jul 2024 23:50:50 -0700 Subject: [PATCH] add new builtin: `@disableInstrumentation` This is needed to ensure that start code does not try to access thread local storage before it has set up thread local storage. --- lib/std/zig/AstGen.zig | 14 ++++++++------ lib/std/zig/AstRlAnnotate.zig | 1 + lib/std/zig/BuiltinFn.zig | 9 +++++++++ lib/std/zig/Zir.zig | 4 +++- src/InternPool.zig | 20 ++++++++++++++++++-- src/Sema.zig | 13 +++++++++++++ src/codegen/llvm.zig | 25 +++++++++++++++++++------ src/print_zir.zig | 1 + 8 files changed, 72 insertions(+), 15 deletions(-) diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index aa61714434..a6be743c2b 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -2817,6 +2817,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) { .breakpoint, + .disable_instrumentation, .fence, .set_float_mode, .set_align_stack, @@ -9305,12 +9306,13 @@ fn builtinCall( }, // zig fmt: off - .This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node), - .return_address => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr, node), node), - .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node), - .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node), - .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node), - .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node), + .This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node), + .return_address => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr, node), node), + .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node), + .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node), + .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node), + .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node), + .disable_instrumentation => return rvalue(gz, ri, try gz.addNodeExtended(.disable_instrumentation, node), node), .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info), .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of), diff --git a/lib/std/zig/AstRlAnnotate.zig b/lib/std/zig/AstRlAnnotate.zig index 543c279981..e956ffd2a9 100644 --- a/lib/std/zig/AstRlAnnotate.zig +++ b/lib/std/zig/AstRlAnnotate.zig @@ -877,6 +877,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. .error_return_trace, .frame, .breakpoint, + .disable_instrumentation, .in_comptime, .panic, .trap, diff --git a/lib/std/zig/BuiltinFn.zig b/lib/std/zig/BuiltinFn.zig index 4bea0278fa..fc08f9eb85 100644 --- a/lib/std/zig/BuiltinFn.zig +++ b/lib/std/zig/BuiltinFn.zig @@ -15,6 +15,7 @@ pub const Tag = enum { int_from_bool, bit_size_of, breakpoint, + disable_instrumentation, mul_add, byte_swap, bit_reverse, @@ -263,6 +264,14 @@ pub const list = list: { .illegal_outside_function = true, }, }, + .{ + "@disableInstrumentation", + .{ + .tag = .disable_instrumentation, + .param_count = 0, + .illegal_outside_function = true, + }, + }, .{ "@mulAdd", .{ diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 128e91eef4..051a799db6 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -1553,7 +1553,7 @@ pub const Inst = struct { => false, .extended => switch (data.extended.opcode) { - .fence, .set_cold, .breakpoint => true, + .fence, .set_cold, .breakpoint, .disable_instrumentation => true, else => false, }, }; @@ -1973,6 +1973,8 @@ pub const Inst = struct { /// Implements `@breakpoint`. /// `operand` is `src_node: i32`. breakpoint, + /// Implement builtin `@disableInstrumentation`. `operand` is `src_node: i32`. + disable_instrumentation, /// Implements the `@select` builtin. /// `operand` is payload index to `Select`. select, diff --git a/src/InternPool.zig b/src/InternPool.zig index c3cee5852b..2934340034 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -5184,11 +5184,11 @@ pub const FuncAnalysis = packed struct(u32) { is_noinline: bool, calls_or_awaits_errorable_fn: bool, stack_alignment: Alignment, - /// True if this function has an inferred error set. inferred_error_set: bool, + disable_instrumentation: bool, - _: u14 = 0, + _: u13 = 0, pub const State = enum(u8) { /// This function has not yet undergone analysis, because we have not @@ -8111,6 +8111,7 @@ pub fn getFuncDecl( .calls_or_awaits_errorable_fn = false, .stack_alignment = .none, .inferred_error_set = false, + .disable_instrumentation = false, }, .owner_decl = key.owner_decl, .ty = key.ty, @@ -8214,6 +8215,7 @@ pub fn getFuncDeclIes( .calls_or_awaits_errorable_fn = false, .stack_alignment = .none, .inferred_error_set = true, + .disable_instrumentation = false, }, .owner_decl = key.owner_decl, .ty = func_ty, @@ -8405,6 +8407,7 @@ pub fn getFuncInstance( .calls_or_awaits_errorable_fn = false, .stack_alignment = .none, .inferred_error_set = false, + .disable_instrumentation = false, }, // This is populated after we create the Decl below. It is not read // by equality or hashing functions. @@ -8504,6 +8507,7 @@ pub fn getFuncInstanceIes( .calls_or_awaits_errorable_fn = false, .stack_alignment = .none, .inferred_error_set = true, + .disable_instrumentation = false, }, // This is populated after we create the Decl below. It is not read // by equality or hashing functions. @@ -11225,6 +11229,18 @@ pub fn funcSetCallsOrAwaitsErrorableFn(ip: *InternPool, func: Index) void { @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release); } +pub fn funcSetDisableInstrumentation(ip: *InternPool, func: Index) void { + const unwrapped_func = func.unwrap(ip); + const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex; + extra_mutex.lock(); + defer extra_mutex.unlock(); + + const analysis_ptr = ip.funcAnalysisPtr(func); + var analysis = analysis_ptr.*; + analysis.disable_instrumentation = true; + @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release); +} + pub fn funcSetCold(ip: *InternPool, func: Index, is_cold: bool) void { const unwrapped_func = func.unwrap(ip); const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex; diff --git a/src/Sema.zig b/src/Sema.zig index fbd5d636bd..b01c81ff22 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1316,6 +1316,11 @@ fn analyzeBodyInner( i += 1; continue; }, + .disable_instrumentation => { + try sema.zirDisableInstrumentation(); + i += 1; + continue; + }, .restore_err_ret_index => { try sema.zirRestoreErrRetIndex(block, extended); i += 1; @@ -6576,6 +6581,14 @@ fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) ip.funcSetCold(sema.func_index, is_cold); } +fn zirDisableInstrumentation(sema: *Sema) CompileError!void { + const pt = sema.pt; + const mod = pt.zcu; + const ip = &mod.intern_pool; + if (sema.func_index == .none) return; // does nothing outside a function + ip.funcSetDisableInstrumentation(sema.func_index); +} + fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src = block.builtinCallArgSrc(extra.node, 0); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 7e235ec648..e5f8250064 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1383,6 +1383,25 @@ pub const Object = struct { _ = try attributes.removeFnAttr(.cold); } + if (owner_mod.sanitize_thread and !func_analysis.disable_instrumentation) { + try attributes.addFnAttr(.sanitize_thread, &o.builder); + } else { + _ = try attributes.removeFnAttr(.sanitize_thread); + } + if (owner_mod.fuzz and !func_analysis.disable_instrumentation) { + try attributes.addFnAttr(.optforfuzzing, &o.builder); + if (comp.config.any_fuzz) { + _ = try attributes.removeFnAttr(.skipprofile); + _ = try attributes.removeFnAttr(.nosanitize_coverage); + } + } else { + _ = try attributes.removeFnAttr(.optforfuzzing); + if (comp.config.any_fuzz) { + try attributes.addFnAttr(.skipprofile, &o.builder); + try attributes.addFnAttr(.nosanitize_coverage, &o.builder); + } + } + // TODO: disable this if safety is off for the function scope const ssp_buf_size = owner_mod.stack_protector; if (ssp_buf_size != 0) { @@ -2982,12 +3001,6 @@ pub const Object = struct { try attributes.addFnAttr(.minsize, &o.builder); try attributes.addFnAttr(.optsize, &o.builder); } - if (owner_mod.sanitize_thread) { - try attributes.addFnAttr(.sanitize_thread, &o.builder); - } - if (owner_mod.fuzz) { - try attributes.addFnAttr(.optforfuzzing, &o.builder); - } const target = owner_mod.resolved_target.result; if (target.cpu.model.llvm_name) |s| { try attributes.addFnAttr(.{ .string = .{ diff --git a/src/print_zir.zig b/src/print_zir.zig index 2fee5f5d83..bd06362e62 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -524,6 +524,7 @@ const Writer = struct { .frame, .frame_address, .breakpoint, + .disable_instrumentation, .c_va_start, .in_comptime, .value_placeholder,