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