2009-08-28 5 views
151

내 a.out 파일을 실행하고 있습니다. 실행 후 프로그램은 메시지와 함께 종료 후 일정 시간 실행 :스택 스매싱이 감지 됨

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

무엇 이에 대한 가능한 이유가 될 수 있고, 내가 그것을 어떻게 해결할 수 있습니까?

+1

코드의 어느 부분에서 스택 스매싱이 발생했는지 확인할 수 있습니까? 그러면 우리는 왜 그것이 어떻게 일어나고 그것을 정정하는지를 정확히 지적 할 수있을 것입니다. –

답변

225

스택 실제로 여기는 버퍼 오버 플로우 오류를 감지하기 위해 gcc가 사용하는 보호 메커니즘으로 인해 발생합니다. 다음 코드, 예를 들어 :

#include <stdio.h> 

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

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

컴파일러 (GCC이 경우에) 공지 된 값을 가질 보호 변수 (이라고 카나리아)를 추가한다. 크기가 10보다 큰 입력 문자열은이 변수가 손상되어 SIGABRT가 프로그램을 종료시킵니다.

일부 통찰력을 얻으려면 컴파일 중에 옵션 -fno-stack-protector을 사용하여 gcc 보호를 비활성화 해보십시오. 이 경우 잘못된 메모리 위치에 액세스하려고 할 때 세그멘테이션 오류와 같은 다른 오류가 발생할 수 있습니다. 보안 기능이므로 릴리스 빌드의 경우 항상 -fstack-protector을 켜야합니다.

디버거를 사용하여 프로그램을 실행하면 오버플로 지점에 대한 정보를 얻을 수 있습니다. Valgrind는 스택 관련 오류와 잘 작동하지 않지만 디버거처럼 크래시의 위치와 이유를 정확하게 지적 할 수 있습니다.

+2

이 답변을 주셔서 감사합니다!나는 내 경우에 쓰기를 시도한 변수를 초기화하지 않았 음을 발견했습니다. –

+3

빨간 영역을 추가 할 수 없으므로 Valgrind가 스택 관련 오류에 대해 잘 작동하지 않습니다. –

+5

이 대답은 정확하지 않으며 위험한 조언을 제공합니다. . 우선 스택 보호기를 제거하는 것이 올바른 해결책이 아닙니다. 스택 스매싱 오류가 발생하는 경우 코드에 심각한 보안 취약점이있는 것일 수 있습니다. 올바른 응답은 버그가있는 코드를 수정하는 것입니다. 둘째, grasGendarme이 지적한 바와 같이, Valgrind를 시도하는 것이 효과적이지는 않을 것입니다. Valgrind는 일반적으로 스택에 할당 된 데이터에 대한 잘못된 메모리 액세스를 감지하는 데는 적합하지 않습니다. –

7

당신은 valgrind을 사용하여 문제를 디버깅을 시도 할 수 있습니다 :

Valgrind의 분포는 현재 여섯 생산 품질의 도구가 포함되어 메모리 에러 검출기, 두 개의 스레드 오류 감지기, 캐시 및 branch- 예를 들어, 호출 그래프 생성 캐시 프로파일 러 및 힙 프로파일 러. 두 개의 실험 도구 : 힙/스택/전역 배열 오버런 감지기 및 SimPoint 기본 블록 벡터 생성기가 포함됩니다. X86/Linux, AMD64/Linux, PPC32/Linux, PPC64/Linux, 및 X86/Darwin (Mac OS X) 플랫폼의 에서 실행됩니다.

+2

그래,하지만 Valgrind는 스택 할당 버퍼의 오버 플로우에 대해 잘 작동하지 않습니다. 이는이 오류 메시지가 나타내는 상황입니다. –

+3

어떻게 스택 어레이 오버런 검출기 *를 사용할 수 있습니까? 당신은 정교 할 수 있습니까? –

3

이는 사용자가 잘못된 변수로 스택의 일부 변수에 썼다는 것을 의미합니다. 이는 대부분 Buffer overflow의 결과입니다.

+9

스택 오버플로는 스택이 다른 것으로 스매싱하는 것입니다. 다른 방법은 여기에 있습니다 : 무언가가 스택에 박살났습니다. –

+5

아닙니다. 그것은 다른 부분으로 스매싱하는 스택의 한 부분입니다. 따라서 스택 오버플로는 스택의 맨 위에 있지 않고 스택의 다른 부분에만 있습니다. –

13

다음과 같은 경우를보고하십시오 :

나는 "./a.out wepassssssssssssssssss"

사용할 때 발생해야 오류가 발견되지 않았습니다 스택 스매싱 보호를 비활성화

[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]:$ 

위의 질문에 답하기 위해 스택 스매싱 보호 장치가 활성화되어있어 프로그램에서 스택 오버플로가 발견되어 "** stack smashing detected : xxx"라는 메시지가 표시되었습니다.

어디서 발생했는지 확인하고 수정하십시오.

+9

내 프로그램에 많은 오자가 있습니다. – wearetherock

0

malloc()을 사용하여 struct *에 일부 메모리를 할당 한 후이 코드를 디버깅 한 후 free() 함수를 사용하여 할당 된 메모리를 확보하고 이후에 오류 메시지가 사라졌습니다.

0

어떤 이유가있을 수 있으며이를 어떻게 수정합니까?

한 시나리오는 다음 예제와 같습니다

: 당신은 문자열 또는 문자열의 일부를 취소 할 수 있습니다

#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--; 
    } 
} 

이 프로그램에서는 이런 일에 예를 호출 reverse()을위한 경우

reverse(arr + 2); 

이 같은 배열의 길이 통과하기로 결정하는 경우 :

#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--; 
    } 
} 

잘 작동합니다.

그러나 당신이 할 때 :

revSTR(arr + 2, len); 

당신이 얻을 수 :

==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) 

을 그리고 첫 번째 코드에서, arr의 길이는 괜찮 revSTR()의 내부에 선택되어 있기 때문에 이런 일이 발생, 두 번째 코드에서는 길이를 전달합니다 :

revSTR(arr + 2, len); 

길이가 이제는 arr + 2이라고 말할 때 실제로 길이가 길어집니다.

길이는 strlen (arr + 2)! = strlen (arr)입니다.

0

버퍼 오버 플로우로 인해 스택 손상이 발생합니다. 방위 프로그래밍을 통해 방어 할 수 있습니다.

배열에 액세스 할 때마다 액세스가 범위를 벗어나지 않도록 배열 앞에 어설트를 지정하십시오. 예를 들어 :이 수

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

당신은 배열 범위에 대해 생각하고 또한 가능하면 그들을 트리거 테스트를 추가하는 방법에 대해 생각하게한다. 정상적인 사용 중에 이러한 어설 션 중 일부가 실패하면 일반 if으로 바뀝니다.