주소 0 으로 점프를 시도하다 SIGSEGV. 데이터 역참조([[01-null-deref]])와 달리 "코드를 실행하려다" 죽는 점이 다르다.
$ gcc -g bug.c -o bug
$ ./bug
Segmentation fault (core dumped)
$ echo $?
139
데이터 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 검사를 한다. 디스패치 테이블에 기본 핸들러를 두는 것도 방법.
bt의0x0 in ?? ()와pc = 0은 함수 포인터/스택 리턴 주소 손상의 신호.- 데이터 NULL 역참조와 코드 NULL 점프는 죽는 위치(PC)로 구분된다.