programing

스택 오버플로로 인해 세그먼트화 오류가 아닌 다른 문제가 발생할 수 있습니까?

lastcode 2023. 9. 19. 21:11
반응형

스택 오버플로로 인해 세그먼트화 오류가 아닌 다른 문제가 발생할 수 있습니까?

컴파일된 프로그램에서 (C 또는 C++라고 하지만, 이 질문은 콜 스택이 있는 모든 비 VM-ish 언어로 확장될 수 있다고 생각합니다) 스택을 오버플로하면 세그먼트화 오류가 발생하는 경우가 매우 많습니다.

스택 오버플로가 [a] 원인이고 분할 오류가 결과입니다.

그래도 항상 그렇습니까?스택 오버플로로 인해 다른 종류의 프로그램/OS 동작이 발생할 수 있습니까?

리눅스가 아닌 것, 윈도가 아닌 OS와 X86이 아닌 하드웨어에 대해서도 물어봅니다. (물론 하드웨어 메모리 보호나 이를 위한 OS 지원(예: MS-DOS)이 없다면 분할 오류 같은 것은 없습니다. 분할 오류가 발생할 도 있지만 다른 문제가 발생할 수도 있는 경우에 대해 문의하는 것입니다.

참고: 스택 오버플로를 제외하고 프로그램이 유효하고 범위를 벗어난 배열에 액세스하려고 하지 않으며 잘못된 포인터를 참조하지 않는다고 가정합니다.

예, 표준 OS(Linux)와 표준 하드웨어(x86)에서도 가능합니다.

void f(void) {
    char arr[BIG_NUMBER];
    arr[0] = 0; // stack overflow
}

x86에서는 스택이 다운되므로 오버플로를 트리거하기 위해 배열의 처음에 할당합니다.일반적인 면책 조항이 적용됩니다.정확한 동작은 당신의 C 컴파일러의 세부사항을 포함하여 이 답변에서 논의된 것보다 더 많은 요소에 따라 달라집니다.

BIG_NUMBER가 오버플로가 될 정도로 크기가 작으면 스택 가드와 충돌하여 세그먼트화 오류가 발생합니다.이것이 스택 가드의 목적이며, 4 KiB 페이지 하나만큼 작을 수도 있고(단, 더 작지 않을 수도 있으며, 이 4 KiB 크기는 Linux 4.12 이전에 사용됨), 더 클 수도 있습니다(Linux 4.12에서는 1 MiB 기본값, mm:스택 가드 간격 참조). 그러나 항상 특정 크기입니다.

BIG_NUMBER가 충분히 크면 오버플로가 스택 가드를 건너뛰고 유효한 다른 메모리(잠재적으로 유효한 메모리)에 도달할 수 있습니다.이렇게 하면 프로그램이 잘못 작동하지만 충돌하지 않을 수 있습니다. 이는 기본적으로 최악의 경우입니다. 프로그램이 잘못되었을 때 의도하지 않은 작업을 수행하는 것이 아니라 충돌하기를 원합니다.

한 가지는 스택을 오버플로할 때 런타임에 발생하는 일이며, 이는 많은 것이 될 수 있습니다.세그먼트 오류를 포함하지만 이에 국한되지는 않습니다. 오버플로가 발생하는 변수를 덮어쓰는 것으로, 불법 지시를 초래할 뿐만 아니라 그 이상도 마찬가지입니다."오래된" 고전적인 논문인 Smashing The Stack For Fun and Profit은 사람들이 이런 것들로 "재미"를 느낄 수 있는 많은 방법들을 설명합니다.

또 하나는 컴파일 타임에 일어날 수 있는 일입니다.C와 C++ 모두에서 배열 너머에 쓰거나 스택 크기를 초과하는 것은 정의되지 않은 동작이며, 프로그램이 UB를 포함할 때 컴파일러는 기본적으로 자신이 원하는 모든 부분을 자유롭게 수행할 수 있습니다.그리고 현대의 컴파일러들은 최적화 목적으로 UB를 이용하는 데 매우 적극적이 되고 있습니다. 종종 UB가 결코 일어나지 않는다고 가정함으로써 UB를 포함하는 코드를 단순히 제거하도록 유도하거나 대안이 UB를 야기하기 때문에 분기가 항상 또는 결코 취해지지 않게 합니다.때때로 컴파일러는 시간 이동을 도입하거나 소스 코드에서 호출되지 않은 함수를 호출할 수 있으며 런타임 동작을 정말 혼란스럽게 할 수 있는 많은 다른 것들도 있습니다.

참고 항목:

정의되지 않은 동작 #1/3에 대해 모든 C 프로그래머가 알아야 할 것

정의되지 않은 행동 #2/3에 대해 모든 C 프로그래머가 알아야 할 것

정의되지 않은 행동 #3/3에 대해 모든 C 프로그래머가 알아야 할 것

C와 C++에서 정의되지 않은 행동에 대한 안내서 1부

C와 C++에서 정의되지 않은 행동에 대한 가이드 2부

C와 C++에서 정의되지 않은 행동에 대한 가이드 3부

다른 답변들은 PC 쪽을 상당히 잘 다루고 있습니다.임베디드 세계의 몇 가지 이슈를 다루겠습니다.

임베디드 코드에는 segfault와 유사한 것이 있습니다.코드는 일종의 비휘발성 스토리지에 저장됩니다(요즘은 대개 플래시이지만 과거에는 ROM이나 PROM 같은 것).이를 설정하려면 특별한 작업이 필요합니다. 일반적인 메모리 액세스는 이 작업에서 읽을 수는 있지만 쓸 수는 없습니다.게다가 임베디드 프로세서는 대개 메모리 맵에 큰 틈이 있습니다.프로세서가 읽기 전용인 메모리에 대한 쓰기 요청을 받거나 물리적으로 존재하지 않는 주소에 대한 읽기 또는 쓰기 요청을 받으면 프로세서는 일반적으로 하드웨어 예외를 발생시킵니다.디버거가 연결되어 있으면 코어 덤프와 같이 시스템의 상태를 확인하여 무엇이 잘못되었는지 찾을 수 있습니다.

스택 오버플로에 대해 이런 일이 발생한다는 보장은 없습니다.스택은 RAM의 모든 위치에 배치할 수 있으며, 이는 일반적으로 다른 변수와 함께 배치됩니다.스택 오버플로의 결과는 일반적으로 해당 변수를 손상시킵니다.

응용 프로그램에서 힙(동적 할당)도 사용하는 경우 해당 섹션의 맨 아래에서 스택이 시작되어 위쪽으로 확장되고 힙이 해당 섹션의 맨 위에서 시작되어 아래쪽으로 확장되는 메모리 섹션을 할당하는 것이 일반적입니다.이것은 동적으로 할당된 데이터가 첫 번째 사상자가 된다는 것을 의미합니다.

운이 나쁘면 언제 그런 일이 발생하는지조차 눈치채지 못할 수도 있는데, 그러면 코드가 제대로 작동하지 않는 이유를 알아내야 합니다.가장 아이러니한 경우, 덮어쓰기 중인 데이터가 포인터라면 포인터가 잘못된 메모리에 액세스하려고 할 때 하드웨어 예외가 발생할 수 있지만, 이는 스택 오버플로가 발생한 후 시간이 다소 걸릴 것이며, 일반적으로 코드에 버그가 있다고 가정합니다.

Embedded code는 이 문제를 해결하기 위한 공통 패턴을 가지고 있으며, 이는 모든 바이트를 알려진 값으로 초기화하여 스택을 "워터마크"하는 것입니다.컴파일러가 이 작업을 수행할 수도 있고, 메인() 이전에 시동 코드에 직접 구현해야 할 수도 있습니다.스택 끝에서 뒤를 돌아보면 스택이 더 이상 이 값으로 설정되지 않은 위치, 스택 사용에 대한 고수분 표시를 알고 있거나, 모든 값이 올바르지 않은 경우 오버플로가 있음을 알 수 있습니다.임베디드 애플리케이션은 백그라운드 작업으로 이를 지속적으로 폴링하고 진단 목적으로 보고하는 것이 일반적입니다.

스택 사용을 추적할 수 있게 된 대부분의 회사는 오버플로우를 방지하기 위해 허용 가능한 최악의 경우 마진을 설정할 것입니다.이는 일반적으로 75%에서 90% 사이이지만 항상 여유가 있을 것입니다.이를 통해 아직 보지 못한 최악의 상황이 발생할 가능성이 있을 뿐만 아니라 스택을 더 많이 사용하는 새 코드를 추가해야 할 때 향후 개발에 더욱 용이해질 수 있습니다.

스택 오버플로는 프로그램의 정의되지 않은 동작에 대한 많은 이유 중 하나입니다.이 경우 예상되는 결과나 분할 오류가 발생하거나 하드 디스크가 지워지는 등의 문제가 발생할 수 있습니다.정의되지 않은 동작이므로 정의된 동작을 기대하지 마십시오.

언급URL : https://stackoverflow.com/questions/50728345/can-a-stack-overflow-result-in-something-other-than-a-segmentation-fault

반응형