핵심 요약
- 2바이트 크기에 유효하지 않은 옵코드를 가리키는 `int 6` 대신 굳이 `ud2` 명령어를 사용하는 이유에 대해 MS 개발자 레이먼드 첸이 설명했습니다.
- `ud2`는 오류 발생 주소를 보고하는 '폴트'인 반면 `int 6`은 다음 명령어 주소를 넘기는 '트랩'이라 핸들러가 멀쩡한 코드를 오류로 착각하게 됩니다.
- 또한 현대 운영체제는 유저 모드에서 `int 6` 직접 호출 권한을 차단해 두었기 때문에 시도하더라도 일반 보호 오류(GPF)만 발생합니다.
요약 x86 아키텍처에서 비정상 종료나 정의되지 않은 동작을 의도적으로 유발할 때 사용되는 `ud2` 명령어 대신, 유효하지 않은 옵코드(Invalid Opcode) 인터럽트 벡터인 `int 6`을 직접 호출하면 안 되는지에 대한 MS 베테랑 엔지니어 레이먼드 첸의 명쾌한 설명입니다.
겉으로 보기에 `ud2`와 `int 6`은 둘 다 2바이트 크기의 명령어이고 동일하게 잘못된 옵코드 예외(#UD) 상황을 다루는 것처럼 보입니다. 하지만 CPU 아키텍처 관점에서 두 동작은 근본적으로 '폴트(Fault)'와 '트랩(Trap)'이라는 큰 차이를 가집니다.
폴트는 예외 핸들러가 문제를 해결한 후 해당 명령어를 '다시 시도(retry)'할 수 있도록 오류가 발생한 바로 그 명령어의 주소를 핸들러에 전달합니다. 반면 소프트웨어 인터럽트인 `int 6`과 같은 트랩은 이미 실행된 동작에 대해 후속 조치를 취한 뒤 '다음 명령어'로 넘어가도록 설계되어 있어, 핸들러에는 `int 6` 다음 명령어의 주소가 기록됩니다.
만약 개발자가 `int 6`을 수동으로 호출해 가짜로 #UD 예외를 흉내 내려 한다면, 운영체제의 예외 핸들러는 심각한 혼란에 빠지게 됩니다. CPU가 생성한 정의되지 않은 명령어 예외라고 생각하고 예외 프레임의 명령어 포인터를 확인했는데, 실제로는 `int 6` 바로 다음에 위치한 멀쩡한 명령어(예: `xor eax, eax`)를 가리키고 있기 때문입니다. 핸들러 입장에서는 '멀쩡한 명령어인데 왜 잘못된 옵코드 예외가 떴지?'라며 엉뚱한 코드를 검사하게 됩니다. 특히 구형 프로세서에서 지원하지 않는 명령어를 에뮬레이션해야 하는 경우 이런 주소 불일치는 치명적입니다.
결정적으로 보호 모드(Protected Mode) 환경의 현대 OS(Windows, Linux 등)에서는 인터럽트 디스크립터 테이블(IDT) 설정을 통해 유저 모드 프로그램이 `int 6`을 직접 호출하는 권한 자체를 원천 차단하고 있습니다. 따라서 유저 모드에서 임의로 `int 6`을 실행하려고 시도해도 #UD 예외가 발생하는 것이 아니라 권한 위반에 따른 일반 보호 오류(GPF)가 발생할 뿐입니다. 결국 아키텍처상 온전한 정의되지 않은 명령어 예외(#UD)를 발생시키려면 꼼수 대신 실제 정의되지 않은 명령어로 설계된 `ud2`를 써야 합니다.
해외 반응 번역
그럼 만약 코드에서 `int 6` 실행을 시도했다가 권한 때문에 거부당하면 어떻게 되나요? 제 생각엔 폴트가 뜨긴 하는데 엉뚱한 폴트가 뜰 것 같은데요?
제 기억이 맞다면 일반 보호 오류(General Protection Fault)가 뜰 겁니다.
Sponsored · 광고