11
11
//@ compile-flags: -C opt-level=2 -Z merge-functions=disabled
12
12
//@ min-llvm-version: 17.0.2
13
13
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
+
14
19
#![ crate_type = "lib" ]
15
20
16
21
#![ allow( incomplete_features) ]
@@ -39,23 +44,9 @@ pub fn array_char(f: fn(*const char)) {
39
44
f ( & b as * const _ ) ;
40
45
f ( & c as * const _ ) ;
41
46
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
-
56
47
// all: __stack_chk_fail
57
48
// strong: __stack_chk_fail
58
- // basic-NOT : __stack_chk_fail
49
+ // basic: __stack_chk_fail
59
50
// none-NOT: __stack_chk_fail
60
51
// missing-NOT: __stack_chk_fail
61
52
}
@@ -163,26 +154,11 @@ pub fn local_string_addr_taken(f: fn(&String)) {
163
154
f ( & x) ;
164
155
165
156
// 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.
182
158
183
159
// all: __stack_chk_fail
184
160
// strong: __stack_chk_fail
185
- // basic-NOT : __stack_chk_fail
161
+ // basic: __stack_chk_fail
186
162
// none-NOT: __stack_chk_fail
187
163
// missing-NOT: __stack_chk_fail
188
164
}
@@ -233,8 +209,8 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) {
233
209
// Even though the local variable conceptually doesn't have its address
234
210
// taken, it's so large that the "move" is implemented with a reference to a
235
211
// 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:
238
214
// ```
239
215
// cat <<EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk
240
216
// #include <cstdint>
@@ -248,7 +224,7 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) {
248
224
249
225
// all: __stack_chk_fail
250
226
// strong: __stack_chk_fail
251
- // basic-NOT : __stack_chk_fail
227
+ // basic: __stack_chk_fail
252
228
// none-NOT: __stack_chk_fail
253
229
// missing-NOT: __stack_chk_fail
254
230
}
@@ -261,9 +237,9 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) {
261
237
// A new instance of `Gigastruct` is passed to `f()`, without any apparent
262
238
// connection to this stack frame. Still, since instances of `Gigastruct`
263
239
// 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++:
267
243
// ```
268
244
// cat <<EOF | clang++ -O2 -fstack-protector-strong -S -x c++ - -o - | grep stack_chk
269
245
// #include <cstdint>
@@ -275,10 +251,9 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) {
275
251
// EOF
276
252
// ```
277
253
278
-
279
254
// all: __stack_chk_fail
280
255
// strong: __stack_chk_fail
281
- // basic-NOT : __stack_chk_fail
256
+ // basic: __stack_chk_fail
282
257
// none-NOT: __stack_chk_fail
283
258
// missing-NOT: __stack_chk_fail
284
259
}
0 commit comments