Linux에서 C의 스택 오버플로를 예측할 수 있습니까?
문제
x86 Linux 시스템에서 스택 오버플로를 일으킬 수 있는 특정 조건이 있습니다.
struct my_big_object[HUGE_NUMBER]
스택에.그것을 통해 걷는 것은 결국SIGSEGV
.- 그만큼
alloca()
루틴(예:malloc()
, 그러나 스택을 사용하고 자동으로 해제되며 다음과 같이 폭발합니다.SIGSEGV
너무 큰 경우). 업데이트:alloca()는 내가 원래 언급한 것처럼 공식적으로 더 이상 사용되지 않습니다.단지 낙담했을 뿐이야.
로컬 스택이 주어진 객체에 비해 충분히 큰지 프로그래밍 방식으로 감지하는 방법이 있습니까?나는 스택 크기가 다음을 통해 조정 가능하다는 것을 알고 있습니다. ulimit
, 그래서 방법이 있기를 바랍니다(그러나 이식성이 없을 수도 있음).이상적으로는 다음과 같은 작업을 수행하고 싶습니다.
int min_stack_space_available = /* ??? */;
if (object_size < min_stack_space_available)
{
char *foo = alloca(object_size);
do_stuff(foo);
}
else
{
char *foo = malloc(object_size);
do_stuff(foo);
free(foo);
}
해결책
프로세스 '스택 공간의 크기를 찾은 다음 사용 된 양을 빼서 프로세스가 사용할 수있는 스택 공간을 결정할 수 있습니다.
ulimit -s
Linux 시스템의 스택 크기를 보여줍니다. 프로그래밍 방식의 접근 방식은 확인하십시오 getrlimit (). 그런 다음 현재 스택 깊이를 결정하려면 스택 상단에 포인터를 한쪽에서 하단으로 빼냅니다. 예를 들어 (코드 테스트되지 않은) :
unsigned char *bottom_of_stack_ptr;
void call_function(int argc, char *argv) {
unsigned char top_of_stack;
unsigned int depth = (&top_of_stack > bottom_of_stack_ptr) ?
&top_of_stack-bottom_of_stack_ptr :
bottom_of_stack_ptr-&top_of_stack;
if( depth+100 < PROGRAMMATICALLY_DETERMINED_STACK_SIZE ) {
...
}
}
int main(int argc, char *argv) {
unsigned char bottom_of_stack;
bottom_of_stack_ptr = &bottom_of_stack;
my_function();
return 0;
}
다른 팁
더 이상 사용되지 않은 Alloca () 루틴 (Malloc ()와 같은 스택을 사용하고 자동으로 해방되며 Sigsegv가 너무 큰 경우 SIGSEGV로 날려 버립니다).
Alloca가 더 이상 사용되지 않는 이유는 무엇입니까?
어쨌든, 당신의 경우에 Alloca vs Malloc은 얼마나 빠릅니까? (그만한 가치가 있습니까?)
공간이 충분하지 않으면 Alloca에서 널 되돌아 가지 않습니까? (Malloc과 마찬가지로?)
그리고 코드가 충돌하면 어디에서 충돌합니까? Alloca에 있습니까 아니면 Dostuff ()에 있습니까?
/요한
이것이 Linux에서 적용되는지 확실하지 않지만 Windows에서는 대규모 스택 할당으로 액세스 위반에 들어갈 수 있습니다. 그들이 성공하더라도!
기본적으로 Windows의 VMM은 실제로 스택 액세스가 일반적으로 아래쪽으로 행진한다고 믿기 때문에 실제로 상위 몇 대 (정확히 얼마나 많은지 확실하지 않음) 4096 바이트의 스택 램 페이지를 pagable으로 표시하기 때문입니다. 상단; 액세스가 현재 "경계"에 가까워지면서 하위 및 하위 페이지는 Pagable으로 표시됩니다. 그러나 이것은 메모리가 아직 할당되지 않았기 때문에 스택 상단보다 훨씬 아래에있는 초기 메모리 읽기/쓰기가 액세스 위반을 트리거한다는 것을 의미합니다!
Alloca ()는 실패시 Null을 반환 할 것입니다. Alloca (0)의 동작은 정의되지 않은 플랫폼 변형이라고 생각합니다. do_something () 전에 그것을 확인하면 segv에 맞지 않아야합니다.
몇 가지 질문이 있습니다.
- 왜, 왜, 당신은 스택에 큰 무언가가 필요합니까? 대부분의 시스템의 기본 크기는 8m이므로 여전히 너무 작습니까?
- Alloca () 블록을 호출하는 함수가 Mlock () / mlockall ()을 통해 동일한 양의 힙을 보호하는 경우 시간이 지남에 따라 동일한 액세스 성능 (예 : "Do n't Swap Me, Bro!")에 가깝게 보장합니까? 보다 공격적인 'RT'스케줄러를 사용하는 경우 어쨌든 전화하는 것이 좋습니다.
문제는 흥미롭지 만 눈썹을 높입니다. 그것은 내 정사각형 끈 끈-O- 미터의 바늘을 올립니다.
스택에 할당하는 이유에 대해별로 말하지는 않지만 매력적인 스택 메모리 모델이라면 힙에 스택 할당도 구현 될 수 있습니다. 프로그램의 시작 부분에 큰 메모리 덩어리를 할당하고 일반 스택의 프레임에 해당하는 포인터 스택을 유지하십시오. 함수가 반환 될 때 개인 스택 포인터를 팝업하는 것을 기억하면됩니다.
예를 들어 여러 컴파일러 Watcom C/C ++를 열었습니다, 정확히 할 수있는 stackavail () 함수를 지원합니다.
당신이 사용할 수있는 GNU libsigsegv
에게 핸들 스택 오버플로가 발생하는 경우를 포함한 페이지 오류(웹 사이트에서):
일부 응용 프로그램에서는 스택 오버플로 처리기가 일부 정리를 수행하거나 사용자에게 알린 다음 즉시 응용 프로그램을 종료합니다.다른 애플리케이션에서는 스택 오버플로 처리기 longjmp가 애플리케이션의 중앙 지점으로 돌아갑니다.이 라이브러리는 두 가지 용도를 모두 지원합니다.두 번째 경우, 핸들러는 일반 신호 마스크를 복원해야 하며(핸들러가 실행되는 동안 많은 신호가 차단되기 때문에) 제어권을 전달하려면 sigsegv_leave_handler()도 호출해야 합니다.그런 다음에만 longjmp할 수 있습니다.
Alloca 기능은입니다 ~ 아니다 감가 상각 된. 그러나 POSIX에 있지 않으며 기계 및 컴파일러에 따라 다릅니다. Alloca의 Linux Man-Page는 "특정 응용 프로그램의 경우 Malloc 사용에 비해 효율성을 향상시킬 수 있으며 특정 경우 Longjmp () 또는 Siglongjmp ()를 사용하는 응용 프로그램에서 메모리 거래를 단순화 할 수 있다고 지적합니다. 그것의 사용은 낙담합니다. "
인력은 또한 "스택 프레임을 확장 할 수없는 경우 오류 표시가 없다고 말합니다. 그러나 할당 실패 후 프로그램은 SIGSEGV를받을 가능성이 높습니다."
Malloc의 성능은 실제로 언급되었습니다 stackoverflow 팟 캐스트 #36.
(나는 이것이 당신의 질문에 대한 적절한 답이 아니라는 것을 알고 있지만 어쨌든 그것이 유용 할 것이라고 생각했습니다.)
이것이 당신의 질문에 대한 직접적인 답이 아니더라도, 나는 당신이 존재한다는 것을 알고 있기를 바랍니다. Valgrind - Linux에서 런타임에서 이러한 문제를 감지하기위한 훌륭한 도구.
스택 문제와 관련하여 이러한 오버플로를 감지하는 고정 풀에서 동적으로 객체를 할당 할 수 있습니다. 간단한 거대-와이즈 드라이를 사용하면 릴리스 시간에 실제 코드가 실행되면서 디버그 시간 에이 실행을 할 수 있으므로 (적어도 실행중인 시나리오에 대해) 너무 많이 복용하지 않는다는 것을 알 수 있습니다. 다음은 더 많은 정보와 링크입니다 샘플 구현에.
내가 생각할 수있는 좋은 방법은 없습니다. getRlimit () (이전 제안)와 일부 포인터 산술을 사용하여 가능할 수 있습니까? 그러나 먼저 당신이 정말로 이것을 원하는지 스스로에게 물어보십시오.
void *closeToBase; main () { int closeToBase; stackTop = &closeToBase; } int stackHasRoomFor(int bytes) { int currentTop; return getrlimit(...) - (¤tTop - closeToBase) > bytes + SomeExtra; }
개인적으로, 나는 이것을하지 않을 것입니다. 힙에 큰 것을 할당하면 스택은 그 의미가 아닙니다.
스택 영역의 끝은 OS에 의해 동적으로 결정됩니다. 가상 메모리 영역 (VMA)을 OS의 종속 방식으로 보면 스택의 "정적"경계를 찾을 수 있지만 (StackVma* 파일 참조 libsigsegv/src/), 당신은 추가로 고려해야합니다
- getrlimit 값,
- 스레드 당 스택 크기 (참조 pthread_getStacksize)
이것이 명백하다면 사과하지만, 해당 크기의 Alloca를 시도하고 스택 오버 플로우 예외를 포착하여 특정 스택 할당 크기를 테스트하는 기능을 쉽게 작성할 수 있습니다. 원한다면 함수 스택 오버 헤드에 대한 사전 결정된 수학으로 기능에 넣을 수 있습니다. 예 :
bool CanFitOnStack( size_t num_bytes )
{
int stack_offset_for_function = 4; // <- Determine this
try
{
alloca( num_bytes - stack_offset_for_function );
}
catch ( ... )
{
return false;
}
return true;
}