Skip to content

Commit d054b94

Browse files
committed
modified: compiler/rustc_codegen_llvm/src/attributes.rs
modified: compiler/rustc_middle/src/ty/context.rs modified: compiler/rustc_mir_transform/src/lib.rs new file: compiler/rustc_mir_transform/src/stack_protector.rs modified: compiler/rustc_target/src/spec/mod.rs modified: tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs modified: tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect.rs
1 parent 383b9c4 commit d054b94

File tree

7 files changed

+174
-6
lines changed

7 files changed

+174
-6
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,19 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
270270
Some(llvm::CreateAttrStringValue(cx.llcx, "probe-stack", attr_value))
271271
}
272272

273-
fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
273+
fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> Option<&'ll Attribute> {
274274
let sspattr = match cx.sess().stack_protector() {
275275
StackProtector::None => return None,
276276
StackProtector::All => AttributeKind::StackProtectReq,
277+
278+
StackProtector::Rusty => {
279+
if cx.tcx.stack_protector.borrow().contains(&def_id) {
280+
AttributeKind::StackProtectStrong
281+
} else {
282+
return None;
283+
}
284+
}
285+
277286
StackProtector::Strong => AttributeKind::StackProtectStrong,
278287
StackProtector::Basic => AttributeKind::StackProtect,
279288
};
@@ -384,7 +393,9 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
384393
to_add.extend(instrument_function_attr(cx));
385394
to_add.extend(nojumptables_attr(cx));
386395
to_add.extend(probestack_attr(cx));
387-
to_add.extend(stackprotector_attr(cx));
396+
397+
// stack protector
398+
to_add.extend(stackprotector_attr(cx, instance.def_id()));
388399

389400
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_BUILTINS) {
390401
to_add.push(llvm::CreateAttrString(cx.llcx, "no-builtins"));

compiler/rustc_middle/src/ty/context.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, Varia
1919
use rustc_ast as ast;
2020
use rustc_data_structures::defer;
2121
use rustc_data_structures::fingerprint::Fingerprint;
22-
use rustc_data_structures::fx::FxHashMap;
22+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2323
use rustc_data_structures::intern::Interned;
2424
use rustc_data_structures::jobserver::Proxy;
2525
use rustc_data_structures::profiling::SelfProfilerRef;
@@ -1519,6 +1519,8 @@ pub struct GlobalCtxt<'tcx> {
15191519
/// Stores memory for globals (statics/consts).
15201520
pub(crate) alloc_map: interpret::AllocMap<'tcx>,
15211521

1522+
pub stack_protector: Lock<FxHashSet<DefId>>,
1523+
15221524
current_gcx: CurrentGcx,
15231525

15241526
/// A jobserver reference used to release then acquire a token while waiting on a query.
@@ -1746,6 +1748,7 @@ impl<'tcx> TyCtxt<'tcx> {
17461748
clauses_cache: Default::default(),
17471749
data_layout,
17481750
alloc_map: interpret::AllocMap::new(),
1751+
stack_protector: Default::default(),
17491752
current_gcx,
17501753
jobserver_proxy,
17511754
});

compiler/rustc_mir_transform/src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ mod pass_manager;
4040
use std::sync::LazyLock;
4141

4242
use pass_manager::{self as pm, Lint, MirLint, MirPass, WithMinOptLevel};
43+
use rustc_target::spec::StackProtector;
4344

4445
mod check_pointers;
4546
mod cost_checker;
@@ -193,6 +194,7 @@ declare_passes! {
193194
mod single_use_consts : SingleUseConsts;
194195
mod sroa : ScalarReplacementOfAggregates;
195196
mod strip_debuginfo : StripDebugInfo;
197+
mod stack_protector: StackProtectorFinder;
196198
mod unreachable_enum_branching : UnreachableEnumBranching;
197199
mod unreachable_prop : UnreachablePropagation;
198200
mod validate : Validator;
@@ -450,6 +452,17 @@ fn mir_promoted(
450452
lint_tail_expr_drop_order::run_lint(tcx, def, &body);
451453

452454
let promoted = promote_pass.promoted_fragments.into_inner();
455+
456+
if tcx.sess.stack_protector() == StackProtector::Rusty {
457+
pm::run_passes(
458+
tcx,
459+
&mut body,
460+
&[&stack_protector::StackProtectorFinder],
461+
None,
462+
pm::Optimizations::Allowed,
463+
)
464+
}
465+
453466
(tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
454467
}
455468

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//! Validates the MIR to ensure that invariants are upheld.
2+
3+
use std::ops::Deref;
4+
5+
use rustc_middle::mir::*;
6+
use rustc_middle::ty;
7+
use rustc_middle::ty::{Instance, TyCtxt};
8+
use rustc_target::callconv::PassMode;
9+
10+
pub(super) struct StackProtectorFinder;
11+
12+
impl<'tcx> crate::MirPass<'tcx> for StackProtectorFinder {
13+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
14+
use Rvalue::*;
15+
let def_id = body.source.def_id();
16+
17+
for block in body.basic_blocks.iter() {
18+
for stmt in block.statements.iter() {
19+
if let StatementKind::Assign(assign) = &stmt.kind {
20+
let (_, rvalue) = assign.deref();
21+
match rvalue {
22+
// Get a reference/pointer to a variable
23+
Ref(..) | ThreadLocalRef(_) | RawPtr(..) => {
24+
tcx.stack_protector.borrow_mut().insert(def_id);
25+
return;
26+
}
27+
_ => continue,
28+
}
29+
}
30+
}
31+
32+
if let Some(terminator) = block.terminator.as_ref() {
33+
if let TerminatorKind::Call { destination: place, .. } = &terminator.kind {
34+
// Returns a mutable raw pointer, possibly a memory allocation function
35+
if let ty::RawPtr(_, Mutability::Mut) = place.ty(body, tcx).ty.kind() {
36+
tcx.stack_protector.borrow_mut().insert(def_id);
37+
return;
38+
}
39+
}
40+
}
41+
42+
let instance = Instance::mono(tcx, def_id);
43+
let Ok(fn_abi) = tcx.fn_abi_of_instance(
44+
ty::TypingEnv::fully_monomorphized().as_query_input((instance, ty::List::empty())),
45+
) else {
46+
// FIXME: Find when an Err() message is returned
47+
48+
return;
49+
};
50+
51+
// for arg in fn_abi.args.iter() {
52+
// if matches!(&arg.mode, PassMode::Indirect { attrs: _, meta_attrs: _, on_stack: false }) {
53+
// tcx.stack_protector.borrow_mut().insert(def_id);
54+
// return;
55+
// }
56+
// }
57+
58+
let ret = &fn_abi.ret;
59+
if matches!(&ret.mode, PassMode::Indirect { attrs: _, meta_attrs: _, on_stack: false })
60+
{
61+
tcx.stack_protector.borrow_mut().insert(def_id);
62+
return;
63+
}
64+
}
65+
}
66+
67+
fn is_required(&self) -> bool {
68+
true
69+
}
70+
}

compiler/rustc_target/src/spec/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1760,6 +1760,12 @@ pub enum StackProtector {
17601760
/// the address of a local variable.
17611761
Strong,
17621762

1763+
/// Stack protection for Rust code, the following are function check rules
1764+
/// that require stack protection in Rust:
1765+
/// - calls to stack memory allocation
1766+
/// - obtaining reference/pointer of local variables
1767+
Rusty,
1768+
17631769
/// Generate stack canaries in all functions.
17641770
All,
17651771
}
@@ -1770,6 +1776,7 @@ impl StackProtector {
17701776
StackProtector::None => "none",
17711777
StackProtector::Basic => "basic",
17721778
StackProtector::Strong => "strong",
1779+
StackProtector::Rusty => "rusty",
17731780
StackProtector::All => "all",
17741781
}
17751782
}
@@ -1783,6 +1790,7 @@ impl FromStr for StackProtector {
17831790
"none" => StackProtector::None,
17841791
"basic" => StackProtector::Basic,
17851792
"strong" => StackProtector::Strong,
1793+
"rusty" => StackProtector::Rusty,
17861794
"all" => StackProtector::All,
17871795
_ => return Err(()),
17881796
})

tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
//@ revisions: all strong basic none missing
1+
//@ revisions: all rusty strong basic none missing
22
//@ assembly-output: emit-asm
33
//@ only-windows
44
//@ only-msvc
55
//@ ignore-32bit 64-bit table based SEH has slightly different behaviors than classic SEH
66
//@ [all] compile-flags: -Z stack-protector=all
7+
//@ [rusty] compile-flags: -Z stack-protector=rusty
78
//@ [strong] compile-flags: -Z stack-protector=strong
89
//@ [basic] compile-flags: -Z stack-protector=basic
910
//@ [none] compile-flags: -Z stack-protector=none
@@ -16,6 +17,7 @@
1617
#[no_mangle]
1718
pub fn emptyfn() {
1819
// all: __security_check_cookie
20+
// rusty-NOT: __security_check_cookie
1921
// strong-NOT: __security_check_cookie
2022
// basic-NOT: __security_check_cookie
2123
// none-NOT: __security_check_cookie
@@ -34,6 +36,7 @@ pub fn array_char(f: fn(*const char)) {
3436
f(&c as *const _);
3537

3638
// all: __security_check_cookie
39+
// rusty: __security_check_cookie
3740
// strong: __security_check_cookie
3841
// basic: __security_check_cookie
3942
// none-NOT: __security_check_cookie
@@ -50,6 +53,7 @@ pub fn array_u8_1(f: fn(*const u8)) {
5053
// array variables regardless of their size.
5154

5255
// all: __security_check_cookie
56+
// rusty: __security_check_cookie
5357
// strong: __security_check_cookie
5458
// basic-NOT: __security_check_cookie
5559
// none-NOT: __security_check_cookie
@@ -67,6 +71,7 @@ pub fn array_u8_small(f: fn(*const u8)) {
6771
// Small arrays do not lead to stack protection by the 'basic' heuristic.
6872

6973
// all: __security_check_cookie
74+
// rusty: __security_check_cookie
7075
// strong: __security_check_cookie
7176
// basic-NOT: __security_check_cookie
7277
// none-NOT: __security_check_cookie
@@ -83,6 +88,7 @@ pub fn array_u8_large(f: fn(*const u8)) {
8388
// will also protect this function.
8489

8590
// all: __security_check_cookie
91+
// rusty: __security_check_cookie
8692
// strong: __security_check_cookie
8793
// basic: __security_check_cookie
8894
// none-NOT: __security_check_cookie
@@ -102,6 +108,7 @@ pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) {
102108
// also protect this function.
103109

104110
// all: __security_check_cookie
111+
// rusty: __security_check_cookie
105112
// strong: __security_check_cookie
106113
// basic: __security_check_cookie
107114
// none-NOT: __security_check_cookie
@@ -129,6 +136,7 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) {
129136
// ```
130137

131138
// all: __security_check_cookie
139+
// rusty: __security_check_cookie
132140
// strong: __security_check_cookie
133141
// basic-NOT: __security_check_cookie
134142
// none-NOT: __security_check_cookie
@@ -164,9 +172,10 @@ pub fn local_string_addr_taken(f: fn(&String)) {
164172
// LLVM does not support generating stack protectors in functions with funclet
165173
// based EH personalities.
166174
// https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4
175+
167176
// all-NOT: __security_check_cookie
177+
// rusty-NOT: __security_check_cookie
168178
// strong-NOT: __security_check_cookie
169-
170179
// basic-NOT: __security_check_cookie
171180
// none-NOT: __security_check_cookie
172181
// missing-NOT: __security_check_cookie
@@ -197,6 +206,10 @@ pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32
197206
// the `strong` heuristic.
198207

199208
// all: __security_check_cookie
209+
210+
// FIXME: rusty stack smash protection needs to support inline scenario detection
211+
// rusty: __security_check_cookie
212+
200213
// strong-NOT: __security_check_cookie
201214
// basic-NOT: __security_check_cookie
202215
// none-NOT: __security_check_cookie
@@ -234,6 +247,10 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) {
234247
// ```
235248

236249
// all: __security_check_cookie
250+
251+
// FIXME: How does the rust compiler handle moves of large structures?
252+
// rusty-NOT: __security_check_cookie
253+
237254
// strong: __security_check_cookie
238255
// basic: __security_check_cookie
239256
// none-NOT: __security_check_cookie
@@ -263,6 +280,10 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) {
263280
// ```
264281

265282
// all: __security_check_cookie
283+
284+
// FIXME: How does the rust compiler handle moves of large structures?
285+
// rusty-NOT: __security_check_cookie
286+
266287
// strong: __security_check_cookie
267288
// basic: __security_check_cookie
268289
// none-NOT: __security_check_cookie
@@ -303,6 +324,12 @@ pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) {
303324
f(unsafe { alloca(8) });
304325

305326
// all: __security_check_cookie
327+
328+
// FIXME: Rusty thinks a function that returns a mutable raw pointer may
329+
// be a stack memory allocation function, so it performs stack smash protection.
330+
// Is it possible to optimize the heuristics?
331+
// rusty: __security_check_cookie
332+
306333
// strong-NOT: __security_check_cookie
307334
// basic-NOT: __security_check_cookie
308335
// none-NOT: __security_check_cookie
@@ -315,6 +342,7 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) {
315342
f(unsafe { alloca(9) });
316343

317344
// all: __security_check_cookie
345+
// rusty: __security_check_cookie
318346
// strong-NOT: __security_check_cookie
319347
// basic-NOT: __security_check_cookie
320348
// none-NOT: __security_check_cookie
@@ -327,6 +355,7 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) {
327355
f(unsafe { alloca(n) });
328356

329357
// all: __security_check_cookie
358+
// rusty: __security_check_cookie
330359
// strong-NOT: __security_check_cookie
331360
// basic-NOT: __security_check_cookie
332361
// none-NOT: __security_check_cookie
@@ -358,6 +387,7 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
358387
// based EH personalities.
359388
// https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4
360389
// all-NOT: __security_check_cookie
390+
// rusty-NOT: __security_check_cookie
361391
// strong-NOT: __security_check_cookie
362392

363393
// basic-NOT: __security_check_cookie

0 commit comments

Comments
 (0)