programing

c/c++ 프로그램에서 가능한/잠재적인 스택 오버플로 문제를 감지하는 방법은 무엇입니까?

bestprogram 2023. 6. 11. 11:04

c/c++ 프로그램에서 가능한/잠재적인 스택 오버플로 문제를 감지하는 방법은 무엇입니까?

앱에 스택 공간이 얼마나 있는지, 실행 중 스택 사용량의 최고 워터마크가 얼마인지를 확인할 수 있는 표준 방법이 있습니까?

또한 실제 오버플로의 무서운 경우에는 어떻게 됩니까?

충돌, 예외 또는 신호를 트리거합니까?모든 시스템과 컴파일러에 표준이 있습니까?

특히 Windows, Linux 및 Macintosh를 찾고 있습니다.

Windows에서는 스택 오버플로 예외가 생성됩니다.

이를 보여주는 윈도우 코드는 다음과 같습니다.

#include <stdio.h>
#include <windows.h>

void StackOverFlow()
{
  CONTEXT context;

  // we are interested control registers
  context.ContextFlags = CONTEXT_CONTROL;

  // get the details
  GetThreadContext(GetCurrentThread(), &context);

  // print the stack pointer
  printf("Esp: %X\n", context.Esp);

  // this will eventually overflow the stack
  StackOverFlow();
}

DWORD ExceptionFilter(EXCEPTION_POINTERS *pointers, DWORD dwException)
{
  return EXCEPTION_EXECUTE_HANDLER;
}

void main()
{
  CONTEXT context;

  // we are interested control registers
  context.ContextFlags = CONTEXT_CONTROL;

  // get the details
  GetThreadContext(GetCurrentThread(), &context);

  // print the stack pointer
  printf("Esp: %X\n", context.Esp);

  __try
  {
    // cause a stack overflow
    StackOverFlow();
  }
  __except(ExceptionFilter(GetExceptionInformation(), GetExceptionCode()))
  {
    printf("\n****** ExceptionFilter fired ******\n");
  }
}

이 exe를 실행하면 다음과 같은 출력이 생성됩니다.

Esp: 12FC4C
Esp: 12F96C
Esp: 12F68C
.....
Esp: 33D8C
Esp: 33AAC
Esp: 337CC

****** ExceptionFilter fired ******

Linux에서 코드가 스택을 통과하여 쓰기를 시도하면 분할 오류가 발생합니다.

스택 크기는 프로세스 간에 상속되는 속성입니다.에서 다과같명령을사용은하여셸에경수와 같은 할 수 있는 ulimit -s단위:)()sh,ksh,zsh) 또는limit stacksize(tcsh,zsh).

프로그램에서 스택의 크기는 다음을 사용하여 읽을 수 있습니다.

#include <sys/resource.h>
#include <stdio.h>

int main() {
    struct rlimit l;
    getrlimit(RLIMIT_STACK, &l);
    printf("stack_size = %ld\n", l.rlim_cur);
    return 0;
}

저는 사용 가능한 스택의 크기를 구하는 표준 방법을 모릅니다.

은 스은다로시니로 합니다.argc에 다에의내있습다니이용의 .argv그리고 환경의 복사본, 그리고 당신의 변수.그러나 커널은 스택의 시작 위치를 랜덤화할 수 있고 위에 더미 값이 있을 수 있기 때문입니다.argc당신이 가지고 있다고 가정하는 것은 잘못된 것입니다.l.rlim_cur아래에서 사용 가능한 바이트&argc.

하는 한 입니다./proc/1234/maps서)1234는 프로그램의 프로세스 ID입니다.이러한 경계를 알고 나면 최신 로컬 변수의 주소를 확인하여 스택 사용량을 계산할 수 있습니다.

gcc는 반환 주소와 일반 변수 사이에 추가 메모리 블록을 "void" 함수 호출에 배치합니다(이 예에서 함수는 void test() {chara[10]; b[20]:

call stack:
-----------
return address
dummy
char b[10]
char a[20]

함수가 포인터 'a'에 36바이트를 쓰면 오버플로가 반환 주소를 '손상'합니다(보안 위반 가능성 있음).그러나 포인터와 반환 주소 사이에 있는 '더미'의 값도 변경하여 프로그램이 충돌하고 경고가 표시됩니다(-fno-stack-protector를 사용하여 이를 비활성화할 수 있음).

Linux에서 Gnu libsigsegv 라이브러리는 다음 기능을 포함합니다.stackoverflow_install_handler스택 오버플로를 탐지하고 경우에 따라 복구할 수 있습니다.

창에서 스택(특정 스레드의 경우)은 생성 전에 이 스레드에 대해 지정된 스택 크기에 도달할 때까지 주문형으로 증가합니다.

온디맨드 확장은 처음에는 스택의 일부만 사용할 수 있다는 점에서 가드 페이지를 사용하여 구현됩니다. 그 다음에 가드 페이지가 표시되면 예외가 트리거됩니다. 이 예외는 특별하며 시스템에서 처리됩니다. 처리를 수행하면 사용 가능한 스택 공간이 증가합니다(제한에 도달한 경우에도 확인됩니다!).)가 표시되고 읽기 작업이 다시 시도됩니다.

제한에 도달하면 스택 오버플로 예외가 발생하는 증가가 더 이상 없습니다.는 스레드 블록에 되며, 이은 현재스베저환블스다장니됩록경에레드라는 로 되어 ._NT_TIB(신호 정보 블록).디버거를 사용할 수 있는 경우 다음과 같이 표시됩니다.

0:000> dt ntdll!_teb @$teb nttib.
   +0x000 NtTib  : 
      +0x000 ExceptionList : 0x0012e030 _EXCEPTION_REGISTRATION_RECORD
      +0x004 StackBase : 0x00130000 
      +0x008 StackLimit : 0x0011e000 
      +0x00c SubSystemTib : (null) 
      +0x010 FiberData : 0x00001e00 
      +0x010 Version : 0x1e00
      +0x014 ArbitraryUserPointer : (null) 
      +0x018 Self   : 0x7ffdf000 _NT_TIB

StackLimit 특성은 필요에 따라 업데이트됩니다.이 메모리 블록의 특성을 확인하면 다음과 유사한 내용이 표시됩니다.

0:000> !address 0x0011e000 
    00030000 : 0011e000 - 00012000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000004 PAGE_READWRITE
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageStack
                    Pid.Tid  abc.560

그리고 그 옆에 있는 페이지를 확인하면 가드 속성이 나타납니다.

0:000> !address 0x0011e000-1000
    00030000 : 0011d000 - 00001000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000104 PAGE_READWRITE | PAGE_GUARD
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageStack
                    Pid.Tid  abc.560

도움이 되길 바랍니다.

예외 처리기는 최소한의 스택을 처리해야 하므로(일반적으로 단일 페이지만 이 용도로 예약됨) 스택 오버플로는 처리하기에 가장 유해한 예외 유형일 수 있습니다.

이러한 유형의 예외 처리 문제에 대한 흥미로운 토론은 Chris Brumme의 블로그 게시물 1과 2를 참조하십시오. 블로그 게시물은 .NET 관점에서 문제, 특히 CLR 호스팅에 초점을 맞춥니다.

일부 컴파일러는 스택의 남은 여유 공간을 반환하는 stackavail() 함수를 지원합니다.프로그램에서 스택 공간이 많이 필요한 함수를 호출하기 전에 이 기능을 사용하여 호출해도 안전한지 확인할 수 있습니다.

Linux를 사용하는 경우 대체 신호 스택을 사용하는 것이 좋습니다.

  1. 이 경우 모든 신호는 대체 스택을 통해 처리됩니다.
  2. 스택 오버플로가 발생하면 시스템에서 SEGV 신호를 생성하며, 이 신호는 대체 스택을 통해 처리할 수 있습니다.
  3. 사용하지 않을 경우...그러면 신호를 처리할 수 없을 수 있으며 프로그램이 처리/오류 없이 충돌할 수 있습니다.

Visual Studio에서 editbin을 사용하여 스택 크기를 변경할 수 있습니다. 정보는 msdn.microsoft.com/en-us/library/35yc2tc3.aspx 에서 찾을 수 있습니다.

언급URL : https://stackoverflow.com/questions/199747/how-to-detect-possible-potential-stack-overflow-problems-in-a-c-c-program