운영 체제는 스택이나 기타 항목을 위해 고정된 양의 유효 가상 공간을 예약합니까? 큰 지역 변수를 사용하는 것만으로도 스택 오버플로를 생성할 수 있나요?
나는 내 가설을 테스트하기 위해 작은 C
프로그램을 작성했습니다. X86-64 CentOS 6.5에서 실행됩니다.
#include <string.h>
#include <stdio.h>
int main()
{
int n = 10240 * 1024;
char a[n];
memset(a, 'x', n);
printf("%x\n%x\n", &a[0], &a[n-1]);
getchar();
return 0;
}
프로그램을 실행 &a[0] = f0ceabe0
하면&a[n-1] = f16eabdf
proc 맵은 스택을 보여줍니다.7ffff0cea000-7ffff16ec000. (10248 * 1024B)
그럼 늘리려고 노력해요n = 11240 * 1024
프로그램을 실행 &a[0] = b6b36690
하면&a[n-1] = b763068f
proc 맵은 스택을 보여줍니다.7fffb6b35000-7fffb7633000. (11256 * 1024B)
ulimit -s
내 컴퓨터에서 인쇄하세요 10240
.
보시다시피, 두 경우 모두 스택 크기가 ulimit -s
주어진 크기보다 큽니다. 그리고 지역 변수가 커지면 스택도 커집니다. 스택의 상단은 3-5kB 정도 적습니다 &a[0]
(내가 아는 한 빨간색 영역은 128B입니다).
그렇다면 이 스택 맵은 어떻게 할당됩니까?
답변1
스택 메모리 제한이 할당되지 않은 것 같습니다(어쨌든 무한히 스택할 수는 없습니다).https://www.kernel.org/doc/Documentation/vm/overcommit-accounting설명하다:
C 언어 스택은 암시적 mremap을 통해 증가합니다. 절대적인 보장을 원하고 가장자리에 가깝게 실행하려면 필요하다고 생각되는 가장 큰 크기로 스택을 매핑해야 합니다. 일반적인 스택 사용의 경우 이는 중요하지 않지만 실제로 관심이 있는 경우에는 극단적인 경우입니다.
그러나 스택 매핑은 컴파일러의 대상이 됩니다(옵션이 있는 경우).
편집: x84_64 Debian 시스템에서 몇 가지 테스트를 수행한 후 (Stack Overflow에 따라 strace
) 시스템 호출 없이 스택이 커지는 것을 발견했습니다. 따라서 이는 커널이 프로세스에서 명시 mmap
적으로 성장하지 않고 자동으로 성장한다는 것을 의미합니다(위에서 "암시적으로"라는 의미) .mremap
이를 확인하는 세부 정보를 찾기가 어렵습니다. 나는 추천한다Linux 가상 메모리 관리자 이해멜 고먼 지음. 내 생각에 대답은 섹션 4.6.1에 있습니다.페이지 오류 처리, 예외는 "지역이 유효하지 않지만 확장 가능한 영역(예: 스택) 옆에 있습니다." 및 해당 작업 "영역 확장 및 페이지 할당"입니다. D.5.2도 참조하세요.확장 스택.
Linux 메모리 관리에 대한 기타 참고 자료(스택에는 거의 없음):
- 메모리 FAQ
- 모든 프로그래머가 메모리에 대해 알아야 할 사항저자: 울리히 드레이퍼
편집 2: 이 구현에는 단점이 있습니다. 극단적인 경우 스택이 제한보다 크더라도 스택 힙 충돌이 감지되지 않을 수 있습니다! 그 이유는 스택의 변수에 대한 쓰기가 할당된 힙 메모리에서 끝날 수 있기 때문입니다. 이 경우 페이지 오류가 발생하지 않으며 커널은 스택을 확장해야 한다는 것을 알 수 없습니다. 토론에서 내 예를 참조하십시오.GNU/Linux에서 자동 스택 충돌나는 gcc-help 목록부터 시작했습니다. 이를 방지하려면 컴파일러는 함수가 호출될 때 일부 코드를 추가해야 합니다. 이는 GCC를 통해 수행할 수 있습니다 -fstack-check
(자세한 내용은 Ian Lance Taylor의 답변 및 GCC 매뉴얼 페이지 참조).
답변2
리눅스 커널 4.2
- mm/mmap.c#acct_stack_growth세그폴트 발생 여부를 결정합니다.
rlim[RLIMIT_STACK]
POSIX 대응물을 사용합니다.gerlimit(RLIMIT_STACK)
- 아치/x86/mm/fault.c#do_page_fault궁극적으로 호출하는 체인을 시작하는 인터럽트 핸들러입니다.
acct_stack_growth
- 아치/x86/entry/entry_64.S페이지 폴트 핸들러를 설정합니다. 이 섹션을 이해하려면 페이지 매김에 대해 알아야 합니다.x86 페이징은 어떻게 작동하나요? |스택 오버플로
최소 테스트 프로그램
그런 다음 최소 NASM 64비트 프로그램을 사용하여 테스트할 수 있습니다.
global _start
_start:
sub rsp, 0x7FF000
mov [rsp], rax
mov rax, 60
mov rdi, 0
syscall
ASLR을 끄고 환경 변수를 제거하십시오. 이러한 변수는 스택에 저장되어 공간을 차지하게 됩니다.
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
env -i ./main.out
한도는 내 것보다 약간 낮습니다 ulimit -s
(나의 경우 8MiB). 이는 환경 외에도 처음에 스택에 배치되는 추가 System V 관련 데이터 때문인 것 같습니다.어셈블리에서 Linux 64 명령줄 인수 스택 오버플로
이것을 진지하게 받아들이면 TODO최소한의 initrd 이미지 만들기스택의 맨 위에서 쓰기 시작하여 적어 둔 다음QEMU + GDB를 사용하여 실행. dprintf
루프에 인쇄 스택 주소를 배치하고 에 중단점을 배치합니다 acct_stack_growth
. 그것은 훌륭할 것입니다.
관련된:
답변3
기본적으로 최대 스택 크기는 프로세스당 8MB로 구성되지만
다음 명령을 사용하여 변경할 수 있습니다 ulimit
.
기본값 표시(kB):
$ ulimit -s
8192
무제한으로 설정:
ulimit -s unlimited
현재 셸, 하위 셸 및 해당 하위 프로세스에 영향을 줍니다.
( ulimit
쉘 내장 명령입니다)
다음 명령을 사용하면 사용 중인 실제 스택 주소 범위를 표시할 수 있습니다.
cat /proc/$PID/maps | grep -F '[stack]'
Linux의 경우.