Skip to content

Commit 651251f

Browse files
committed
rusty stack protector
1 parent ce36a96 commit 651251f

File tree

6 files changed

+129
-4
lines changed

6 files changed

+129
-4
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,12 +254,19 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
254254
Some(llvm::CreateAttrStringValue(cx.llcx, "probe-stack", attr_value))
255255
}
256256

257-
fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
257+
fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> Option<&'ll Attribute> {
258258
let sspattr = match cx.sess().stack_protector() {
259259
StackProtector::None => return None,
260260
StackProtector::All => AttributeKind::StackProtectReq,
261261
StackProtector::Strong => AttributeKind::StackProtectStrong,
262262
StackProtector::Basic => AttributeKind::StackProtect,
263+
StackProtector::Rusty => {
264+
if cx.tcx.stack_protector.borrow().contains(&def_id) {
265+
AttributeKind::StackProtectReq
266+
} else {
267+
return None;
268+
}
269+
}
263270
};
264271

265272
Some(sspattr.create_attr(cx.llcx))
@@ -384,7 +391,10 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
384391
to_add.extend(instrument_function_attr(cx));
385392
to_add.extend(nojumptables_attr(cx));
386393
to_add.extend(probestack_attr(cx));
387-
to_add.extend(stackprotector_attr(cx));
394+
395+
// stack protector
396+
to_add.extend(stackprotector_attr(cx, instance.def_id()));
397+
388398

389399
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_BUILTINS) {
390400
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
@@ -17,7 +17,7 @@ use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, Varia
1717
use rustc_ast as ast;
1818
use rustc_data_structures::defer;
1919
use rustc_data_structures::fingerprint::Fingerprint;
20-
use rustc_data_structures::fx::FxHashMap;
20+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2121
use rustc_data_structures::intern::Interned;
2222
use rustc_data_structures::profiling::SelfProfilerRef;
2323
use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
@@ -1392,6 +1392,8 @@ pub struct GlobalCtxt<'tcx> {
13921392
/// Stores memory for globals (statics/consts).
13931393
pub(crate) alloc_map: interpret::AllocMap<'tcx>,
13941394

1395+
pub stack_protector: Lock<FxHashSet<DefId>>,
1396+
13951397
current_gcx: CurrentGcx,
13961398
}
13971399

@@ -1608,6 +1610,7 @@ impl<'tcx> TyCtxt<'tcx> {
16081610
canonical_param_env_cache: Default::default(),
16091611
data_layout,
16101612
alloc_map: interpret::AllocMap::new(),
1613+
stack_protector: Default::default(),
16111614
current_gcx,
16121615
});
16131616

compiler/rustc_mir_transform/src/lib.rs

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

4646
use pass_manager::{self as pm, Lint, MirLint, MirPass, WithMinOptLevel};
47+
use rustc_target::spec::StackProtector;
4748

4849
mod check_pointers;
4950
mod cost_checker;
@@ -57,6 +58,7 @@ mod lint_tail_expr_drop_order;
5758
mod patch;
5859
mod shim;
5960
mod ssa;
61+
mod stack_protector;
6062

6163
/// We import passes via this macro so that we can have a static list of pass names
6264
/// (used to verify CLI arguments). It takes a list of modules, followed by the passes
@@ -451,6 +453,17 @@ fn mir_promoted(
451453
lint_tail_expr_drop_order::run_lint(tcx, def, &body);
452454

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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::TyCtxt;
8+
9+
pub(super) struct StackProtectorFinder;
10+
11+
impl<'tcx> crate::MirPass<'tcx> for StackProtectorFinder {
12+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
13+
use Rvalue::*;
14+
let def_id = body.source.def_id();
15+
16+
for block in body.basic_blocks.iter() {
17+
for stmt in block.statements.iter() {
18+
if let StatementKind::Assign(assign) = &stmt.kind {
19+
let (_, rvalue) = assign.deref();
20+
match rvalue {
21+
// Get a reference/pointer to a variable
22+
Ref(..) | ThreadLocalRef(_) | RawPtr(..) => {
23+
tcx.stack_protector.borrow_mut().insert(def_id);
24+
return;
25+
}
26+
_ => continue,
27+
}
28+
}
29+
}
30+
31+
if let Some(terminator) = block.terminator.as_ref() {
32+
if let TerminatorKind::Call { destination: place, .. } = &terminator.kind {
33+
// Returns a mutable raw pointer, possibly a memory allocation function
34+
if let ty::RawPtr(_, Mutability::Mut) = place.ty(body, tcx).ty.kind() {
35+
tcx.stack_protector.borrow_mut().insert(def_id);
36+
return;
37+
}
38+
}
39+
}
40+
}
41+
}
42+
43+
fn is_required(&self) -> bool {
44+
true
45+
}
46+
}

compiler/rustc_target/src/spec/mod.rs

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

1608+
/// Stack protection for Rust code, the following are function check rules
1609+
/// that require stack protection in Rust:
1610+
/// - calls to stack memory allocation
1611+
/// - obtaining reference/pointer of local variables
1612+
Rusty,
1613+
16081614
/// Generate stack canaries in all functions.
16091615
All,
16101616
}
@@ -1615,6 +1621,7 @@ impl StackProtector {
16151621
StackProtector::None => "none",
16161622
StackProtector::Basic => "basic",
16171623
StackProtector::Strong => "strong",
1624+
StackProtector::Rusty => "rusty",
16181625
StackProtector::All => "all",
16191626
}
16201627
}
@@ -1628,6 +1635,7 @@ impl FromStr for StackProtector {
16281635
"none" => StackProtector::None,
16291636
"basic" => StackProtector::Basic,
16301637
"strong" => StackProtector::Strong,
1638+
"rusty" => StackProtector::Rusty,
16311639
"all" => StackProtector::All,
16321640
_ => return Err(()),
16331641
})

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

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
//@ revisions: all strong basic none missing
1+
//@ revisions: all strong basic none missing rusty
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
@@ -21,6 +22,8 @@ pub fn emptyfn() {
2122
// basic-NOT: __security_check_cookie
2223
// none-NOT: __security_check_cookie
2324
// missing-NOT: __security_check_cookie
25+
26+
// rusty-NOT: __security_check_cookie
2427
}
2528

2629
// CHECK-LABEL: array_char
@@ -39,6 +42,8 @@ pub fn array_char(f: fn(*const char)) {
3942
// basic: __security_check_cookie
4043
// none-NOT: __security_check_cookie
4144
// missing-NOT: __security_check_cookie
45+
46+
// rusty: __security_check_cookie
4247
}
4348

4449
// CHECK-LABEL: array_u8_1
@@ -55,6 +60,8 @@ pub fn array_u8_1(f: fn(*const u8)) {
5560
// basic-NOT: __security_check_cookie
5661
// none-NOT: __security_check_cookie
5762
// missing-NOT: __security_check_cookie
63+
64+
// rusty: __security_check_cookie
5865
}
5966

6067
// CHECK-LABEL: array_u8_small:
@@ -72,6 +79,8 @@ pub fn array_u8_small(f: fn(*const u8)) {
7279
// basic-NOT: __security_check_cookie
7380
// none-NOT: __security_check_cookie
7481
// missing-NOT: __security_check_cookie
82+
83+
// rusty: __security_check_cookie
7584
}
7685

7786
// CHECK-LABEL: array_u8_large:
@@ -88,6 +97,8 @@ pub fn array_u8_large(f: fn(*const u8)) {
8897
// basic: __security_check_cookie
8998
// none-NOT: __security_check_cookie
9099
// missing-NOT: __security_check_cookie
100+
101+
// rusty: __security_check_cookie
91102
}
92103

93104
#[derive(Copy, Clone)]
@@ -107,6 +118,8 @@ pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) {
107118
// basic: __security_check_cookie
108119
// none-NOT: __security_check_cookie
109120
// missing-NOT: __security_check_cookie
121+
122+
// rusty: __security_check_cookie
110123
}
111124

112125
// CHECK-LABEL: local_var_addr_used_indirectly
@@ -134,6 +147,8 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) {
134147
// basic-NOT: __security_check_cookie
135148
// none-NOT: __security_check_cookie
136149
// missing-NOT: __security_check_cookie
150+
151+
// rusty: __security_check_cookie
137152
}
138153

139154
// CHECK-LABEL: local_string_addr_taken
@@ -172,6 +187,8 @@ pub fn local_string_addr_taken(f: fn(&String)) {
172187
// none-NOT: __security_check_cookie
173188
// missing-NOT: __security_check_cookie
174189

190+
// rusty-NOT: __security_check_cookie
191+
175192
// CHECK-DAG: .seh_endproc
176193
}
177194

@@ -202,6 +219,9 @@ pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32
202219
// basic-NOT: __security_check_cookie
203220
// none-NOT: __security_check_cookie
204221
// missing-NOT: __security_check_cookie
222+
223+
// FIXME: rusty stack smash protection needs to support inline scenario detection
224+
// rusty: __security_check_cookie
205225
}
206226

207227
pub struct Gigastruct {
@@ -239,6 +259,9 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) {
239259
// basic: __security_check_cookie
240260
// none-NOT: __security_check_cookie
241261
// missing-NOT: __security_check_cookie
262+
263+
// TODO: How does the rust compiler handle moves of large structures?
264+
// rusty-NOT: __security_check_cookie
242265
}
243266

244267
// CHECK-LABEL: local_large_var_cloned
@@ -268,6 +291,9 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) {
268291
// basic: __security_check_cookie
269292
// none-NOT: __security_check_cookie
270293
// missing-NOT: __security_check_cookie
294+
295+
// TODO: How does the rust compiler handle moves of large structures?
296+
// rusty-NOT: __security_check_cookie
271297
}
272298

273299
extern "C" {
@@ -308,6 +334,11 @@ pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) {
308334
// basic-NOT: __security_check_cookie
309335
// none-NOT: __security_check_cookie
310336
// missing-NOT: __security_check_cookie
337+
338+
// TODO: Rusty thinks a function that returns a mutable raw pointer may
339+
// be a stack memory allocation function, so it performs stack smash protection.
340+
// Is it possible to optimize the heuristics?
341+
// rusty: __security_check_cookie
311342
}
312343

313344
// CHECK-LABEL: alloca_large_compile_time_constant_arg
@@ -320,6 +351,11 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) {
320351
// basic-NOT: __security_check_cookie
321352
// none-NOT: __security_check_cookie
322353
// missing-NOT: __security_check_cookie
354+
355+
// TODO: Rusty thinks a function that returns a mutable raw pointer may
356+
// be a stack memory allocation function, so it performs stack smash protection.
357+
// Is it possible to optimize the heuristics?
358+
// rusty: __security_check_cookie
323359
}
324360

325361
// CHECK-LABEL: alloca_dynamic_arg
@@ -332,6 +368,11 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) {
332368
// basic-NOT: __security_check_cookie
333369
// none-NOT: __security_check_cookie
334370
// missing-NOT: __security_check_cookie
371+
372+
// TODO: Rusty thinks a function that returns a mutable raw pointer may
373+
// be a stack memory allocation function, so it performs stack smash protection.
374+
// Is it possible to optimize the heuristics?
375+
// rusty: __security_check_cookie
335376
}
336377

337378
// The question then is: in what ways can Rust code generate array-`alloca`
@@ -364,6 +405,8 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
364405
// basic-NOT: __security_check_cookie
365406
// none-NOT: __security_check_cookie
366407
// missing-NOT: __security_check_cookie
408+
409+
// rusty-NOT: __security_check_cookie
367410
}
368411

369412
// CHECK-LABEL: unsized_local
@@ -388,4 +431,6 @@ pub fn unsized_local(s: &[u8], l: bool, f: fn(&mut [u8])) {
388431

389432
// none-NOT: __security_check_cookie
390433
// missing-NOT: __security_check_cookie
434+
435+
// rusty-NOT: __security_check_cookie
391436
}

0 commit comments

Comments
 (0)