77//@ [strong] compile-flags: -Z stack-protector=strong
88//@ [basic] compile-flags: -Z stack-protector=basic
99//@ [none] compile-flags: -Z stack-protector=none
10- //@ compile-flags: -C opt-level=2 -Z merge-functions=disabled
10+ //@ compile-flags: -C opt-level=2 -Z merge-functions=disabled -Cpanic=abort
1111
1212#![ crate_type = "lib" ]
1313#![ allow( internal_features) ]
@@ -26,6 +26,7 @@ pub fn emptyfn() {
2626// CHECK-LABEL: array_char
2727#[ no_mangle]
2828pub fn array_char ( f : fn ( * const char ) ) {
29+ // CHECK-DAG: .cv_fpo_endprologue
2930 let a = [ 'c' ; 1 ] ;
3031 let b = [ 'd' ; 3 ] ;
3132 let c = [ 'e' ; 15 ] ;
@@ -39,11 +40,14 @@ pub fn array_char(f: fn(*const char)) {
3940 // basic: __security_check_cookie
4041 // none-NOT: __security_check_cookie
4142 // missing-NOT: __security_check_cookie
43+
44+ // CHECK-DAG: .cv_fpo_endproc
4245}
4346
4447// CHECK-LABEL: array_u8_1
4548#[ no_mangle]
4649pub fn array_u8_1 ( f : fn ( * const u8 ) ) {
50+ // CHECK-DAG: .cv_fpo_endprologue
4751 let a = [ 0u8 ; 1 ] ;
4852 f ( & a as * const _ ) ;
4953
@@ -55,11 +59,14 @@ pub fn array_u8_1(f: fn(*const u8)) {
5559 // basic-NOT: __security_check_cookie
5660 // none-NOT: __security_check_cookie
5761 // missing-NOT: __security_check_cookie
62+
63+ // CHECK-DAG: .cv_fpo_endproc
5864}
5965
6066// CHECK-LABEL: array_u8_small:
6167#[ no_mangle]
6268pub fn array_u8_small ( f : fn ( * const u8 ) ) {
69+ // CHECK-DAG: .cv_fpo_endprologue
6370 let a = [ 0u8 ; 2 ] ;
6471 let b = [ 0u8 ; 7 ] ;
6572 f ( & a as * const _ ) ;
@@ -72,11 +79,14 @@ 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+ // CHECK-DAG: .cv_fpo_endproc
7584}
7685
7786// CHECK-LABEL: array_u8_large:
7887#[ no_mangle]
7988pub fn array_u8_large ( f : fn ( * const u8 ) ) {
89+ // CHECK-DAG: .cv_fpo_endprologue
8090 let a = [ 0u8 ; 9 ] ;
8191 f ( & a as * const _ ) ;
8292
@@ -88,6 +98,8 @@ pub fn array_u8_large(f: fn(*const u8)) {
8898 // basic: __security_check_cookie
8999 // none-NOT: __security_check_cookie
90100 // missing-NOT: __security_check_cookie
101+
102+ // CHECK-DAG: .cv_fpo_endproc
91103}
92104
93105#[ derive( Copy , Clone ) ]
@@ -96,6 +108,7 @@ pub struct ByteSizedNewtype(u8);
96108// CHECK-LABEL: array_bytesizednewtype_9:
97109#[ no_mangle]
98110pub fn array_bytesizednewtype_9 ( f : fn ( * const ByteSizedNewtype ) ) {
111+ // CHECK-DAG: .cv_fpo_endprologue
99112 let a = [ ByteSizedNewtype ( 0 ) ; 9 ] ;
100113 f ( & a as * const _ ) ;
101114
@@ -107,11 +120,14 @@ pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) {
107120 // basic: __security_check_cookie
108121 // none-NOT: __security_check_cookie
109122 // missing-NOT: __security_check_cookie
123+
124+ // CHECK-DAG: .cv_fpo_endproc
110125}
111126
112127// CHECK-LABEL: local_var_addr_used_indirectly
113128#[ no_mangle]
114129pub fn local_var_addr_used_indirectly ( f : fn ( bool ) ) {
130+ // CHECK-DAG: .cv_fpo_endprologue
115131 let a = 5 ;
116132 let a_addr = & a as * const _ as usize ;
117133 f ( a_addr & 0x10 == 0 ) ;
@@ -134,37 +150,27 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) {
134150 // basic-NOT: __security_check_cookie
135151 // none-NOT: __security_check_cookie
136152 // missing-NOT: __security_check_cookie
153+
154+ // CHECK-DAG: .cv_fpo_endproc
137155}
138156
139157// CHECK-LABEL: local_string_addr_taken
140158#[ no_mangle]
141159pub fn local_string_addr_taken ( f : fn ( & String ) ) {
160+ // CHECK-DAG: .cv_fpo_endprologue
142161 let x = String :: new ( ) ;
143162 f ( & x) ;
144163
145164 // Taking the address of the local variable `x` leads to stack smash
146- // protection with the `strong` heuristic, but not with the `basic`
147- // heuristic. It does not matter that the reference is not mut.
148- //
149- // An interesting note is that a similar function in C++ *would* be
150- // protected by the `basic` heuristic, because `std::string` has a char
151- // array internally as a small object optimization:
152- // ```
153- // cat <<EOF | clang++ -O2 -fstack-protector -S -x c++ - -o - | grep stack_chk
154- // #include <string>
155- // void f(void (*g)(const std::string&)) {
156- // std::string x;
157- // g(x);
158- // }
159- // EOF
160- // ```
161- //
165+ // protection. It does not matter that the reference is not mut.
162166
163167 // all: __security_check_cookie
164- // strong-NOT : __security_check_cookie
165- // basic-NOT : __security_check_cookie
168+ // strong: __security_check_cookie
169+ // basic: __security_check_cookie
166170 // none-NOT: __security_check_cookie
167171 // missing-NOT: __security_check_cookie
172+
173+ // CHECK-DAG: .cv_fpo_endproc
168174}
169175
170176pub trait SelfByRef {
@@ -180,6 +186,7 @@ impl SelfByRef for i32 {
180186// CHECK-LABEL: local_var_addr_taken_used_locally_only
181187#[ no_mangle]
182188pub fn local_var_addr_taken_used_locally_only ( factory : fn ( ) -> i32 , sink : fn ( i32 ) ) {
189+ // CHECK-DAG: .cv_fpo_endprologue
183190 let x = factory ( ) ;
184191 let g = x. f ( ) ;
185192 sink ( g) ;
@@ -194,6 +201,8 @@ pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32
194201 // basic-NOT: __security_check_cookie
195202 // none-NOT: __security_check_cookie
196203 // missing-NOT: __security_check_cookie
204+
205+ // CHECK-DAG: .cv_fpo_endproc
197206}
198207
199208pub struct Gigastruct {
@@ -207,6 +216,7 @@ pub struct Gigastruct {
207216// CHECK-LABEL: local_large_var_moved
208217#[ no_mangle]
209218pub fn local_large_var_moved ( f : fn ( Gigastruct ) ) {
219+ // CHECK-DAG: .cv_fpo_endprologue
210220 let x = Gigastruct { does : 0 , not : 1 , have : 2 , array : 3 , members : 4 } ;
211221 f ( x) ;
212222
@@ -231,11 +241,14 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) {
231241 // basic: __security_check_cookie
232242 // none-NOT: __security_check_cookie
233243 // missing-NOT: __security_check_cookie
244+
245+ // CHECK-DAG: .cv_fpo_endproc
234246}
235247
236248// CHECK-LABEL: local_large_var_cloned
237249#[ no_mangle]
238250pub fn local_large_var_cloned ( f : fn ( Gigastruct ) ) {
251+ // CHECK-DAG: .cv_fpo_endprologue
239252 f ( Gigastruct { does : 0 , not : 1 , have : 2 , array : 3 , members : 4 } ) ;
240253
241254 // A new instance of `Gigastruct` is passed to `f()`, without any apparent
@@ -260,6 +273,8 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) {
260273 // basic: __security_check_cookie
261274 // none-NOT: __security_check_cookie
262275 // missing-NOT: __security_check_cookie
276+
277+ // CHECK-DAG: .cv_fpo_endproc
263278}
264279
265280extern "C" {
@@ -293,49 +308,57 @@ extern "C" {
293308// CHECK-LABEL: alloca_small_compile_time_constant_arg
294309#[ no_mangle]
295310pub fn alloca_small_compile_time_constant_arg ( f : fn ( * mut ( ) ) ) {
311+ // CHECK-DAG: .cv_fpo_endprologue
296312 f ( unsafe { alloca ( 8 ) } ) ;
297313
298314 // all: __security_check_cookie
299315 // strong-NOT: __security_check_cookie
300316 // basic-NOT: __security_check_cookie
301317 // none-NOT: __security_check_cookie
302318 // missing-NOT: __security_check_cookie
319+
320+ // CHECK-DAG: .cv_fpo_endproc
303321}
304322
305323// CHECK-LABEL: alloca_large_compile_time_constant_arg
306324#[ no_mangle]
307325pub fn alloca_large_compile_time_constant_arg ( f : fn ( * mut ( ) ) ) {
326+ // CHECK-DAG: .cv_fpo_endprologue
308327 f ( unsafe { alloca ( 9 ) } ) ;
309328
310329 // all: __security_check_cookie
311330 // strong-NOT: __security_check_cookie
312331 // basic-NOT: __security_check_cookie
313332 // none-NOT: __security_check_cookie
314333 // missing-NOT: __security_check_cookie
334+
335+ // CHECK-DAG: .cv_fpo_endproc
315336}
316337
317338// CHECK-LABEL: alloca_dynamic_arg
318339#[ no_mangle]
319340pub fn alloca_dynamic_arg ( f : fn ( * mut ( ) ) , n : usize ) {
341+ // CHECK-DAG: .cv_fpo_endprologue
320342 f ( unsafe { alloca ( n) } ) ;
321343
322344 // all: __security_check_cookie
323345 // strong-NOT: __security_check_cookie
324346 // basic-NOT: __security_check_cookie
325347 // none-NOT: __security_check_cookie
326348 // missing-NOT: __security_check_cookie
349+
350+ // CHECK-DAG: .cv_fpo_endproc
327351}
328352
329353// The question then is: in what ways can Rust code generate array-`alloca`
330354// LLVM instructions? This appears to only be generated by
331355// rustc_codegen_ssa::traits::Builder::array_alloca() through
332- // rustc_codegen_ssa::mir::operand::OperandValue::store_unsized(). FWICT
333- // this is support for the "unsized locals" unstable feature:
334- // https://doc.rust-lang.org/unstable-book/language-features/unsized-locals.html.
356+ // rustc_codegen_ssa::mir::operand::OperandValue::store_unsized().
335357
336358// CHECK-LABEL: unsized_fn_param
337359#[ no_mangle]
338360pub fn unsized_fn_param ( s : [ u8 ] , l : bool , f : fn ( [ u8 ] ) ) {
361+ // CHECK-DAG: .cv_fpo_endprologue
339362 let n = if l { 1 } else { 2 } ;
340363 f ( * Box :: < [ u8 ] > :: from ( & s[ 0 ..n] ) ) ; // slice-copy with Box::from
341364
@@ -346,14 +369,11 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) {
346369 // alloca, and is therefore not protected by the `strong` or `basic`
347370 // heuristics.
348371
349- // We should have a __security_check_cookie call in `all` and `strong` modes but
350- // LLVM does not support generating stack protectors in functions with funclet
351- // based EH personalities.
352- // https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4
353372 // all-NOT: __security_check_cookie
354373 // strong-NOT: __security_check_cookie
355-
356374 // basic-NOT: __security_check_cookie
357375 // none-NOT: __security_check_cookie
358376 // missing-NOT: __security_check_cookie
377+
378+ // CHECK-DAG: .cv_fpo_endproc
359379}
0 commit comments