2009-08-28 32 views
151

Tôi đang thực thi tệp a.out của mình. Sau khi thực thi chương trình chạy một thời gian rồi thoát ra với thông báo:Ngăn xếp đập được phát hiện

**** stack smashing detected ***: ./a.out terminated* 
*======= Backtrace: =========* 
*/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)Aborted* 

Điều gì có thể là lý do có thể cho việc này và làm cách nào để khắc phục?

+1

Bạn có thể xác định những phần nào trong mã của bạn khiến cho ngăn xếp đập và đăng lên không? Sau đó, chúng tôi có thể sẽ chỉ ra chính xác lý do tại sao nó xảy ra và cách sửa nó. –

Trả lời

225

Stack Smashing ở đây thực sự là do cơ chế bảo vệ được sử dụng bởi gcc để phát hiện lỗi tràn bộ đệm. Ví dụ: đoạn mã sau:

#include <stdio.h> 

void func() 
{ 
    char array[10]; 
    gets(array); 
} 

int main(int argc, char **argv) 
{ 
    func(); 
} 

Trình biên dịch, (trong trường hợp này gcc) thêm các biến bảo vệ (được gọi là canaries) có giá trị đã biết. Một chuỗi đầu vào có kích thước lớn hơn 10 làm cho tham nhũng của biến này dẫn đến SIGABRT chấm dứt chương trình.

Để hiểu rõ hơn, bạn có thể thử tắt bảo vệ gcc này bằng cách sử dụng tùy chọn -fno-stack-protector khi biên dịch. Trong trường hợp đó, bạn sẽ gặp phải một lỗi khác, rất có thể là lỗi phân đoạn khi bạn đang cố truy cập vào một vị trí bộ nhớ bất hợp pháp. Lưu ý rằng -fstack-protector phải luôn được bật cho bản phát hành bản phát hành vì đây là tính năng bảo mật.

Bạn có thể nhận được một số thông tin về điểm tràn bằng cách chạy chương trình bằng trình gỡ lỗi. Valgrind không hoạt động tốt với các lỗi liên quan đến ngăn xếp, nhưng giống như một trình gỡ lỗi, nó có thể giúp bạn xác định vị trí và lý do cho sự cố.

+2

cảm ơn câu trả lời này!Tôi thấy rằng trong trường hợp của tôi, tôi đã không khởi tạo biến mà tôi đang cố gắng viết cho –

+3

Valgrind không hoạt động tốt cho các lỗi liên quan đến ngăn xếp, vì nó không thể thêm vùng màu đỏ ở đó –

+5

Câu trả lời này không đúng và cung cấp lời khuyên nguy hiểm . Trước hết, loại bỏ bảo vệ ngăn xếp không phải là giải pháp đúng - nếu bạn đang nhận được một lỗi đập ngăn xếp, bạn có thể có một lỗ hổng bảo mật nghiêm trọng trong mã của bạn. Câu trả lời chính xác là * sửa mã lỗi *. Thứ hai, như grasGendarme chỉ ra, đề nghị thử Valgrind sẽ không hiệu quả. Valgrind thường không hoạt động để phát hiện truy cập bộ nhớ bất hợp pháp tới dữ liệu được phân bổ theo chồng. –

7

Bạn có thể thử để gỡ lỗi các vấn đề sử dụng valgrind:

Sự phân bố Valgrind hiện bao gồm sáu công cụ sản xuất chất lượng: một máy dò lỗi bộ nhớ, hai chủ đề dò lỗi, một bộ nhớ cache và ngành, sơ đồ dự đoán, một tạo sơ đồ bộ nhớ cache tạo biểu đồ cuộc gọi, và một trình lược tả heap. Nó cũng bao gồm hai công cụ thử nghiệm: a heap/stack/overrun mảng toàn cầu detector và máy phát vectơ SimPoint cơ bản . Nó chạy trên các nền tảng sau đây: X86/Linux, AMD64/Linux, PPC32/Linux, PPC64/Linux, và X86/Darwin (Mac OS X).

+2

Vâng, nhưng Valgrind không hoạt động tốt đối với tràn bộ đệm được phân bổ theo chồng, đó là tình huống mà thông báo lỗi này cho biết. –

+3

Làm cách nào chúng tôi có thể sử dụng * bộ dò tìm ngăn xếp mảng ngăn xếp * đó? Bạn có thể xây dựng? –

3

Điều đó có nghĩa là bạn đã viết cho một số biến trên ngăn xếp theo cách bất hợp pháp, rất có thể là kết quả của một Buffer overflow.

+9

Ngăn xếp tràn là ngăn xếp đập vào thứ khác. Ở đây nó là một cách khác xung quanh: một cái gì đó đã đập vào ngăn xếp. –

+5

Không thực sự. Đó là một phần của ngăn xếp đập vào một phần khác. Vì vậy, nó thực sự là một tràn bộ đệm, chỉ cần không trên đầu trang của ngăn xếp, nhưng "chỉ" vào một phần khác của ngăn xếp. –

13

Hãy nhìn vào tình huống sau đây:

[email protected]:$ cat test_overflow.c 
#include <stdio.h> 
#include <string.h> 

int check_password(char *password){ 
    int flag = 0; 
    char buffer[20]; 
    strcpy(buffer, password); 

    if(strcmp(buffer, "mypass") == 0){ 
     flag = 1; 
    } 
    if(strcmp(buffer, "yourpass") == 0){ 
     flag = 1; 
    } 
    return flag; 
} 

int main(int argc, char *argv[]){ 
    if(argc >= 2){ 
     if(check_password(argv[1])){ 
      printf("%s", "Access granted\n"); 
     }else{ 
      printf("%s", "Access denied\n"); 
     } 
    }else{ 
     printf("%s", "Please enter password!\n"); 
    } 
} 
[email protected]:$ gcc -g -fno-stack-protector test_overflow.c 
[email protected]:$ ./a.out mypass 
Access granted 
[email protected]:$ ./a.out yourpass 
Access granted 
[email protected]:$ ./a.out wepass 
Access denied 
[email protected]:$ ./a.out wepassssssssssssssssss 
Access granted 

[email protected]:$ gcc -g -fstack-protector test_overflow.c 
[email protected]:$ ./a.out wepass 
Access denied 
[email protected]:$ ./a.out mypass 
Access granted 
[email protected]:$ ./a.out yourpass 
Access granted 
[email protected]:$ ./a.out wepassssssssssssssssss 
*** stack smashing detected ***: ./a.out terminated 
======= Backtrace: ========= 
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)[0xce0ed8] 
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x0)[0xce0e90] 
./a.out[0x8048524] 
./a.out[0x8048545] 
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xc16b56] 
./a.out[0x8048411] 
======= Memory map: ======== 
007d9000-007f5000 r-xp 00000000 08:06 5776  /lib/libgcc_s.so.1 
007f5000-007f6000 r--p 0001b000 08:06 5776  /lib/libgcc_s.so.1 
007f6000-007f7000 rw-p 0001c000 08:06 5776  /lib/libgcc_s.so.1 
0090a000-0090b000 r-xp 00000000 00:00 0   [vdso] 
00c00000-00d3e000 r-xp 00000000 08:06 1183  /lib/tls/i686/cmov/libc-2.10.1.so 
00d3e000-00d3f000 ---p 0013e000 08:06 1183  /lib/tls/i686/cmov/libc-2.10.1.so 
00d3f000-00d41000 r--p 0013e000 08:06 1183  /lib/tls/i686/cmov/libc-2.10.1.so 
00d41000-00d42000 rw-p 00140000 08:06 1183  /lib/tls/i686/cmov/libc-2.10.1.so 
00d42000-00d45000 rw-p 00000000 00:00 0 
00e0c000-00e27000 r-xp 00000000 08:06 4213  /lib/ld-2.10.1.so 
00e27000-00e28000 r--p 0001a000 08:06 4213  /lib/ld-2.10.1.so 
00e28000-00e29000 rw-p 0001b000 08:06 4213  /lib/ld-2.10.1.so 
08048000-08049000 r-xp 00000000 08:05 1056811 /dos/hacking/test/a.out 
08049000-0804a000 r--p 00000000 08:05 1056811 /dos/hacking/test/a.out 
0804a000-0804b000 rw-p 00001000 08:05 1056811 /dos/hacking/test/a.out 
08675000-08696000 rw-p 00000000 00:00 0   [heap] 
b76fe000-b76ff000 rw-p 00000000 00:00 0 
b7717000-b7719000 rw-p 00000000 00:00 0 
bfc1c000-bfc31000 rw-p 00000000 00:00 0   [stack] 
Aborted 
[email protected]:$ 

Khi tôi vô hiệu hóa các đập bảo vệ chồng không có lỗi đã được phát hiện, trong đó nên đã xảy ra khi tôi sử dụng "./a.out wepassssssssssssssssss"

Vì vậy, để trả lời câu hỏi của bạn ở trên, thông báo "** ngăn xếp đập phát hiện: xxx" đã được hiển thị bởi vì ngăn xếp đập ngăn xếp của bạn đã hoạt động và thấy rằng có ngăn xếp tràn trong chương trình của bạn.

Chỉ cần tìm nơi xảy ra và khắc phục sự cố.

+9

có nhiều lỗi chính tả trong chương trình của tôi – wearetherock

0

Tôi đã gặp lỗi này khi sử dụng malloc() để cấp phát bộ nhớ cho cấu trúc * sau khi chi tiêu một số mã này, cuối cùng tôi đã sử dụng hàm free() để giải phóng bộ nhớ được cấp phát và sau đó thông báo lỗi biến mất :)

0

Điều gì có thể là lý do có thể cho việc này và làm cách nào để khắc phục?

Một kịch bản sẽ là trong ví dụ sau:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

void swap (char *a , char *b); 
void revSTR (char *const src); 

int main (void){ 
    char arr[] = "A-B-C-D-E"; 

    revSTR(arr); 
    printf("ARR = %s\n", arr); 
} 

void swap (char *a , char *b){ 
    char tmp = *a; 
    *a = *b; 
    *b = tmp; 
} 

void revSTR (char *const src){ 
    char *start = src; 
    char *end = start + (strlen(src) - 1); 

    while (start < end){ 
     swap(&(*start) , &(*end)); 
     start++; 
     end--; 
    } 
} 

Trong chương trình này, bạn có thể đảo ngược một chuỗi hoặc một phần của chuỗi nếu bạn ví dụ gọi reverse() với một cái gì đó như thế này:

reverse(arr + 2); 

Nếu bạn quyết định chuyển độ dài của mảng như sau:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

void swap (char *a , char *b); 
void revSTR (char *const src, size_t len); 

int main (void){ 
    char arr[] = "A-B-C-D-E"; 
    size_t len = strlen(arr); 

    revSTR(arr, len); 
    printf("ARR = %s\n", arr); 
} 

void swap (char *a , char *b){ 
    char tmp = *a; 
    *a = *b; 
    *b = tmp; 
} 

void revSTR (char *const src, size_t len){ 
    char *start = src; 
    char *end = start + (len - 1); 

    while (start < end){ 
     swap(&(*start) , &(*end)); 
     start++; 
     end--; 
    } 
} 

Hoạt động tốt.

Nhưng khi bạn làm điều này:

revSTR(arr + 2, len); 

Bạn nhận được:

==7125== Command: ./program 
==7125== 
ARR = A- 
*** stack smashing detected ***: ./program terminated 
==7125== 
==7125== Process terminating with default action of signal 6 (SIGABRT) 
==7125== at 0x4E6F428: raise (raise.c:54) 
==7125== by 0x4E71029: abort (abort.c:89) 
==7125== by 0x4EB17E9: __libc_message (libc_fatal.c:175) 
==7125== by 0x4F5311B: __fortify_fail (fortify_fail.c:37) 
==7125== by 0x4F530BF: __stack_chk_fail (stack_chk_fail.c:28) 
==7125== by 0x400637: main (program.c:14) 

Và điều này xảy ra bởi vì trong các mã đầu tiên, chiều dài của arr được kiểm tra bên trong revSTR() đó là tốt, nhưng trong mã thứ hai nơi bạn vượt qua độ dài:

revSTR(arr + 2, len); 

chiều dài bây giờ dài hơn chiều dài thực sự bạn vượt qua khi bạn nói arr + 2.

Độ dài strlen (arr + 2)! = strlen (arr).

0

Ngăn xếp sự cố do lỗi tràn bộ đệm gây ra. Bạn có thể bảo vệ chống lại chúng bằng cách lập trình phòng thủ.

Bất cứ khi nào bạn truy cập một mảng, hãy đưa ra lời khẳng định trước khi đảm bảo quyền truy cập không nằm ngoài giới hạn. Ví dụ:

assert(i + 1 < N); 
assert(i < N); 
a[i + 1] = a[i]; 

Điều này khiến bạn suy nghĩ về giới hạn mảng và cũng khiến bạn nghĩ đến việc thêm thử nghiệm để kích hoạt chúng nếu có thể. Nếu một số xác nhận này có thể bị lỗi trong quá trình sử dụng bình thường, hãy biến chúng thành một số if thông thường.

Các vấn đề liên quan