NX 비트는 보안만을 위한 것이 아니다
The NX bit is not just about security
ARM64 하이퍼바이저 개발 중 CTR_EL0 인터셉트를 활성화하면 시스템이 무작위로 멈추는 난해한 버그를 추적한 과정을 다룬다. C 핸들러가 정상 작동하는데도 불구하고 시스템이 부팅되지 않자, 저자는 어셈블리 수준에서 이진 탐색을 통해 문제를 좁혀 나간다. 결국 원인은 동적 호출(blr)과 정적 호출(bl)의 차이에서 발생한 분기 예측 오류로, 잘못된 예측이 MMIO 접근을 유발해 시스템이 멈춘 것으로 밝혀진다. 이는 NX 비트가 단순한 보안 기능이 아니라, 하드웨어의 미세 아키텍처 동작과도 연관되어 있음을 보여준다.
두 명령어는 어떻게 다를까? `blr x0`는 동적이고, `bl get_ctr_el0`는 정적이다. 동적 디스패치는 분기 예측을 사용하지만, 정적 분기는 목적지를 미리 알고 있어 잘못 예측할 수 없다. 명령어가 잘못 예측되면 무엇으로 잘못 예측될까? ... 아마도 널 포인터, `0x0`일 것이다.
HN 토론
48- repiret
"투기적 명령어 페치가 장치 메모리로 가서 장치가 죽는 것"은 제가 발견한 버그 중 가장 좋아하는 버그 목록에 있습니다. MPU가 없는 Cortex-M4에서 이 문제를 겪었는데, 메모리 속성을 고칠 수도 없어서 유일한 해결책은 컴파일된 코드를 교묘하게 조작하여 투기적 페치를 방지하는 것이었습니다.
Armv8/9-A가 장치 메모리로의 투기적 명령어 페치를 허용한다는 것은 정말 미친 일입니다. 너무 미친 나머지 직접 찾아봐야 믿을 수 있었습니다:
"하드웨어는 메모리 위치가 모든 Exception Level에서 execute-never로 표시되지 않는 한, Device 메모리 속성을 가진 메모리 위치로의 투기적 명령어 페치를 방지하지 않습니다." - ARM DDI 0487K.a § B2.15.2
Armv7-M 사양은 덜 명확합니다. Armv8/9-A가 유사한 문장을 "데이터 접근"으로 한정하는 것과 달리, "아키텍처는 Device로 표시된 메모리에 대한 투기적 접근을 허용하지 않습니다"라고 말합니다. 그러나 나중에 "정확성을 보장하려면 읽기에 민감한 위치는 실행 불가로 표시되어야 합니다"라고 말합니다. (이것은 모두 ARM DDI 0403E.e § A3.5.7에서 가져온 것입니다.)
비교할 수 있도록 오래된 v7-A 문서를 가지고 있지 않습니다.
- asveikau
보안만을 위한 것이 아니라는 주제에 대해: use-after-free와 같은 버그가 함수 포인터를 덮는 경우, NX 비트는 해당 포인터를 호출할 때 실패 지점에 최대한 가까운 곳에서 깔끔한 트랩을 발생시킬 수 있게 합니다. 만약 오래된 바이트를 맹목적으로 코드로 실행했다면, 크래시와 스택 트레이스가 그렇게 깔끔하지 않을 수 있습니다.
하지만 그런 종류의 많은 정확성 버그는 또한 보안 문제이기도 합니다.
- anyfoo
요즘에는 0번지에 아무것도 매핑하는 것은 나쁜 관행으로 여겨집니다... 이제 그 이유 중 하나를 알게 되었습니다!
제가 아는 경우에는 주소 공간의 1:1 동일 매핑이 있는데, 물리 주소 공간에도 실제로 0번지에 아무것도 없으므로 매핑할 필요가 없습니다.
- mubbicles
이 기사를 높이 평가합니다. 저희 CSP 담당자에게 차이점을 물어봤지만, 이 글이 Device와 NS의 차이를 더 잘 이해하는 데 도움이 되었습니다. 또한 이 글이 AI가 쓴 것처럼 보이지 않는 점도 마음에 듭니다.
- tripdout
이 글을 정말 이해할 수 있었으면 좋겠습니다. 들어본 적이 있고 흥미로워 보이는 많은 주제들을 다루지만, 따라갈 만큼 충분히 알지 못합니다. NX 비트(이해함)와 투기적 접근 사이의 연관성을 모르겠습니다.