1111//@ compile-flags: -C opt-level=2 -Z merge-functions=disabled 
1212//@ min-llvm-version: 17.0.2 
1313
14+ // NOTE: the heuristics for stack smash protection inappropriately rely on types in LLVM IR, 
15+ // despite those types having no semantic meaning. This means that the `basic` and `strong` 
16+ // settings do not behave in a coherent way. This is a known issue in LLVM. 
17+ // See comments on https://github.com/rust-lang/rust/issues/114903. 
18+ 
1419#![ crate_type = "lib" ]  
1520
1621#![ allow( incomplete_features) ]  
@@ -39,23 +44,9 @@ pub fn array_char(f: fn(*const char)) {
3944    f ( & b as  * const  _ ) ; 
4045    f ( & c as  * const  _ ) ; 
4146
42-     // Any type of local array variable leads to stack protection with the 
43-     // "strong" heuristic. The 'basic' heuristic only adds stack protection to 
44-     // functions with local array variables of a byte-sized type, however. Since 
45-     // 'char' is 4 bytes in Rust, this function is not protected by the 'basic' 
46-     // heuristic 
47-     // 
48-     // (This test *also* takes the address of the local stack variables. We 
49-     // cannot know that this isn't what triggers the `strong` heuristic. 
50-     // However, the test strategy of passing the address of a stack array to an 
51-     // external function is sufficient to trigger the `basic` heuristic (see 
52-     // test `array_u8_large()`). Since the `basic` heuristic only checks for the 
53-     // presence of stack-local array variables, we can be confident that this 
54-     // test also captures this part of the `strong` heuristic specification.) 
55- 
5647    // all: __stack_chk_fail 
5748    // strong: __stack_chk_fail 
58-     // basic-NOT : __stack_chk_fail 
49+     // basic: __stack_chk_fail 
5950    // none-NOT: __stack_chk_fail 
6051    // missing-NOT: __stack_chk_fail 
6152} 
@@ -163,26 +154,11 @@ pub fn local_string_addr_taken(f: fn(&String)) {
163154    f ( & x) ; 
164155
165156    // Taking the address of the local variable `x` leads to stack smash 
166-     // protection with the `strong` heuristic, but not with the `basic` 
167-     // heuristic. It does not matter that the reference is not mut. 
168-     // 
169-     // An interesting note is that a similar function in C++ *would* be 
170-     // protected by the `basic` heuristic, because `std::string` has a char 
171-     // array internally as a small object optimization: 
172-     // ``` 
173-     // cat <<EOF | clang++ -O2 -fstack-protector -S -x c++ - -o - | grep stack_chk 
174-     // #include <string> 
175-     // void f(void (*g)(const std::string&)) { 
176-     //     std::string x; 
177-     //     g(x); 
178-     // } 
179-     // EOF 
180-     // ``` 
181-     // 
157+     // protection. It does not matter that the reference is not mut. 
182158
183159    // all: __stack_chk_fail 
184160    // strong: __stack_chk_fail 
185-     // basic-NOT : __stack_chk_fail 
161+     // basic: __stack_chk_fail 
186162    // none-NOT: __stack_chk_fail 
187163    // missing-NOT: __stack_chk_fail 
188164} 
@@ -233,8 +209,8 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) {
233209    // Even though the local variable conceptually doesn't have its address 
234210    // taken, it's so large that the "move" is implemented with a reference to a 
235211    // stack-local variable in the ABI. Consequently, this function *is* 
236-     // protected by the `strong` heuristic . This is also the case for 
237-     // rvalue-references in C++,  regardless of struct size: 
212+     // protected. This is also the case for rvalue-references in C++,  
213+     // regardless of struct size: 
238214    // ``` 
239215    // cat <<EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk 
240216    // #include <cstdint> 
@@ -248,7 +224,7 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) {
248224
249225    // all: __stack_chk_fail 
250226    // strong: __stack_chk_fail 
251-     // basic-NOT : __stack_chk_fail 
227+     // basic: __stack_chk_fail 
252228    // none-NOT: __stack_chk_fail 
253229    // missing-NOT: __stack_chk_fail 
254230} 
@@ -261,9 +237,9 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) {
261237    // A new instance of `Gigastruct` is passed to `f()`, without any apparent 
262238    // connection to this stack frame. Still, since instances of `Gigastruct` 
263239    // are sufficiently large, it is allocated in the caller stack frame and 
264-     // passed as a pointer. As such, this function is *also* protected by the  
265-     // `strong` heuristic, just  like `local_large_var_moved`. This is also the 
266-     // case for pass-by-value  of sufficiently large structs in C++: 
240+     // passed as a pointer. As such, this function is *also* protected, just  
241+     // like `local_large_var_moved`. This is also the case for pass-by-value  
242+     // of sufficiently large structs in C++: 
267243    // ``` 
268244    // cat <<EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk 
269245    // #include <cstdint> 
@@ -275,10 +251,9 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) {
275251    // EOF 
276252    // ``` 
277253
278- 
279254    // all: __stack_chk_fail 
280255    // strong: __stack_chk_fail 
281-     // basic-NOT : __stack_chk_fail 
256+     // basic: __stack_chk_fail 
282257    // none-NOT: __stack_chk_fail 
283258    // missing-NOT: __stack_chk_fail 
284259} 
0 commit comments