"스택 오버플로"는 어떻게 발생하며 어떻게 방지합니까?
스택 오버플로는 어떻게 발생하고 발생하지 않도록하는 가장 좋은 방법은 무엇이며 특히 웹 서버에서이를 방지하는 방법은 무엇입니까?하지만 다른 예도 흥미로울까요?
스택
이 컨텍스트에서 스택은 프로그램이 실행되는 동안 데이터를 배치하는 마지막 인선 출 버퍼입니다. LIFO (Last in, First Out)는 마지막으로 넣은 것이 항상 가장 먼저 다시 나오는 것을 의미합니다. 스택에 2 개의 항목 ( 'A', 'B'를 차례로 밀어 넣으면 가장 먼저 터지는 것) 스택에서 'B'가되고 다음은 'A'입니다.
코드에서 함수를 호출하면 함수 호출 후 다음 명령어가 스택에 저장되고 함수 호출로 덮어 쓸 수있는 모든 저장 공간이 저장됩니다. 호출하는 함수는 자체 지역 변수에 대해 더 많은 스택을 사용할 수 있습니다. 완료되면 사용한 로컬 변수 스택 공간을 비운 다음 이전 함수로 돌아갑니다.
스택 오버플로
스택 오버플로는 프로그램에서 사용하는 것보다 더 많은 메모리를 스택에 사용했을 때 발생합니다. 임베디드 시스템에서는 스택에 256 바이트 만있을 수 있으며 각 함수가 32 바이트를 차지하는 경우 함수 호출 8 개만 가질 수 있습니다. 함수 1은 함수 4를 호출하는 함수 3을 호출하는 함수 2를 호출합니다. 함수 9를 호출하는 함수 8이지만 함수 9는 스택 외부의 메모리를 덮어 씁니다. 메모리, 코드 등을 덮어 쓸 수 있습니다.
많은 프로그래머는 함수 A를 호출하여 함수 B를 호출 한 다음 함수 C를 호출 한 다음 함수 A를 호출함으로써 이러한 실수를 저지 릅니다. 대부분의 경우 작동 할 수 있지만 잘못된 입력이 한 번만 입력하면 해당 서클에 영원히 포함됩니다. 컴퓨터가 스택이 과도하다는 것을 인식 할 때까지.
재귀 함수도 이에 대한 원인이되지만 재귀 적으로 작성하는 경우 (즉, 함수 자체 호출)이를 인식하고 정적 / 전역 변수를 사용하여 무한 재귀를 방지해야합니다.
일반적으로 사용중인 OS와 프로그래밍 언어가 스택을 관리하며 손이 닿지 않습니다. 함수 호출의 깊이를 확인하고 의도하지 않은 순환과 재귀를 감지하려면 호출 그래프 (각 함수가 호출하는 내용을 기본에서 보여주는 트리 구조)를 확인해야합니다. 의도적 인 순환과 재귀는 서로를 너무 많이 호출하면 오류가 발생하도록 인위적으로 검사해야합니다.
좋은 프로그래밍 관행, 정적 및 동적 테스트 외에 이러한 높은 수준의 시스템에서 할 수있는 일은 많지 않습니다.
임베디드 시스템
임베디드 세계, 특히 고 신뢰성 코드 (자동차, 항공기, 우주)에서 광범위한 코드 검토 및 검사를 수행하지만 다음 작업도 수행합니다.
- 재귀 및주기 금지-정책 및 테스트에 의해 시행
- 코드와 스택을 멀리 떨어져 유지하십시오 (플래시 코드, RAM 스택, 두 사람이 만나지 않음).
- 스택 주위에 가드 밴드를 배치합니다. 매직 넘버 (일반적으로 소프트웨어 인터럽트 명령이지만 여기에는 많은 옵션이 있음)로 채우는 빈 메모리 영역이 있으며, 1 초에 수백 또는 수천 번 가드 밴드를 확인하여 확인합니다. 덮어 쓰지 않았습니다.
- 메모리 보호 사용 (즉, 스택에서 실행 안 함, 스택 외부에서 읽기 또는 쓰기 안 함)
- 인터럽트는 2 차 함수를 호출하지 않습니다. 플래그를 설정하고, 데이터를 복사하고, 애플리케이션이 처리하도록합니다 (그렇지 않으면 함수 호출 트리에서 8 개 깊이를 얻고 인터럽트가 발생한 다음 내부에있는 다른 몇 가지 함수로 이동할 수 있습니다.) 중단, 폭발 유발). 여러 호출 트리가 있습니다. 하나는 주 프로세스 용이고 하나는 각 인터럽트 용입니다. 당신의 인터럽트가 서로를 방해 할 수 있다면 ... 글쎄, 드래곤이 있습니다 ...
고급 언어 및 시스템
그러나 고급 언어에서는 운영 체제에서 실행됩니다.
- 로컬 변수 저장소를 줄입니다 (로컬 변수는 스택에 저장됩니다. 컴파일러는 이에 대해 꽤 똑똑하고 호출 트리가 얕은 경우 힙에 큰 로컬을 배치 할 수도 있습니다)
- 재귀를 피하거나 엄격하게 제한
- 프로그램을 더 작고 작은 함수로 너무 많이 나누지 마십시오. 지역 변수를 계산하지 않아도 각 함수 호출은 스택에서 64 바이트를 소비합니다 (32 비트 프로세서, CPU 레지스터, 플래그의 절반 절약 등).
- 호출 트리를 얕게 유지합니다 (위의 설명과 유사).
웹 서버
스택을 제어하거나 볼 수 있는지 여부는 보유한 '샌드 박스'에 따라 다릅니다. 웹 서버를 다른 높은 수준의 언어 및 운영 체제처럼 취급 할 수있는 가능성이 높습니다. 대부분 손에서 벗어 났지만 사용중인 언어와 서버 스택을 확인하십시오. 이다 예를 들어, SQL 서버의 스택을 날려 버릴 수있다.
-아담
실제 코드에서 스택 오버플로는 거의 발생하지 않습니다. 그것이 발생하는 대부분의 상황은 종료가 잊혀진 재귀입니다. 그러나 매우 중첩 된 구조, 예를 들어 특히 큰 XML 문서에서는 거의 발생하지 않을 수 있습니다. 여기서 유일한 도움은 호출 스택 대신 명시 적 스택 객체를 사용하도록 코드를 리팩터링하는 것입니다.
대부분의 사람들은 종료 경로가없는 재귀로 스택 오버플로가 발생한다고 말할 것입니다.하지만 대부분의 경우 충분한 데이터 구조로 작업하는 경우 적절한 재귀 종료 경로가 도움이되지 않습니다.
이 경우 몇 가지 옵션 :
무한 재귀는 스택 오버플로 오류를 얻는 일반적인 방법입니다. 방지하기 위해 - 항상 확인하는 것은 반드시 출구 경로있을 것입니다 타격을받을. :-)
스택 오버플로를 얻는 또 다른 방법 (적어도 C / C ++에서는)은 스택에 엄청난 변수를 선언하는 것입니다.
char hugeArray[100000000];
그렇게하겠습니다.
Jeff와 Joel이 기술 질문에 대한 답을 얻을 수있는 더 나은 장소를 제공하고자 할 때 스택 오버플로가 발생합니다. 이 스택 오버플로를 방지하기에는 너무 늦었습니다. 그 "다른 사이트"는 은밀하지 않음으로써이를 막을 수있었습니다. ;)
일반적으로 스택 오버플로는 무한 재귀 호출의 결과입니다 (요즘 표준 컴퓨터의 일반적인 메모리 양을 고려할 때).
메서드, 함수 또는 프로 시저를 호출 할 때 "표준"방식 또는 호출은 다음으로 구성됩니다.
- Pushing the return direction for the call into the stack(that's the next sentence after the call)
- Usually the space for the return value get reserved into the stack
- Pushing each parameter into the stack (the order diverges and depends on each compiler, also some of them are sometimes stored on the CPU registers for performance improvements)
- Making the actual call.
So, usually this takes a few bytes depeding on the number and type of the parameters as well as the machine architecture.
You'll see then that if you start making recursive calls the stack will begin to grow. Now, stack is usually reserved in memory in such a way that it grows in opposite direction to the heap so, given a big number of calls without "coming back" the stack begins to get full.
Now, on older times stack overflow could occur simply because you exausted all available memory, just like that. With the virtual memory model (up to 4GB on a X86 system) that was out of the scope so usually, if you get an stack overflow error, look for an infinite recursive call.
What? Nobody has any love for those cased by an infinite loop?
do
{
JeffAtwood.WritesCode();
} while(StackOverflow.MakingMadBank.Equals(false));
Aside from the form of stack overflow that you get from a direct recursion (eg Fibonacci(1000000)
), a more subtle form of it that I have experienced many times is an indirect recursion, where a function calls another function, which calls another, and then one of those functions calls the first one again.
This can commonly occur in functions that are called in response to events but which themselves may generate new events, for example:
void WindowSizeChanged(Size& newsize) {
// override window size to constrain width
newSize.width=200;
ResizeWindow(newSize);
}
In this case the call to ResizeWindow
may cause the WindowSizeChanged()
callback to be triggered again, which calls ResizeWindow
again, until you run out of stack. In situations like these you often need to defer responding to the event until the stack frame has returned, eg by posting a message.
Considering this was tagged with "hacking", I suspect the "stack overflow" he's referring to is a call stack overflow, rather than a higher level stack overflow such as those referenced in most other answers here. It doesn't really apply to any managed or interpreted environments such as .NET, Java, Python, Perl, PHP, etc, which web apps are typically written in, so your only risk is the web server itself, which is probably written in C or C++.
Check out this thread:
https://stackoverflow.com/questions/7308/what-is-a-good-starting-point-for-learning-buffer-overflow
참고URL : https://stackoverflow.com/questions/26158/how-does-a-stack-overflow-occur-and-how-do-you-prevent-it
'programing tip' 카테고리의 다른 글
Perl에서 << 'm'= ~ m >> 구문은 무엇을 의미합니까? (0) | 2020.09.25 |
---|---|
Android Studio- 이미 존재하는 프로그램 유형 : com.google.android.gms.internal.measurement.zzwp (0) | 2020.09.25 |
Swift에서 HTTP 요청 + 기본 인증을 만드는 방법 (0) | 2020.09.24 |
Prototype.js를 사용한 JSON.stringify () 배열 기괴함 (0) | 2020.09.24 |
C #에서 null이 아닌 경우 메서드 호출 (0) | 2020.09.24 |