최소 스택 할당

최소 스택 할당

저는 ELF 사양을 작업 중입니다(http://www.skyfree.org/linux/references/ELF_Format.pdf), 프로그램 로딩 프로세스에 대해 내가 모르는 한 가지는 스택이 초기화되는 방법과 초기 페이지 크기가 무엇인지입니다. 테스트는 다음과 같습니다(Ubuntu x86-64).

$ cat test.s
.text
  .global _start
_start:
  mov $0x3c,%eax
  mov $0,%edi
  syscall
$ as test.s -o test.o && ld test.o
$ gdb a.out -q
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) b _start
Breakpoint 1 at 0x400078
(gdb) run
Starting program: ~/a.out 

Breakpoint 1, 0x0000000000400078 in _start ()
(gdb) print $sp
$1 = (void *) 0x7fffffffdf00
(gdb) info proc map
process 20062
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
            0x400000           0x401000     0x1000        0x0 ~/a.out
      0x7ffff7ffa000     0x7ffff7ffd000     0x3000        0x0 [vvar]
      0x7ffff7ffd000     0x7ffff7fff000     0x2000        0x0 [vdso]
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]

ELF 사양에서는 이 스택 페이지가 처음에 어떻게, 왜 존재하는지에 대해 거의 설명하지 않지만, argv, envp 및 위의 보조 벡터를 사용하여 argc를 가리키는 SP로 스택을 초기화해야 한다는 일부 참조를 찾을 수 있습니다. 제가 확인했습니다. 알겠습니다. 그런데 SP 아래에는 얼마나 많은 여유 공간이 남아 있습니까? 내 시스템에는 SP 아래에 매핑된 바이트가 있지만 0x1FF00아마도 이것은 스택 상단에서 카운트다운되고 0x7ffffffff000전체 맵에 바이트가 있습니다. 0x21000이 숫자에 어떤 영향이 있습니까?

나는 스택 바로 아래에 있는 페이지가 "가드 페이지"라는 것을 알고 있으며 여기에 쓰면 자동으로 쓰기 가능해지고 "스택 아래로 늘어납니다"(아마도 순진한 스택 처리가 "그냥 작동"할 것입니다). 거대한 스택 프레임이 있으면 가드 페이지와 세그폴트를 초과할 수 있으므로 프로세스가 시작될 때 얼마나 많은 공간이 올바르게 할당되었는지 확인하고 싶습니다.

편집하다: 데이터가 많아질수록 무슨 일이 일어나고 있는지 확신할 수 없게 됩니다. 테스트는 다음과 같습니다.

.text
  .global _start
_start:
  subq $0x7fe000,%rsp
  movq $1,(%rsp)
  mov $0x3c,%eax
  mov $0,%edi
  syscall

여기서는 어떤 일이 일어나는지 확인하기 위해 다른 상수 값을 사용했습니다 0x7fe000. 어떤 값에 대해서는 segfault 발생 여부가 정의되지 않습니다. GDB에 따르면 subq명령어 자체는 mmap 크기를 확장하는데, 이는 나에게 신비스럽습니다(Linux는 내 레지스터에 무엇이 있는지 어떻게 알 수 있습니까?). 그러나 프로그램은 일반적으로 어떤 이유로 종료 시 GDB와 충돌합니다. GOT나 PLT 부분을 사용하지 않기 때문에 ASLR이 불확실성을 유발할 수는 없습니다. 실행 파일은 매번 가상 메모리의 동일한 위치에 로드됩니다. 그렇다면 이것은 일종의 PID 또는 물리적 메모리에 대한 무작위 침투입니까? 전체적으로 나는 실제로 무작위 액세스에 합법적으로 사용될 수 있는 스택의 양과 RSP를 변경하거나 "범위를 벗어난" 합법적인 메모리 영역에 쓸 때 얼마나 많은 양이 필요한지 혼란스럽습니다.

답변1

나는 이 질문이 실제로 ELF와 관련이 있다고 생각하지 않습니다. 내가 아는 한, ELF는 "플랫 팩"프로그램 이미지는 파일에 기록된 다음 첫 번째 실행을 준비하기 위해 다시 어셈블됩니다. 운영 체제 동작이 POSIX로 승격되지 않은 경우 스택 정의와 스택 구현 방법은 CPU 특정과 운영 체제 사이 어딘가에 있습니다. 특정한. ELF 사양이 스택에 필요한 사항에 대해 몇 가지 요구 사항을 부과한다는 것은 의심의 여지가 없습니다.

최소 스택 할당

귀하의 질문에서 :

나는 스택 바로 아래에 있는 페이지가 "가드 페이지"라는 것을 알고 있으며 여기에 쓰면 자동으로 쓰기 가능해지고 "스택 아래로 늘어납니다"(아마도 순진한 스택 처리가 "그냥 작동"할 것입니다). 거대한 스택 프레임이 있으면 가드 페이지와 세그폴트를 초과할 수 있으므로 프로세스가 시작될 때 얼마나 많은 공간이 올바르게 할당되었는지 확인하고 싶습니다.

이에 대한 권위 있는 참고 자료를 찾으려고 노력 중입니다. 그러나 나는 이것이 틀렸다고 제안할 만큼 신뢰할 수 없는 참조를 충분히 찾았습니다.

내가 아는 한, 가드 페이지는 "정상적인" 스택 증가가 아닌 최대 스택 할당 외부의 액세스를 포착하는 데 사용됩니다. 실제 메모리 할당(페이지를 메모리 주소에 매핑)은 요청 시 수행됩니다. 즉, 메모리에서 매핑되지 않은 주소(스택 베이스와 스택 베이스 사이 - max-stack-size + 1)에 액세스할 때 CPU는 예외를 트리거할 수 있지만 커널은 페이지 메모리를 매핑하여 예외를 처리합니다. 분할 오류가 계단식으로 발생합니다.

따라서 최대 할당 범위 내에서 스택에 액세스하면 분할 오류가 발생하지 않아야 합니다. 당신이 찾은대로

최대 스택 할당

연구 문서는 스레드 생성 및 이미지 로딩에 대한 Linux 문서를 따라야 합니다(포크(2),클론 (2),실행(2)). execve에 대한 문서몇 가지 흥미로운 사항이 언급되었습니다.

매개변수 크기 및 환경 제한

...전단...

커널 2.6.23 이상에서는 대부분의 아키텍처가 소프트에서 파생된 크기 제한을 지원합니다.RLIMIT_STACK리소스 제한(참조제한 사항 받기(2))

...전단...

이는 해당 제한 사항을 지원하려면 아키텍처가 필요하다는 점을 확인하고 해당 제한 사항도 언급합니다(제한 사항 받기(2)).

RLIMIT_STACK

이는 프로세스 스택의 최대 크기(바이트)입니다. 이 한계에 도달하면 SIGSEGV 신호가 생성됩니다. 이 신호를 처리하려면 프로세스가 대체 신호 스택(sigaltstack(2))을 사용해야 합니다.

Linux 2.6.23부터 이 제한은 프로세스 명령줄 인수와 환경 변수가 사용하는 공간의 양도 결정합니다. 자세한 내용은 execve(2)를 참조하세요.

RSP 레지스터를 변경하여 스택을 늘립니다.

나는 x86 어셈블러를 모른다. 그러나 SS 레지스터가 변경될 때 x86 CPU에 의해 트리거될 수 있는 "스택 오류 예외"에 주의를 기울이고 싶습니다. 내가 틀렸다면 정정해주세요, 하지만 x86-64 SS:SP에서는 "RSP"가 되었다고 생각합니다. 따라서 올바르게 이해했다면 RSP() 를 감소시켜 스택 오류 예외가 발생할 수 있습니다 subq $0x7fe000,%rsp.

여기 222페이지를 참조하세요.https://xem.github.io/minix86/manual/intel-x86-and-64-manual-vol3/o_fe12b1e2a880e0ce.html

답변2

각 프로세스 메모리 영역(예: 코드, 정적 데이터, 힙, 스택 등)에는 경계가 있으며, 영역 외부의 메모리 액세스 또는 읽기 전용 영역에 대한 쓰기 액세스는 CPU 예외를 생성합니다. 커널은 이러한 메모리 영역을 유지 관리합니다. 영역 외부의 액세스는 분할 오류 신호로 사용자 공간에 전파됩니다.

영역 외부의 메모리에 액세스하여 모든 예외가 생성되는 것은 아닙니다. 지역 내 액세스도 예외를 생성할 수 있습니다. 예를 들어, 페이지가 실제 메모리에 매핑되지 않은 경우 페이지 오류 처리기는 실행 중인 프로세스에 투명한 방식으로 이를 처리합니다.

프로세스 메인 스택 영역에는 처음에는 매핑된 페이지 프레임 수가 적지만 스택 포인터를 통해 더 많은 데이터가 여기에 푸시됨에 따라 자동으로 커집니다. 예외 처리기는 액세스가 여전히 스택에 예약된 영역 내에 있는지 확인하고, 그렇다면 새 페이지 프레임을 할당합니다. 사용자 수준 코드의 관점에서 보면 이는 자동으로 발생합니다.

스택 영역의 오버플로를 감지하기 위해 스택 영역 끝 뒤에 가드 페이지가 배치됩니다. 최근(2017)에는 프로그램이 스택 포인터를 대량으로 줄이도록 속여 스택 포인터가 쓰기가 허용되는 다른 영역을 가리킬 수 있기 때문에 단일 가드 페이지만으로는 충분하지 않다는 것이 인식되었습니다. 이 문제에 대한 "해결책"은 4kB 가드 페이지를 1MB 가드 영역으로 바꾸는 것입니다. 이것 좀 봐그린넷 기사.

예를 들어, 사용자가 호출을 통해 프로그램이 할당하는 메모리 양을 제어할 수 있어야 하는 등 취약점을 악용하기가 완전히 쉽지는 않습니다 alloca. 강력한 프로그램은 전달된 인수를 확인해야 하며 alloca, 특히 사용자 입력에서 오는 경우라면 더욱 그렇습니다.

관련 정보