Skip to content

Commit 8536da4

Browse files
adjust stack-protector test (which inappropriately depends on IR types)
1 parent 2601a6c commit 8536da4

File tree

1 file changed

+15
-40
lines changed

1 file changed

+15
-40
lines changed

tests/assembly/stack-protector/stack-protector-heuristics-effect.rs

+15-40
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
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

Comments
 (0)