| GPUArmor: A Hardware-Software Co-design for Efficient and Scalable Memory Safety on GPUs | |
|---|---|
| Author | Mohamed Tarek Ibn Ziad, Sana Damani, Mark Stephenson, Stephen W. Keckler, Aamer Jaleel |
| Conference | ACM TACO |
| Year | 2025 |
개요
이 논문은 GPU에서 Memory safety 검사를 할 때 metadata lookup을 빠르게 만들기 위해 큰 direct-addressing table을 써야 한다는 가정을, GPU workload의 allocation working set이 작다는 관찰을 통해서 작은 hardware metadata cache로 해결하였다.
Motivation
기존 CPU sanitizer 방식의 핵심 비용은 runtime metadata lookup이다. AddressSanitizer는 8 byte application memory마다 1 byte shadow memory를 두어 O(1) lookup을 얻지만 12.5% memory bloat를 낸다. GPU 쪽에서도 GPUShield와 cuCatch가 direct-addressing table류의 구조를 사용하지만, GPUShield는 57-bit address space에서 upper pointer bit를 index로 쓰는 설계 때문에 128 live allocation 수준에서 scalability 한계가 있고 temporal safety도 약하다. cuCatch는 더 scalable하지만 12.5% metadata storage overhead와 non-trivial runtime overhead가 남는다.
GPUArmor의 출발점은 GPU에서는 CPU와 같은 cost model이 성립하지 않을 수 있다는 점이다. 논문은 real-world GPU kernel 28개를 분석하여 global memory access가 전체 dynamic instruction의 평균 6% 정도이고, shared memory도 평균 6% 정도이며, local memory access는 관측되지 않았다고 보고한다. GPU는 warp scheduling으로 long-latency memory operation을 숨기는 데 강하고, allocation size는 대체로 크지만 active allocation working set은 작다. 따라서 metadata lookup을 매 access마다 절대적으로 O(1)로 만들기보다, 작은 cache로 대부분의 lookup을 처리하고 compact metadata structure를 쓰는 쪽이 GPU에 더 맞을 수 있다.
Main Idea
Observations
본 연구는 다음과 같은 특징을 고려해야 효율적인 Sanitizer를 디자인 할 수 있다고 주장한다.
- GPU의 메모리 접근은 CPU보다 Pressure가 낮으며, Local memory (stack 할당)은 거의 없는 수준이다. 또한 Global memory access가 Shared memory access보다 작다.
- GPU의 워크로드들은 대부분 병령 처리 되기 떄문에 Memory access checking의 Latency가 크더라도 Hiding된다.
- GPU의 메모리 크기는 보통 CPU보다 작다. (HMM은 제외하였을 경우)
- GPU의 Allocation size는 대부분 크다. 예를 들어서 CUDA Memory allocation은 적어도 256B의 정렬을 요구한다.
- Live allocation의 개수-즉 특정 시점에 동시에 접근 할 수 있는 allocation의 개수-는 병렬 처리의 특징으로 인해 큰 경향이 있다.
- Working set크기-즉 특정 시점에 반복적으로 접근하는 메모리의 크기-는 작은 경우가 대부분이다.
Ramification
위의 Observation결과에 따라 다음과 같은 GPU Sanitizer의 요구사항이 정해진다.
- Observation 1-2: 생각보다 memory instrumentation체크에 걸리는 시간이 CPU workload만큼 중요하지는 않다. 또한 stack allocation에 걸리는 시간이 길더라도 전체 실행시간에는 큰 영향을 주지 않는다.
- Observation 3-4: 메타데이트가 Memory크기에 따라서 Linear하게 증가하는 것 보다는 Per-allocation에 있는 것이 메모리를 더 절약할 수 있다.
- Observation 5: Live allocation의 개수를 모두 처리해야 하기 떄문에 Pointer-tagging과 같은 구조는 Metadata fetching에 있어서 불리하다.
- Observation 6: Workgin set의 개수가 작기 때문에 Per-SM hardware structure가 있으면 metadata cache allocation을 줄일 수 있다.
따라서, 아이디어는 GPU kernel이 동시에 접근할 수 있는 전체 live allocation 수는 많아도, 실제 실행 중 짧은 구간에서 반복적으로 접근하는 allocation working set은 작다는 관찰을 이용해 allocation metadata lookup을 작은 per-SM cache로 처리하는 것이다. 이렇게 하면 metadata table 자체는 direct-addressing table처럼 memory footprint에 비례해 커질 필요가 없고, linked list나 binary search tree처럼 compact한 per-allocation metadata structure를 쓸 수 있다.
Design
Metadata design
- Allocation
- Allocation 생성 시 runtime wrapper가 base address, size, random tag를 metadata structure에 저장하고, tag를 pointer upper bits (VA의 57부터 64번쨰의 비트)에 넣은 tagged pointer를 application에 돌려준다. Metadata structure는 linked list, balanced binary search tree, hypothetical speed-of-light table처럼 여러 종류의 Metadata structure로 구현할 수 있다. Linked list와 binary tree는 allocation당 32 byte node를 사용하며, 그중 16 byte는 base/size/tag metadata이고 나머지는 pointer field이다.
- Deallocation
- Free 시에는 metadata entry를 즉시 제거하지 않고 tag를 reserved value로 바꾼다. 이 방식은 dangling pointer의 tag와 metadata tag가 mismatch되도록 만들어 immediate Use-after-free를 잡는다. 삭제 entry를 남기는 선택은 concurrent kernel이 metadata structure를 traverse 중일 수 있다는 GPU execution model에도 맞는다.
- Metadata checking
- Memory access가 발생하면, Input address가 base and size에 들어가는지를 메타데이터를 참고해서 Spatial safety bug를 판단하였다. 또한 Temporal safety를 방지하기 위해서 Pointer tag가 Metadata tag와 일치하는지를 체크하였다.
Hardware modifications
- ISA extension
- GPUArmor는 세 개의 instruction을 추가한다. `LOADMETA`는 compiler가 찾은 root pointer를 key로 metadata structure를 traverse하고 metadata location을 반환하며 MLB를 채운다. `MEMCHECK.G`는 global memory address와 metadata pointer를 받아 base/bounds/tag를 검사하고, check를 통과하면 upper tag bit를 clear한 address를 반환한다. `MEMCHECK.S`는 shared memory address에 대해 compile-time에 알 수 있는 base/size로 bounds check를 수행한다.
Metadata Loading Unit와 Metadata Lookaside Buffer
- Metadata Loading Unit
- MLU는 Load/Store Unit 근처에 위치하며, metadata structure traversal을 담당하는 FSM과 Metadata Lookaside Buffer(MLB)를 포함한다. LOADMETA에서는 MLB가 input address를 포함하는 allocation range를 찾으면 metadata location을 바로 반환하고, miss일 때만 FSM이 linked list 또는 binary tree를 traverse한다. MEMCHECK에서는 metadata location으로 MLB를 찾고, miss일 때 metadata를 cache hierarchy에서 가져온다. 논문이 사용하는 기본 설계는 per-SM 8-entry MLB이다. 각 entry는 16 byte metadata와 8 byte metadata location을 담는다. 이 구조가 충분한 이유는 평가 workload의 allocation working set이 8개 미만이기 때문이다.
Compiler/Runtime support
- Compiler pass
- Compiler pass는 ptxas backend에 구현되어 global/shared memory access 앞에 MEMCHECK를 넣고, object당 한 번 LOADMETA를 넣는다. Root pointer analysis는 cuCatch의 intra-procedural reaching-definition 기반 분석을 따르며, pointer arithmetic을 거슬러 올라가 object의 root pointer 또는 적어도 checked address와 다른 intermediate pointer를 찾는다. 이 방식은 memory access마다 metadata를 다시 load하지 않고 root pointer 단위로 metadata pointer를 propagate한다.
- Runtime support
- Runtime은 `cudaMalloc`, `cudaFree` 같은 CUDA global memory management API를 wrapper로 감싼다. Allocation 시 size/base/tag를 기록하고 tagged pointer를 반환하며, free 시 reserved tag로 metadata를 갱신한다. 논문은 allocation/free 수가 적기 때문에 runtime metadata management overhead는 negligible하다고 본다.
- Binary compatibility와 HWOnly mode
- GPUArmor는 non-zero tag가 붙은 pointer에만 metadata retrieval과 check를 수행하고, memory hierarchy에 request를 보내기 전 tag bit를 mask한다. 이는 ARM TBI, Intel LAM, AMD UAI와 유사한 upper-address-bit 무시 기능을 전제로 한다. Recompilation이 불가능한 third-party library에는 GPUArmor-HWOnly mode를 제안한다. 이 모드에서는 compiler instrumentation 없이 hardware가 모든 memory instruction에서 metadata를 fetch/check하며, coverage는 memory tagging과 유사하게 probabilistic해지지만 per-allocation metadata 덕분에 LAK 같은 per-granule tag storage보다 memory overhead가 작다.
Evaluation
- MLB 없이 metadata structure만 비교하면 linked list는 평균 2x, 최대 22x overhead를 낸다. Binary search tree는 평균 10%, 최대 80% overhead로 줄지만 large allocation count workload에서는 여전히 traversal latency가 크다. Hypothetical speed-of-light table은 O(1) lookup으로 평균 1.4% overhead를 보인다.
- 8-entry MLB 효과: 8-entry MLB를 넣으면 metadata traversal 비용이 대부분 사라진다. Binary search tree + 8-entry MLB는 평균 약 2% 수준의 runtime overhead를 보이며, 논문 결론에서는 base-and-bounds coverage를 2.3% average slowdown으로 달성한다고 요약한다. Linked list도 8-entry MLB가 있으면 평균 7% 수준까지 내려가며, SoL table은 약 1% 수준이다. 이 결과는 MLB가 underlying metadata structure의 asymptotic lookup cost를 대부분 capacity-hit case에서 지운다는 주장과 연결된다.
- MLB ize sensitivity: MLB entry 수를 0, 1, 2, 4, 8, 16으로 바꿔 평가했을 때 overhead는 8-entry 이후 거의 saturate된다. 이는 characterization에서 관찰한 "allocation working set < 8"과 직접 대응한다.
- Shared memory protection: Shared memory protection은 metadata load가 필요 없고 bounds-check compute logic만 추가하므로 전체 workload에서 overhead가 0.1% 미만이다.
- Related work와의 비교: cuCatch와 같은 safety guarantee를 비교했을 때, cuCatch는 평균 29% runtime overhead와 12.5% metadata storage overhead를 보인다. GPUArmor는 binary search tree + 8-entry MLB에서 약 2% runtime overhead와 0.0005% storage overhead를 보고한다. AMG_3 사례에서는 GPUArmor가 instruction bloat를 2x 줄이고 register pressure를 1.7x 줄여 baseline과 유사한 SM occupancy를 유지한다고 설명한다.
- Hardware cost: 16-entry MLB는 entry당 24 byte로 약 384 byte per SM이다. 7nm SRAM/comparator/priority logic 구현 가정에서 L1 cache 대비 area overhead는 약 0.2%, per-access energy는 약 5%이다. MLB lookup은 global memory instruction에만 적용되고 이들이 전체 instruction의 약 6%이므로 total GPU power impact는 0.0006% 미만으로 추정된다.
Contribution
- Real-world GPU workload의 memory access, allocation size, live allocation count, allocation working set을 memory-safety 관점에서 정량화하였다.
- Direct-addressing metadata table이 GPU memory safety의 필수 조건이 아니라는 점을 보이고, 작은 per-SM MLB가 metadata lookup cost를 대부분 제거할 수 있음을 제안하였다.
- LOADMETA, MEMCHECK.G, MEMCHECK.S instruction과 MLU/MLB hardware, CUDA runtime wrapper, ptxas compiler instrumentation을 결합한 GPUArmor hardware-software co-design을 제시하였다.
- Base-and-bounds checking에서 평균 2.3% 수준 slowdown과 0.0005% storage overhead를 보이며, cuCatch 대비 runtime/storage overhead를 크게 낮출 수 있음을 평가하였다.
- Compiler support가 없는 GPUArmor-HWOnly mode를 통해 memory tagging 수준의 probabilistic safety를 낮은 overhead와 per-allocation metadata storage로 제공할 수 있음을 보였다.
Criticisms
Major Comments
- Writing quality측면에서 개선될 방향이 존재한다. 예를 들어서 Observations들은 잘 정리되어 있고 휼룡하지만, 서술하는 방식이 개조식으로 서술되어 있었으면 좀더 이해하기 쉬웠을 듯 하다. 예를 들어서 Instruction behavior에 top sentence로 GPU workloads access shared memory much more frequently than global memory compared to CPU-centric workloads. 이런식이였으면 좀더 이해하기 쉬웠을 듯 하다.
- Writing quality측면 2: GPUArmor uses a pointer’s value (rather than the pointer’s location in memory [19, 24]) as a key to search the metadata structure. 이문장은 배경지식이 부족하면 이해하기 힘들듯. Location-based라는 말을 위에서 설명한적이 없기 때문에 pointer의 value는 *ptr의 값을 의미하는 것처럼 느껴질 수 도 있을듯.
- 보통 있는 Pointer scheme에 대한 그림이 없음. Pointer가 어떻게 Tagging되고 있는지 그림이 있으면 좋을 것 같다는 생각이 든다.
Minor Comments
- 평가 workload가 NVIDIA 내부 product group kernel 중심이고, simulator와 수정된 ptxas/NVAS에 의존한다. 논문의 artifact나 workload 접근성이 제한된다면 재현성은 약할 수 있다.
- Root pointer analysis는 intra-procedural이다. CUDA compiler의 aggressive inlining이 도움을 주지만, inter-procedural pointer flow나 복잡한 aliasing에서는 deterministic base-and-bounds coverage가 memory-tagging 수준으로 내려간다.
- HMM/UVM 지원은 discussion 수준이다. HMM에서는 GPU kernel이 `malloc`/`new` host allocation에 접근할 수 있어 allocation universe가 급격히 커지고, CPU allocation metadata와 GPU check를 어떻게 consistency 있게 연결할지가 아직 prototype에 들어가 있지 않다.
- Pointer tag를 host와 공유하려면 CPU host가 TBI/LAM/UAI 같은 upper-address-ignore 기능을 지원해야 한다. 그렇지 않으면 HMM/UVM allocation에 대해서는 temporal-safety coverage를 잃을 수 있다.
- Concurrent metadata update 문제는 완전히 해결되지 않았다. Running kernel이 metadata structure를 traverse하는 동안 `cudaFree`나 HMM allocation update가 일어나는 경우 lock-free structure, atomic synchronization, persistent data structure 같은 추가 설계가 필요하다.
- Threat model은 device-side memory safety bug와 reliable GPU hardware를 가정하고 side/covert channel은 제외한다. 따라서 GPU isolation attack 전체에 대한 방어로 해석하면 안 된다.
- Runtime wrapper overhead는 memory management API 자체에 비해 negligible하다고 보고 simulation overhead에 포함하지 않는다. Allocation-intensive workload에서는 이 가정이 별도 검증을 요구할 수 있다.
Conclusion
이 연구는 GPU memory safety의 비용을 direct-addressing metadata table의 O(1) lookup 문제로만 보지 않고, GPU workload의 small allocation working set과 latency tolerance를 활용하는 metadata-cache 문제로 제시하였다.. GPUArmor는 compiler root pointer analysis, CUDA runtime metadata management, LOADMETA/MEMCHECK ISA, 그리고 작은 per-SM MLB를 결합해 scalable base-and-bounds checking을 평균 2.3% slowdown과 매우 작은 storage overhead로 달성할 수 있음을 보였다.