| Securing GPU via Region-based Bounds Checking | |
|---|---|
| Author | Jaewon Lee, Yonghae Kim, Jiashen Cao, Euna Kim, Jaekyu Lee, Hyesoon Kim |
| Conference | ISCA |
| Year | 2022 |
개요
이 논문은 GPU에서 memory safety 위반, 특히 buffer overflow가 다른 buffer나 CPU가 공유하는 메모리를 훼손할 수 있는 이유를 분석하고, GPU의 병렬 실행 모델에 맞춘 region-based bounds checking으로 이를 낮은 비용으로 방어하는 방법을 다룬다.
Background
GPU kernel은 대규모 SIMT 병렬성을 사용하며, 대부분의 buffer가 kernel launch 전에 kernel argument로 전달된다. 따라서 일반 CPU 프로그램보다 한 kernel이 사용하는 buffer 수가 작고, 같은 workgroup/warp의 thread가 유사한 주소 범위를 동시에 요청하는 경우가 많다. GPUShield는 이 구조를 bounds metadata의 저장·조회와 검사 단위의 공동화에 활용한다.
GPU의 주소 계산은 binding-table 기반 접근, full virtual address, base address + offset의 세 가지 형태로 나타난다. 특히 Intel GPU의 binding-table 접근과 일부 base-plus-offset 접근은 buffer의 base와 offset을 분리하므로, pointer에 bounds 정보를 추가하는 비용을 줄일 여지가 있다.
Motivation
GPU는 deep learning, scientific computing, HPC, cloud virtualization에서 일반-purpose accelerator로 사용되고, Shared Virtual Memory(SVM) 또는 CUDA Unified Memory를 통해 CPU와 GPU가 데이터를 공유한다. 이 변화로 GPU kernel이 encryption key나 image 같은 민감한 데이터를 처리하고, GPU buffer overflow가 host 측 상태와 control flow에 영향을 줄 가능성이 커졌다.
기존 GPU 방어는 주로 canary를 buffer 주변에 삽입하고 kernel 종료 후 훼손 여부를 검사하는 방식이었다. 이 방식은 하드웨어 비용이 작지만 illegal read와 canary 영역을 건너뛰는 non-adjacent write를 놓칠 수 있다. 반대로 소프트웨어 instrumentation과 bounds metadata load는 GPU의 매우 많은 memory operation에 추가 비용을 얹는다. GPU에서는 CPU식 fat pointer, shadow memory, per-thread 검사도 거대한 register file·메모리 bandwidth·동시성 때문에 그대로 적용하기 어렵다.
저자들은 Nvidia CUDA SVM에서 다음 현상을 재현했다. 512B 정렬된 작은 buffer에 대한 경계 밖 write는 같은 512B 영역 안에서는 억제되지만, 같은 2MB 영역 안에서는 허용되고, 2MB 경계를 넘으면 kernel이 illegal memory access로 중단된다. 즉 page-level protection만으로는 object-level spatial safety를 제공하지 못하며, 허용된 overflow가 CPU에서도 관찰될 수 있다.
Importance
이 논문의 연구적 중요성은 GPU memory safety를 단순한 디버깅 도구나 canary 문제가 아니라 GPU architecture와 compiler·driver interface가 함께 해결해야 하는 시스템 문제로 정식화한 데 있다. 저자들이 제안한 GPUShield는 spatial memory safety를 위한 최초의 hardware-based GPU bounds-checking 제안으로 제시된다.
핵심적인 설계 관점은 GPU kernel이 적은 수의 buffer를 사용하고, warp/workgroup 단위로 주소 요청을 모아 처리하며, 대부분의 접근이 반복된다는 점이다. 이를 이용하면 모든 thread마다 bounds metadata를 읽지 않고도 object 단위 보호를 제공할 수 있다. 따라서 bounds checking의 높은 coverage와 GPU 실행 모델에 맞춘 낮은 overhead를 함께 추구한다.
Main Idea
GPUShield의 핵심 아이디어는 각 buffer의 base와 size를 per-kernel Region Bounds Table (RBT)에 저장하고, pointer의 사용되지 않는 상위 bit에 buffer 식별자를 넣어 memory access와 bounds metadata를 연결하는 것이다. GPU driver가 kernel마다 random unique ID와 encryption key를 만들기 때문에, 공격자가 buffer ID를 예측해 다른 buffer의 metadata를 선택하는 pointer-forging 공격을 어렵게 만든다.
그 위에서 compiler는 정적으로 안전하다고 증명할 수 있는 접근을 제거하고, 나머지 접근만 Bounds-Checking Unit (BCU)에서 검사한다. BCU는 한 thread가 아니라 coalesced memory transaction을 만드는 sub-workgroup의 최소·최대 주소 범위를 검사한다. 즉 GPU의 병렬성을 검사 자체에 활용하여, bounds metadata 조회와 주소 변환·load/store pipeline의 latency를 겹치는 것이 설계의 중심이다.
Design
- Per-kernel Region Bounds Table: host-allocated global buffer, local variable, heap에 대한 bounds metadata를 GPU global memory의 RBT에 저장한다. RBT는 14-bit ID로 index하는 16,384-entry direct-mapped table이다. 각 entry는 48-bit base address, 32-bit size, valid/read-only bit, kernel ID를 가진다. kernel별 table과 RBT physical address 보호를 사용해 다른 kernel이 metadata를 임의로 읽는 경로를 제한한다.
- Encrypted pointer metadata: GPU driver가 buffer와 local variable마다 random unique ID를 부여하고, per-kernel key로 암호화한 ID를 pointer의 unused upper bits에 삽입한다. ID는 pointer arithmetic을 따라 전파되며 BCU가 복호화해 RBT를 조회한다. 매 kernel launch마다 key를 바꾸어, 반복 실행에서 노출된 pointer 값을 이용한 ID 추측을 어렵게 한다. 이 방식은 fat pointer와 별도 shadow memory를 피하는 대신, pointer 상위 bit와 driver·하드웨어의 key 보호를 전제로 한다.
- Pointer type과 address mode: compiler 분석 결과에 따라 pointer를 세 종류로 구분한다. Type 1은 정적 분석으로 안전성이 증명되어 runtime check를 생략한다. Type 2는 full virtual address와 encrypted buffer ID를 사용하여 RBT에서 base/size를 찾는다. Type 3은 base-plus-offset이 가능한 경우 pointer에 log2(buffer size)를 넣고 offset 범위를 직접 비교하여 RBT access를 우회한다. 임의 크기 buffer를 위해 power-of-two alignment/padding을 사용할 수 있지만 fragmentation이 생긴다.
- LLVM 기반 static analysis: compiler가 LLVM IR의 GetElementPtr와 operand data-flow를 역추적하여 pointer가 어느 buffer를 가리키는지 식별하고, index의 최대값을 알 수 있는 접근은 compile time에 검사한다. 결과는 bounds-analysis table (BAT)에 기록되어 binary와 함께 driver로 전달된다. 정적으로 안전한 접근은 runtime 검사에서 제외하고, overflow는 compile time에 보고한다. 간접 memory access처럼 값을 정적으로 알 수 없는 경우에는 runtime 검사를 유지한다.
- Warp/workgroup 단위 BCU: BCU는 load-store unit 옆에서 address gathering, RCache hierarchy, address range comparison을 수행한다. 한 sub-workgroup에서 coalescing된 요청의 최소·최대 주소를 구해 buffer 범위와 비교하므로 thread마다 별도 metadata access를 하지 않는다. Type 2는 4-entry L1 RCache와 64-entry fully associative L2 RCache를 사용하고, Type 3은 cache 조회 없이 검사한다. L1 hit은 1 cycle, L2 hit은 3 cycle로 설계되며, 대부분의 경우 LSU·D-TLB pipeline과 겹친다.
- 접근 위반 처리와 보호 범위: precise exception을 지원하면 즉시 fault를 발생시키고, 그렇지 않으면 error를 기록한 뒤 load는 0을 반환하고 store는 버린다. 보호 범위는 kernel argument로 전달된 host buffer별 isolation, local memory에서 thread 간 isolation, kernel 전체에 대해 하나의 영역으로 취급하는 heap isolation이다. 동적 heap buffer 각각을 분리하지 않고 전체 heap chunk를 보호하는 것은 구현 비용을 줄이지만 fine-grained heap isolation을 포기하는 tradeoff다.
Result
- 사례 분석에서 Nvidia CUDA SVM의 object-level out-of-bounds write가 실제로 수행되고 CPU 측에서 관찰될 수 있음을 보였다. 이는 기존 512B 정렬 및 2MB granularity 보호가 buffer별 bounds checking을 대체하지 못한다는 문제 정의를 뒷받침한다.
- MacSim cycle-level simulator로 Nvidia GPU의 88개 CUDA benchmark와 Intel GPU의 17개 OpenCL benchmark를 평가했다. Nvidia 기본 설정은 4-entry·1-cycle L1 RCache와 64-entry·3-cycle L2 RCache이며, 대부분의 benchmark에서 L1 RCache hit rate가 거의 100%에 가까워 baseline 대비 성능 저하가 관찰되지 않았다. Intel 평가에서도 대부분 4-entry L1 RCache로 거의 100% hit rate를 보였다.
- GPUShield는 RCache-sensitive benchmark에서도 작은 cache로 대부분의 buffer metadata를 수용했다. 이는 kernel이 적은 buffer를 사용하고 같은 core의 sub-workgroup이 buffer 접근에 temporal locality를 보인다는 설계 가정을 지지한다. 다만 memory request가 많고 Dcache hit이 높은 streamcluster에서는 L1 RCache miss에 따른 1-cycle bubble이 상대적으로 크게 나타났다.
- compiler static bounds checking은 compile time에 수십 ms를 추가하지만, 단순한 주소 계산이 많은 benchmark에서는 runtime bounds check를 크게 줄이거나 제거했다. 반면 graph benchmark는 indirect access가 많아 정적 분석의 적용 범위가 제한되었고, 이 경우에도 GPUShield의 hardware check가 필요하다.
- 두 kernel을 동시에 실행한 Intel 평가 21개 조합에서 평균 overhead는 inter-core와 intra-core 모두 0.3% 미만이었다. memory-intensive한 bfs와 nn의 inter-core 경우에는 no-bounds-checking baseline 대비 6.2% degradation이 관찰되었으며, 저자들은 static analysis로 이를 완화할 수 있다고 설명한다.
- Rodinia benchmark에서 GPUShield의 평균 overhead는 0.8%로, CUDA-MEMCHECK 72.3배, clArmor 3.1배, GMOD 1.5배보다 작았다. 비교 대상은 각각 JIT/instrumentation, canary, software monitoring 비용을 추가하므로, 이 결과는 BCU가 GPU load/store pipeline에 통합된다는 설계 선택의 효과를 보여준다.
- Synopsys Design Compiler와 45nm FreePDK SRAM 모델로 합성한 추가 구조의 면적은 0.0858 mm², leakage는 799.75 µW, dynamic power는 203.36 mW로 보고되었다. RCache와 comparator를 포함한 저장공간은 GPU 구성에 따라 Nvidia 14.2KB, Intel 21.3KB로 추정되었다. 이 수치는 실제 GPU silicon 측정이 아니라 논문이 설정한 합성·시뮬레이션 모델에 기반한다.
Contribution
- Nvidia CUDA SVM에서 GPU out-of-bounds write가 buffer 간 memory corruption으로 이어지고 CPU에서 관찰될 수 있음을 보였다.
- GPU의 region-based spatial memory safety를 위한 hardware-software cooperative mechanism인 GPUShield를 제안했다.
- RBT, encrypted pointer metadata, LLVM static analysis, warp/workgroup-level address aggregation, GPU-specific address mode를 결합해 bounds-checking 비용을 줄였다.
- Nvidia CUDA와 Intel OpenCL의 총 105개 benchmark 및 multi-kernel 실행에서 낮은 runtime overhead와 작은 hardware overhead를 평가했다.
Criticisms
- 평가는 실제 GPU prototype이나 silicon이 아니라 MacSim cycle-level simulation과 45nm 합성 모델에 기반한다. 따라서 실제 GPU driver integration, timing closure, memory consistency, 제조 공정에서의 비용은 추가 검증이 필요하다.
- heap은 동적 buffer마다 별도 entry를 두지 않고 전체 heap chunk 하나로 보호한다. 그러므로 heap 내부의 서로 다른 allocation 사이 overflow를 object 단위로 격리하지 못하며, fine-grained heap attack에 대한 보장은 제한적이다.
- 보호 목표가 spatial memory safety에 집중되어 있어 use-after-free 같은 temporal safety와 arbitrary pointer provenance 문제를 해결하는 설계는 아니다. 특히 pointer 상위 bit tagging과 per-kernel key 보안은 지원되는 virtual-address 형식과 신뢰할 수 있는 driver/hardware를 전제로 한다.
- 14-bit buffer ID와 per-kernel RBT는 일반적인 GPU kernel의 적은 buffer 수에는 적합하지만, 미래의 programming model에서 kernel argument·local variable 수가 늘어나면 ID 공간과 RCache 용량이 병목이 될 수 있다. 저자도 buffer ID 공유·bounds merge, RCache partitioning을 확장 방향으로 제시한다.
- threat model은 trusted GPU driver와 hardware를 가정하고 side-channel·covert-channel 공격을 제외한다. 따라서 GPUShield의 결과를 multi-tenant GPU 전체의 보안성으로 일반화할 수는 없다.
- static analysis는 수십 ms의 compile-time 비용을 추가하고 indirect access에는 제한적이다. 또한 논문의 주요 결과는 평균값과 대표 benchmark 중심이므로, RCache thrashing·kernel context switching·더 다양한 allocator와 compiler pipeline에서의 overhead breakdown은 더 필요하다.
- software detector와의 비교는 유용하지만, 각 도구의 기능·정확도·탐지 시점이 동일하지 않다. GPUShield의 0.8% overhead가 곧 모든 runtime memory-safety 도구보다 우월하다는 의미는 아니며, precise exception 지원 여부와 위반 보고 latency도 deployment 조건에 따라 달라진다.
Conclusion
이 연구는 GPU buffer overflow를 page-level isolation이나 kernel 종료 후 canary 검사의 문제로 두지 않고, GPU의 buffer 수·SIMT 실행·memory coalescing을 이용하는 architecture/compiler/driver 공동 설계 문제로 바라보게 한다. GPUShield는 RBT와 encrypted pointer metadata로 buffer identity를 유지하고, static analysis와 warp-level BCU로 object bounds를 검사하여 105개 benchmark에서 낮은 overhead를 보고했다. 따라서 GPU가 CPU와 메모리를 공유하고 보안 민감한 workload를 처리하는 환경에서 GPU memory safety와 hardware-assisted bounds checking을 연결하는 출발점으로 기억할 가치가 있다.