C/C++ Stack Usage Compilation Warning and Error

Introduction

In my previous blog post “Large Array Safety Issue”, I discussed the catastrophic consequence of using large arrays in a safety system. In addition to large arrays, technically, there can also be a single object that is too large and unsafe to be put on the stack during the runtime. More importantly, those unsafe implementations might be in some black box functions that we have to use but got no interest or time to read or check. So the questions becomes, how could we identify large stack usages quickly and prevent it as much as possible.

In this blog post, I would like to quickly discuss throwing compilation warning and error for large C/C++ stack usages.

GCC Large Stack Usage and Error

Similar to the examples that I presented in the previous blog post “Large Array Safety Issue”, the functions in the following example are using large static arrays, allocating dynamic memory, and using variable length arrays (dynamic arrays) on stack, respectively.

large_stack.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <alloca.h>
#include <array>
#include <cstdint>
#include <iostream>

// If the stack usage is fully static but exceeds the specified amount, it’s:
// warning: stack usage is 8388640 bytes [-Wstack-usage=]
void work_on_some_static_arrays()
{
std::array<int32_t, 1048576> a{};
a[0] = 1;
std::array<int32_t, 1048576> b{};
b[0] = 1;
}

// If the stack usage is (partly) dynamic and not bounded, it’s:
// warning: stack usage might be unbounded [-Wstack-usage=]
void work_on_some_dynamic_arrays()
{
int32_t* a = static_cast<int32_t*>(alloca(4194304));
a[0] = 1;
int32_t* b = static_cast<int32_t*>(alloca(4194304));
b[0] = 1;
}

// If the stack usage is (partly) dynamic and not bounded, it’s:
// warning: stack usage might be unbounded [-Wstack-usage=]
void work_on_some_variable_length_arrays(int32_t n)
{
// Variable-length array
// Avoid doing this as much as possible as it is bad practice.
int32_t vals[n];
}

int main()
{
int32_t const n{1024};
std::cout << "Hello Underworld!" << std::endl;
work_on_some_static_arrays();
work_on_some_dynamic_arrays();
work_on_some_variable_length_arrays(n);
}

GCC allows us to throw warnings for the functions that has large stack usages over certain threshold using -Wstack-usage=byte-size. It will also throw warnings for stack usages that are dynamic and unbounded via operations such as alloca or variable-length array. (alloca or variable-length array are really bad practices and should be avoided as much as possible.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ g++ large_stack.cpp -o large_stack -std=c++14 -Wstack-usage=204800
large_stack.cpp: In function ‘void work_on_some_static_arrays()’:
large_stack.cpp:8:6: warning: stack usage is 8388640 bytes [-Wstack-usage=]
8 | void work_on_some_static_arrays()
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
large_stack.cpp: In function ‘void work_on_some_dynamic_arrays()’:
large_stack.cpp:18:6: warning: stack usage might be unbounded [-Wstack-usage=]
18 | void work_on_some_dynamic_arrays()
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
large_stack.cpp: In function ‘void work_on_some_variable_length_arrays(int32_t)’:
large_stack.cpp:28:6: warning: stack usage might be unbounded [-Wstack-usage=]
28 | void work_on_some_variable_length_arrays(int32_t n)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ ./large_stack
Hello Underworld!
Segmentation fault (core dumped)

However, -Wstack-usage=byte-size only enables throwing warning at compile time. If the user did not pay enough attention to the compilation logs, those warnings would be missed and the unsafe build would still be used for production.

Therefore, we would like to throw errors instead of warnings at compile time for unsafe stack usages. In our case, we could do it using -Werror=stack-usage=byte-size and the build would be failed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ g++ large_stack.cpp -o large_stack -std=c++14 -Wstack-usage=204800 -Werror=stack-usage=204800
large_stack.cpp: In function ‘void work_on_some_static_arrays()’:
large_stack.cpp:8:6: error: stack usage is 8388640 bytes [-Werror=stack-usage=]
8 | void work_on_some_static_arrays()
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
large_stack.cpp: In function ‘void work_on_some_dynamic_arrays()’:
large_stack.cpp:18:6: error: stack usage might be unbounded [-Werror=stack-usage=]
18 | void work_on_some_dynamic_arrays()
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
large_stack.cpp: In function ‘void work_on_some_variable_length_arrays(int32_t)’:
large_stack.cpp:28:6: error: stack usage might be unbounded [-Werror=stack-usage=]
28 | void work_on_some_variable_length_arrays(int32_t n)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1plus: some warnings being treated as errors

References

Author

Lei Mao

Posted on

01-22-2023

Updated on

01-22-2023

Licensed under


Comments