Skip to content

Latest commit

 

History

History
49 lines (35 loc) · 1.67 KB

File metadata and controls

49 lines (35 loc) · 1.67 KB

Case 10 -- NULL 함수 포인터 호출

증상

주소 0 으로 점프를 시도하다 SIGSEGV. 데이터 역참조([[01-null-deref]])와 달리 "코드를 실행하려다" 죽는 점이 다르다.

$ gcc -g bug.c -o bug
$ ./bug
Segmentation fault (core dumped)
$ echo $?
139

gdb 분석

데이터 NULL 역참조와 구분하는 핵심은 PC(프로그램 카운터) 자체가 0 이라는 점이다.

(gdb) run
Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()        # PC 가 0. 실행 불가 주소로 점프함
(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  main () at bug.c:20            # fn(3, 4) 호출 지점
(gdb) frame 1
(gdb) print fn
$1 = (op_fn) 0x0                   # 함수 포인터가 NULL
(gdb) info registers pc            # arm64: pc = 0x0 확인

0x0 in ?? () 처럼 함수 이름이 없고 주소가 0 이면 함수 포인터/리턴 주소 손상을 의심한다.

원인

dispatch('*') 가 등록되지 않은 코드라 NULL 을 반환한다. 호출부에서 NULL 검사 없이 fn(3, 4) 로 호출해 주소 0 을 실행하려다 크래시.

수정

호출 전에 NULL 을 확인한다.

op_fn fn = dispatch(code);
if (fn == NULL) {
    fprintf(stderr, "unsupported op: %c\n", code);
    return 1;
}
int result = fn(3, 4);

배운 점

  • 함수 포인터는 호출 전 NULL 검사를 한다. 디스패치 테이블에 기본 핸들러를 두는 것도 방법.
  • bt0x0 in ?? ()pc = 0 은 함수 포인터/스택 리턴 주소 손상의 신호.
  • 데이터 NULL 역참조와 코드 NULL 점프는 죽는 위치(PC)로 구분된다.