← ARMv9 해설DUJINLABS.COM

Source deep dive 05 · MTE · TCF0 · GCR · SIGSEGV

ARM MTE와 Linux fault 처리 소스 분석

MTE를 색깔표가 붙은 memory라는 비유로 이해한 뒤, process가 검사 mode를 요청하고 Linux가 SCTLR_EL1·GCR_EL1을 바꾸며 synchronous·asynchronous fault를 signal로 전달하는 실제 코드를 추적한다.

Kernel
Linux v6.18.37
Granule
16 bytes
Tag
4 bits
Fault
sync · async

MTE는 pointer와 memory의 번호표를 비교한다

allocator가 16-byte memory granule에 4-bit allocation tag를 붙이고 pointer 상위 bit에 logical tag를 넣는다. CPU가 load/store할 때 두 tag가 다르면 오류를 보고한다. 해제 후 memory tag를 바꾸면 오래된 pointer가 같은 주소를 가리켜도 tag mismatch로 잡힐 수 있다.

Spatial bug

다른 tag granule까지 넘는 overflow를 발견할 수 있다.

Temporal bug

free 후 retag된 memory에 옛 pointer가 접근하는 것을 잡을 수 있다.

Probabilistic

tag가 4-bit라 우연히 같은 tag가 재사용될 가능성은 남는다.

Opt-in

CPU 지원만으로 application 검사가 자동 활성화되지 않는다.

Logical tag와 allocation tag는 저장 위치가 다르다

Tag저장 위치만드는 주체
Logical tagpointer의 top byte 일부allocator·compiler·application이 tagged pointer를 만든다.
Allocation tagmemory와 연관된 별도 tag storageSTG 계열 instruction 등으로 설정한다.
Excluded tag maskGCR_EL1random tag 생성 시 피할 값을 process 정책으로 정한다.
Fault recordESR/FAR 또는 TFSRsync/async mode에 따라 정확도가 달라진다.

주소 허용과 tag 검사는 서로 다른 제어다

Register field역할실무 해석
TCR_EL1.TBI0EL0 주소 번역에서 top byte 무시tagged pointer를 주소로 사용할 전제다.
SCTLR_EL1.TCF0EL0 tag-check fault modeNONE, SYNC, ASYNC, ASYMM 중 kernel이 선택한다.
GCR_EL1.Excluderandom tag 제외 maskPR_MTE_TAG_MASK 정책에서 변환된다.
TFSRE0_EL1EL0 async fault pending정확한 fault instruction보다 발생 사실을 지연 보고한다.
PSTATE.TCOtag check overrideentry code와 kernel helper가 제한된 구간에서 검사를 제어한다.

PR_SET_TAGGED_ADDR_CTRL이 process 정책을 설정한다

Userspace에서 kernel까지

prctl(PR_SET_TAGGED_ADDR_CTRL, flags)
→ arch_set_tagged_addr_ctrl(task, arg)
→ set_mte_ctrl(task, arg)
→ mode와 allowed tag mask 검증
→ task->thread.mte_ctrl 갱신
→ mte_update_sctlr_user(task)
→ SCTLR_EL1.TCF0 / GCR_EL1 반영

Linux v6.18.37의 set_mte_ctrl()은 지원하지 않는 flag를 거부하고 CPU가 ASYMM을 지원하는지 확인한다.

PR_TAGGED_ADDR_ENABLE은 syscall ABI에서 tagged pointer를 허용하는 정책이고, PR_MTE_TCF_*는 tag mismatch 처리 정책이다. 둘을 같은 bit로 생각하면 안 된다.

Synchronous mode는 fault instruction을 정확히 가리킨다

tag mismatch가 발생한 load/store에서 즉시 Data Abort가 나고 ESR의 fault status가 tag check fault를 나타낸다. arm64 fault table은 do_tag_check_fault()를 선택하고 userspace에는 SIGSEGVSEGV_MTESERR를 전달한다.

Sync fault 경로

mismatched load/store
→ Data Abort to EL1
→ do_mem_abort
→ do_tag_check_fault
→ force_signal_inject(SIGSEGV, SEGV_MTESERR, FAR)
→ userspace signal handler

FAR가 정확하므로 디버깅에 유리하지만 모든 검사에서 즉시 exception을 처리해 비용이 커질 수 있다.

Asynchronous mode는 빠르지만 위치가 부정확하다

CPU가 mismatch를 TFSR에 기록하고 실행을 계속한 뒤 kernel entry·exit 같은 안전 지점에서 확인한다. mte_check_tfsr_el1()은 pending bit를 읽고 정리한다.

entry-common.cmte_check_tfsr_entry()mte_check_tfsr_exit()는 지연 fault가 task 경계를 넘어 유실되지 않게 확인한다. async report의 address가 실제 잘못된 instruction을 정확히 가리킨다고 가정하면 안 된다.

ModeRead mismatchWrite mismatch용도
SYNC즉시 exception즉시 exception정확한 개발·디버그
ASYNC지연 보고지연 보고낮은 비용의 production 탐지
ASYMMsyncasyncCPU 지원 시 정확도·비용 절충

Task switch 때 process별 TCF0와 tag mask를 복원한다

__switch_to() 경로의 mte_thread_switch(next)는 outgoing async fault를 확인하고 incoming task의 mte_ctrl을 register에 반영한다. CPU register는 per-CPU지만 정책은 task별이므로 migration에서도 복원해야 한다.

  1. 이전 task의 TFSR write가 보이도록 필요한 동기화를 수행한다.
  2. pending async fault를 task flag로 보존한다.
  3. next task의 TCF0 mode를 SCTLR_EL1에 반영한다.
  4. allowed tag mask와 random 정책을 GCR_EL1에 반영한다.
  5. userspace 복귀 전에 pending signal을 전달한다.

Page에 allocation tag storage를 준비하는 경로

mte_sync_tags()는 PTE가 가리키는 page의 tag 초기화 상태를 동기화한다. tagged mapping을 만들었다고 physical page의 allocation tag가 자동으로 application 의도에 맞게 설정되는 것은 아니다.

allocator는 object를 반환할 때 logical/allocation tag를 맞추고, free할 때 retag해야 temporal bug 탐지 효과가 생긴다. kernel MTE와 userspace MTE는 fault policy와 allocator 경로가 다르므로 설정을 구분한다.

Linux v6.18.37 소스 지도

파일심볼책임
arch/arm64/kernel/mte.cset_mte_ctrl, mte_update_sctlr_userprocess policy와 register 반영
mte.cmte_check_tfsr_el1, mte_thread_switchasync fault와 task switch
arch/arm64/mm/fault.cdo_tag_check_faultsync fault를 SIGSEGV로 변환
arch/arm64/kernel/entry-common.cmte_check_tfsr_entry/exitentry 경계의 지연 fault 확인
arch/arm64/kernel/process.carch_set_tagged_addr_ctrlprctl architecture hook
asm/mte.htag access helperarchitecture별 inline operation

Use-after-free를 직접 재현한다

검증 체크리스트

CPU: getauxval(AT_HWCAP2) & HWCAP2_MTE
Kernel: tagged address ABI와 MTE config
Process: PR_GET_TAGGED_ADDR_CTRL
Binary: allocator가 memory를 실제 retag하는지
Signal: si_code == SEGV_MTESERR 또는 SEGV_MTEAERR

sync mode에서 먼저 재현해 정확한 stack을 얻고, async mode로 바꿔 성능과 탐지 지연을 비교한다. production에서 pointer나 memory 내용을 log할 때 tag와 secret 노출을 주의한다.

MTE가 보장하지 않는 것

  • 같은 tag granule 내부의 작은 overflow는 tag가 같아 탐지하지 못할 수 있다.
  • 4-bit tag 충돌 때문에 모든 use-after-free를 확정적으로 잡지는 않는다.
  • integer로 pointer를 변형하거나 ABI 규칙을 어기면 false positive·우회가 생길 수 있다.
  • MTE는 bounds checker, type safety, PAC·BTI·GCS를 대체하지 않는다.

근거 자료