Minimal reproduction example with disassembly analysis
main.c
void myfunc(char *const src, int len) {
int i;
for (i = 0; i < len; ++i) {
src[i] = 42;
}
}
int main(void) {
char arr[] = {'a', 'b', 'c', 'd'};
int len = sizeof(arr);
myfunc(arr, len + 1); /* Cause smashing by writing one byte too many. */
return 0;
}
GitHub upstream.
Compile and run:
gcc -fstack-protector-all -g -O0 -std=c99 main.c
ulimit -c unlimited && rm -f core
./a.out
fails as desired:
*** stack smashing detected ***: terminated
Aborted (core dumped)
Tested on Ubuntu 20.04, GCC 10.2.0.
On Ubuntu 16.04, GCC 6.4.0, I could reproduce with -fstack-protector
instead of -fstack-protector-all
, but it stopped blowing up when I tested on GCC 10.2.0 as per Geng Jiawen’s comment. man gcc
clarifies that as suggested by the option name, the -all
version adds checks more aggressively, and therefore presumably incurs a larger performance loss:
-fstack-protector
Emit extra code to check for buffer overflows, such as stack smashing attacks. This is done by adding a guard variable to functions with vulnerable objects. This includes functions that call “alloca”, and functions with buffers larger than or equal to 8 bytes. The guards are initialized when a function is entered and then checked when the function exits. If a guard check fails, an error message is printed and the program exits. Only variables that are actually allocated on the stack are considered, optimized away variables or variables allocated in registers don’t count.
-fstack-protector-all
Like -fstack-protector except that all functions are protected.
Disassembly
Now we look at the disassembly:
objdump -D a.out
which contains:
int main (void){
400579: 55 push %rbp
40057a: 48 89 e5 mov %rsp,%rbp
# Allocate 0x10 of stack space.
40057d: 48 83 ec 10 sub $0x10,%rsp
# Put the 8 byte canary from %fs:0x28 to -0x8(%rbp),
# which is right at the bottom of the stack.
400581: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400588: 00 00
40058a: 48 89 45 f8 mov %rax,-0x8(%rbp)
40058e: 31 c0 xor %eax,%eax
char arr[] = {'a', 'b', 'c', 'd'};
400590: c6 45 f4 61 movb $0x61,-0xc(%rbp)
400594: c6 45 f5 62 movb $0x62,-0xb(%rbp)
400598: c6 45 f6 63 movb $0x63,-0xa(%rbp)
40059c: c6 45 f7 64 movb $0x64,-0x9(%rbp)
int len = sizeof(arr);
4005a0: c7 45 f0 04 00 00 00 movl $0x4,-0x10(%rbp)
myfunc(arr, len + 1);
4005a7: 8b 45 f0 mov -0x10(%rbp),%eax
4005aa: 8d 50 01 lea 0x1(%rax),%edx
4005ad: 48 8d 45 f4 lea -0xc(%rbp),%rax
4005b1: 89 d6 mov %edx,%esi
4005b3: 48 89 c7 mov %rax,%rdi
4005b6: e8 8b ff ff ff callq 400546 <myfunc>
return 0;
4005bb: b8 00 00 00 00 mov $0x0,%eax
}
# Check that the canary at -0x8(%rbp) hasn't changed after calling myfunc.
# If it has, jump to the failure point __stack_chk_fail.
4005c0: 48 8b 4d f8 mov -0x8(%rbp),%rcx
4005c4: 64 48 33 0c 25 28 00 xor %fs:0x28,%rcx
4005cb: 00 00
4005cd: 74 05 je 4005d4 <main+0x5b>
4005cf: e8 4c fe ff ff callq 400420 <__stack_chk_fail@plt>
# Otherwise, exit normally.
4005d4: c9 leaveq
4005d5: c3 retq
4005d6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
4005dd: 00 00 00
Notice the handy comments automatically added by objdump
‘s artificial intelligence module.
If you run this program multiple times through GDB, you will see that:
- the canary gets a different random value every time
- the last loop of
myfunc
is exactly what modifies the address of the canary
The canary randomized by setting it with %fs:0x28
, which contains a random value as explained at:
- https://unix.stackexchange.com/questions/453749/what-sets-fs0x28-stack-canary
- Why does this memory address %fs:0x28 ( fs[0x28] ) have a random value?
Debug attempts
From now on, we modify the code:
myfunc(arr, len + 1);
to be instead:
myfunc(arr, len);
myfunc(arr, len + 1); /* line 12 */
myfunc(arr, len);
to be more interesting.
We will then try to see if we can pinpoint the culprit + 1
call with a method more automated than just reading and understanding the entire source code.
gcc -fsanitize=address
to enable Google’s Address Sanitizer (ASan)
If you recompile with this flag and run the program, it outputs:
#0 0x4008bf in myfunc /home/ciro/test/main.c:4
#1 0x40099b in main /home/ciro/test/main.c:12
#2 0x7fcd2e13d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#3 0x400798 in _start (/home/ciro/test/a.out+0x40079
followed by some more colored output.
This clearly pinpoints the problematic line 12.
The source code for this is at: https://github.com/google/sanitizers but as we saw from the example it is already upstreamed into GCC.
ASan can also detect other memory problems such as memory leaks: How to find memory leak in a C++ code/project?
Valgrind SGCheck
As mentioned by others, Valgrind is not good at solving this kind of problem.
It does have an experimental tool called SGCheck:
SGCheck is a tool for finding overruns of stack and global arrays. It works by using a heuristic approach derived from an observation about the likely forms of stack and global array accesses.
So I was not very surprised when it did not find the error:
valgrind --tool=exp-sgcheck ./a.out
The error message should look like this apparently: Valgrind missing error
GDB
An important observation is that if you run the program through GDB, or examine the core
file after the fact:
gdb -nh -q a.out core
then, as we saw on the assembly, GDB should point you to the end of the function that did the canary check:
(gdb) bt
#0 0x00007f0f66e20428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007f0f66e2202a in __GI_abort () at abort.c:89
#2 0x00007f0f66e627ea in __libc_message (do_abort=do_abort@entry=1, fmt=fmt@entry=0x7f0f66f7a49f "*** %s ***: %s terminatedn") at ../sysdeps/posix/libc_fatal.c:175
#3 0x00007f0f66f0415c in __GI___fortify_fail (msg=<optimized out>, msg@entry=0x7f0f66f7a481 "stack smashing detected") at fortify_fail.c:37
#4 0x00007f0f66f04100 in __stack_chk_fail () at stack_chk_fail.c:28
#5 0x00000000004005f6 in main () at main.c:15
(gdb) f 5
#5 0x00000000004005f6 in main () at main.c:15
15 }
(gdb)
And therefore the problem is likely in one of the calls that this function made.
Next we try to pinpoint the exact failing call by first single stepping up just after the canary is set:
400581: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
400588: 00 00
40058a: 48 89 45 f8 mov %rax,-0x8(%rbp)
and watching the address:
(gdb) p $rbp - 0x8
$1 = (void *) 0x7fffffffcf18
(gdb) watch 0x7fffffffcf18
Hardware watchpoint 2: *0x7fffffffcf18
(gdb) c
Continuing.
Hardware watchpoint 2: *0x7fffffffcf18
Old value = 1800814336
New value = 1800814378
myfunc (src=0x7fffffffcf14 "*****?Vk266", <incomplete sequence 355216>, len=5) at main.c:3
3 for (i = 0; i < len; ++i) {
(gdb) p len
$2 = 5
(gdb) p i
$3 = 4
(gdb) bt
#0 myfunc (src=0x7fffffffcf14 "*****?Vk266", <incomplete sequence 355216>, len=5) at main.c:3
#1 0x00000000004005cc in main () at main.c:12
Now, this does leaves us at the right offending instruction: len = 5
and i = 4
, and in this particular case, did point us to the culprit line 12.
However, the backtrace is corrupted, and contains some trash. A correct backtrace would look like:
#0 myfunc (src=0x7fffffffcf14 "abcd", len=4) at main.c:3
#1 0x00000000004005b8 in main () at main.c:11
so maybe this could corrupt the stack and prevent you from seeing the trace.
Also, this method requires knowing what is the last call of the canary checking function otherwise you will have false positives, which will not always be feasible, unless you use reverse debugging.
ks10ks 0 / 0 / 0 Регистрация: 12.05.2015 Сообщений: 4 |
||||
1 |
||||
12.07.2019, 19:51. Показов 6683. Ответов 5 Метки нет (Все метки)
Здравствуйте! Код следующий:
0 |
Programming Эксперт 94731 / 64177 / 26122 Регистрация: 12.04.2006 Сообщений: 116,782 |
12.07.2019, 19:51 |
5 |
“C with Classes” 1634 / 1392 / 521 Регистрация: 16.08.2014 Сообщений: 5,824 Записей в блоге: 1 |
|
12.07.2019, 19:59 |
2 |
Сообщение было отмечено ks10ks как решение Решениеks10ks, по ходу нарушение границ стека, вместо
1 |
2013 / 1342 / 382 Регистрация: 16.05.2013 Сообщений: 3,463 Записей в блоге: 6 |
|
12.07.2019, 22:23 |
3 |
С точки зрения памяти размер памяти выделяемый для array[j][i] и array[i][j] одинаков и непрерывен так что ошибка должно быть это только сообщение конкретного компилятора.
0 |
Неэпический 17813 / 10585 / 2043 Регистрация: 27.09.2012 Сообщений: 26,625 Записей в блоге: 1 |
|
12.07.2019, 22:30 |
4 |
Ilot, там массив 3×4.
0 |
2013 / 1342 / 382 Регистрация: 16.05.2013 Сообщений: 3,463 Записей в блоге: 6 |
|
12.07.2019, 22:43 |
5 |
Croessmah, истину глаголишь котяра. Посыпаю голову пеплом.
0 |
0 / 0 / 0 Регистрация: 12.05.2015 Сообщений: 4 |
|
13.07.2019, 13:10 [ТС] |
6 |
ks10ks, по ходу нарушение границ стека, вместо array1[j][i] попробуй array1[i][j] Спасибо за помощь!
0 |
#include <stdio.h>
int get_max(int [], int );
int main(){
const int N = 14;
int arr[] = {};
int l;
l = get_max(arr, N);
return 0;
}
int get_max(int arr[], int size)
{
for (int i = 0; i < size; i++){
scanf("%d", &arr[i]);
}
int max = 0;
for (int i = 0; i < size; i++){
if(arr[i] > max){
max = arr[i];
}
}
printf("%dn", max);
}
После выполнения программы, написанной на Си, выдаёт ошибку:
*** stack smashing detected ***: terminated
Аварийный останов (стек памяти сброшен на диск)
-
Вопрос заданболее года назад
-
3303 просмотра
Офрмляйте код нормально, пожалуйста, с отступами. Массиву нужен размер, у вас же он равен нулю. Функция get_max ничего не возвращает, хотя заявлен тип int.
Пригласить эксперта
Вышел за границу локального массива (описанного в теле функции без ключевого слова static) и затер адрес возврата мусорным значением, результат – передача управления по завершении функции get_max неизвестно куда, по итогу ось гасит процесс к чертовой матери, не давая ему даже шанса понять что произошло на данной нам господом богом планете земля, притом, что он всего лишь выполнялся и ничего плохо не сделал никому, это жестоко.
-
Показать ещё
Загружается…
24 мая 2023, в 17:41
300 руб./за проект
24 мая 2023, в 17:38
7000 руб./за проект
24 мая 2023, в 17:29
5000 руб./за проект
Минуточку внимания
Федор Новиков
Искусственный Интеллект
(314854)
1 год назад
В языках С, С++ и многих других массивы индексируются с 0,а не с 1, как в Паскале. Выход за пределы массива ломает содержимое стека
Владимира АбрамоваУченик (94)
1 год назад
Двумерный массив разве не начинается с 1?
Федор Новиков
Искусственный Интеллект
(314854)
Владимира Абрамова, от размерности не зависит. int o[10][4] – элементы от o[0][0] до o[9][3]
Peer-2-PeerМудрец (16176)
1 год назад
Не понял!! Где задаётся и меняется значение переменной sum?
Что-то стёрлось?
Федор Новиков
Искусственный Интеллект
(314854)
Peer-2-Peer, похоже, что про суммирование забыли. Но вопрос был про развал стека
- the
stack smashing detected
Error in C - Fix the
stack smashing detected
Error in C
This tutorial highlights the cause and solves the stack smashing detected
error in C.
Anyone facing this issue should understand the division of the memory and stack layout. It will help identify the causes of this error while coding in C programming.
the stack smashing detected
Error in C
Usually, the compiler (we are talking about the GCC compiler here) generates this error to defend against the buffer overflow. The buffer overflow happens whenever the user input exceeds the capacity of the buffer (a temporary storage area).
In that case, the compiler throws an error saying stack smashing detected
. See the following code snippet where we get the stack smashing detected
error if the user inputs more than 10 characters.
Example Code:
#include<stdio.h>
int main(int argc, char **argv) {
char array[10];
printf("Please enter your username.");
gets(array);
return 0;
}
As this error surrounds the buffer overflow and stack concepts, it is crucial to understand the division of the memory and stack layout here.
The buffer overflow is a dangerous process, so the compiler gets the advantage of different protection mechanisms to guard against this error. One of the mechanisms is known as a canary, a randomly generated value.
The compiler (GCC) adds the protection variables (called canaries) that have known values. The canary is overwritten if the buffer overflows.
Further, the compiler identifies by comparing with known values that the stack is compromised and generates an error saying: stack smashing detected
.
To prevent the buffer overflow protection variable and have some insights, we can disable the GCC’s protection using the -fno-stack-protector
while compiling. For instance, $ gcc -o filename -fno-stack-protector
.
In this way, we most likely will get the segmentation fault
error because we will be trying to access the illegal memory location. Remember that the fstack-protector
must be turned on for the release builds because it’s a security feature.
Fix the stack smashing detected
Error in C
Now, how do we rectify this error and execute the program smoothly?
We can not say that preventing the stack protector is a solution to this problem, but it helps find some insights that we can use to rectify this error. We can also get some details about the cause of overflow by executing the program with a debugger.
The Valgrind will not work well with the stack-related issues and errors, but a debugger may help us pinpoint the reason and location for the crash. Find that buggy code and fix it to avoid severe security vulnerabilities.