다른 명령
Nori: update draft.wikitext |
편집 요약 없음 |
||
| 62번째 줄: | 62번째 줄: | ||
최종적으로 여러 Per-tenent policy가 Global policy랑 Conflict될때 어떻게 Sound하게 처리할지에 대한 이야기가 없다. 만약 Global policy상 이 Process는 schedule out시켜야 하는데 per-tenent eBPF program이 schedule out시키지 않아야 한다고 주장하면 어떻게 할 것인가? Virtualization이 이 모든 Specific case를 보장할 수 있을 것인가? 어떤식으로 보장되는지 Restrict할수 있을 것인가? | 최종적으로 여러 Per-tenent policy가 Global policy랑 Conflict될때 어떻게 Sound하게 처리할지에 대한 이야기가 없다. 만약 Global policy상 이 Process는 schedule out시켜야 하는데 per-tenent eBPF program이 schedule out시키지 않아야 한다고 주장하면 어떻게 할 것인가? Virtualization이 이 모든 Specific case를 보장할 수 있을 것인가? 어떤식으로 보장되는지 Restrict할수 있을 것인가? | ||
=== Major Concerns === | |||
논문에는 또한 State overlay의 Semantic이 정의되어 있지 않다. 논문은 tenant별 patch가 어떻게 capture되고, 적용되며, 다시 되돌려지는지는 설명한다. 그러나 이러한 virtual modification이 실제 kernel의 canonical state와 어떻게 상호작용하는지는 불분명하다. 특히 여러 tenant가 동일한 shared object를 수정하는 경우, 그중 어떤 변경사항이 이후 kernel execution에 실제로 반영되는지가 명확하지 않다. | |||
더 중요한 문제는 tenant patch와 kernel이 정상적으로 수행하는 state update가 서로 어떻게 조정되는가이다. 예를 들어 overlay가 적용되는 사이 또는 동시에 kernel이 같은 object를 수정한다면, 두 변경사항 중 어떤 것이 우선하며 어떻게 병합되는지 설명되어 있지 않다. Discussion에서는 현재 설계가 exclusive access를 가정하며 RCU reader를 명시적으로 다루지 않는다고 인정하고 있지만, exclusive access만으로는 kernel과 tenant가 충돌하는 update를 어떻게 commit하거나 revert할지는 다루지 않는다. | |||
[[분류: USENIX OSDI]] | [[분류: USENIX OSDI]] | ||
2026년 9월 2일 (수) 06:23 기준 최신판
| Virtualizing eBPF with Late-Binding | |
|---|---|
| Author | Jing Zhang, Xiaguannan Song, Dong Du, Yubin Xia, Binyu Zang, Haibo Chen |
| Conference | 20th USENIX Symposium on Operating Systems Design and Implementation (OSDI '26) |
| Year | 2026 |
개요
이 논문은 단일 trust domain을 전제로 설계된 eBPF를 여러 tenant가 공유하면 왜 hook·execution context·kernel state 충돌이 발생하며, physical hook과 logical program의 결합을 실행 시점까지 미루는 late binding으로 이를 어떻게 가상화할 수 있는지를 다루었다.
Motivation
점점 eBPF를 Process의 Customization을 위해서 사용되는 경우가 많아지고 있다. 그러나 native eBPF의 program은 load/attach 시 단순히 커널의 함수에 Hooking되기 때문에 Per-process context가 아니라 Kernel-context하에서 실행된다.
- Singleton conflict:
struct_ops, 일부 LSM hook,sched_ext, eBPF iterator처럼 global callback을 교체하는 hook은 한 program만 수용하므로 tenant별 policy가 공존할 수 없다. - Functionality conflict: 같은 hook의 program은 packet header, return value, shared object 같은 execution context를 순차적으로 관찰·변경한다. 각 program이 단독으로는 올바르더라도 조합되면 forwarding loop나 후속 program의 관측 오염이 발생할 수 있다.
- Performance interference: tenant scope와 무관한 program도 매 event마다 실행된다. 특히 global
sys_entertracepoint에서는 한 tenant의 tracing 비용을 다른 모든 tenant가 부담하며, program 수가 늘수록 비용이 선형 증가한다.
기존 시스템은 다음과 같은 이유로 인해서 Per-process eBPF context에는 적합하지 않다.
- In-program filtering은 모든 program을 먼저 호출해야 하고 tenant가 filter를 빼는 것을 막지 못한다.
- cgroup BPF는 process context에는 유용하지만 XDP 같은 interrupt-context event와 singleton hook을 포괄하지 못하며 state도 분리하지 않는다.
- 중앙 orchestrator가 program을 하나의 binary나
tail_callchain으로 합치면 global policy와 verifier 부담, update latency, API 종속성이 생긴다.
Main Idea
본 논문은 eBPF hook에서 직접 eBPF프로그램을 실행시키는 것이 아니라 generic interposition point로 바꾸고, event가 발생한 뒤 그 event의 tenant를 식별하여 실행하게 하였다.
각 process는 hierarchical eBPF namespace에 속한다. Event가 발생하면 vBPF Sniffer가 해당 namespace를 찾고, Dispatcher가 namespace에 등록된 program과 관찰 권한이 있는 eBPF program만 선택한다. 이어 state isolation framework가 “global” state access를 tenant-private instance 또는 semantic overlay로 redirect한다. 결과적으로 같은 physical hook을 공유해도 tenant마다 독립적인 eBPF environment가 보인다.
최종적으로, vBPF의 핵심 경로는 다음과 같다.
physical event → tenant attribution → eBPF namespace
→ tenant program set + virtual state view → execution
Challenge
- Interrupt-context attribution: packet arrival이나 I/O completion에는 신뢰할 process context가 없다. Process-context resource creation과 interrupt-time representation 사이를 잇는 stable key와 lifecycle 관리가 필요하다.
- Scalable dispatch: native hook의 linked list/array를 tenant filter와 함께 순회하면 program 또는 tenant 수에 따라 [math]\displaystyle{ O(N) }[/math] 비용이 발생한다. Critical path에서는 tenant program의 직접 lookup이 필요하다.
- State isolation의 성능과 유지보수성: page-table switching은 너무 무겁고, kernel state access마다 수작업으로 tenant logic을 넣으면 Linux 변화에 취약하다. 어떤 interface가 shared state를 변경하는지 compile time에 통제하면서, runtime에는 작은 object 단위의 virtual view를 제공해야 한다.
Design
- Hierarchical eBPF namespace와 execution context
- vBPF는 cgroup과 독립적인 namespace를 process에 결합하며
clone,unshare,setnssemantics를 지원한다. Sidecar와 application처럼 resource group은 공유하되 tracing policy는 분리할 수 있고, child-to-root 순서로 program을 실행하여 host namespace가 tenant event를 audit·modify·veto할 수 있다. 이 hierarchy는 tenant isolation과 platform-level visibility를 동시에 보존하지만, parent가 child의 context와 return code에 미치는 우선순위를 policy로 명시해야 한다.
- vBPF는 cgroup과 독립적인 namespace를 process에 결합하며
- vBPF Sniffer: resource-based two-phase attribution
- Process context에서
bind(),connect(), I/O submission처럼 resource가 설정될 때 Sniffer가 stable resource key와 namespace의 mapping을insert한다. Interrupt context에서는 packet header,bio/request등의 reader가 같은 identity를 추출하고resolve하여 namespace를 얻는다. Network sniffer는 5-tuple을 connection setup 단계에 따라 정제하고, request sniffer는 split/merge 시 namespace set을 전파하며, task sniffer는 teardown 뒤늦게 발생한 event를 처리한다. 모든 interrupt를 attribution하지 않고 tenant-bearing resource만 추적하며, tenant owner가 없는 platform interrupt는 host namespace에 남긴다.
- Process context에서
- vBPF Dispatcher: virtual hook multiplexer와 hash index
- Physical hook에는 tenant program 대신 하나의 multiplexer를 두고, namespace를 key로 하는 hash table에서 해당 program array를 직접 찾는다. Native eBPF의 global linear traversal을 tenant별 [math]\displaystyle{ O(1) }[/math] lookup과 cache-friendly sequential execution으로 바꾸므로 unrelated program 호출을 제거한다. Namespace ancestor chain은 attachment 시 flattened array로 미리 계산할 수 있다. 이는 runtime recursion을 없애는 대신 update 때 array를 atomically 재구성하고 namespace별 memory를 추가로 사용한다.
- Compiler-enforced state isolation
- Customized Clang frontend의 static analyzer는 verifier-visible helper와 kfunc를 state-access checkpoint로 사용한다.
vbpf_helper로 interface를 식별하고, global state를 변경할 가능성이 있는 call은 default-deny하며, kernel developer가vbpf_safe로 승인한 경우만 허용한다. Pointer argument는 verifier의 type·memory-region 정보를 이용해 tenant-private/read-only 여부를 분류한다. 이 방식은 tenant code가 임의의 shared state mutation path를 우회하지 못한다는 invariant를 세우지만, annotation과 analyzer soundness를 trusted computing base에 포함한다.
- Customized Clang frontend의 static analyzer는 verifier-visible helper와 kfunc를 state-access checkpoint로 사용한다.
- vBPF variables library
- 복제 가능한 static state는 declarative API로 namespace별 instance를 lazy allocation한다. 여러 field와 이를 보호하는 lock은 한 storage group으로 배치해 일관되게 resolve한다. 반면
sk_buff처럼 복제할 수 없는 shared kernel object에는 BTF layout을 이용한 semantic state overlay를 적용한다. Helper 실행 전 clean base state를 복원하고 tenant patch를 apply하며, 실행 중 capture 또는 field-level update로 변경분을 기록한 뒤 namespace patch set에 finalize한다. Context switch 때 patch를 원자적으로 apply/restore하여 tenant별 view를 제공하며, full memory snapshot 대신 의미 있는 field 변경만 저장한다.
- 복제 가능한 static state는 declarative API로 namespace별 instance를 lazy allocation한다. 여러 field와 이를 보호하는 lock은 한 storage group으로 배치해 일관되게 resolve한다. 반면
- Hot-path implementation
- Sniffer registry와 Dispatcher table은 RCU-protected hash table로 구현하여 lookup을 lock-free로 수행한다. Critical-section allocation 실패와 latency variation을 줄이기 위해 object pool은 preallocate하고, tenant-private state는 첫 접근까지 lazy allocation한다. Hierarchy path와 BTF layout은 setup 시 precompute한다. 구현 규모는 Linux 6.12 kernel 약 12 KLoC와 LLVM 20 Clang plugin 약 1 KLoC이다.
Conclusion
본 논문은 eBPF의 per-kernel context가 아니라 per-process context로 확장 시켜야 된다고 주장한다. 논문에서 제시하는 Motivation은[1] 기존 논문이 다루지 않았다는 점에서 신선하였다. 그러나 본 논문은 필연적으로 커널의 여러 부분을 건드리기 때문에 여러 Corner case에 대한 의문이 생길 수 밖에 없었다.
우선, Motivation에서 궁금한 점이 있다. eBPF는 Per-process context로 실행시키는 프로그램이 있으면 새로운 BPF program type을 만들면 된다고 생각한다. XDP나 Storage같은 Device장치의 가속은 보통은 Per-process가 아니라 Per-kernel에서 작동해야만 효율적인 경우가 대부분인다. 따라서 eBPF의 현재 Execution model이 진짜 Per-process를 Support못해서가 아닌, 그렇게 하는 경우가 대부분인기 떄문에 지원하지 않는다고 보는 것이 좋아보인다. 그리고 eBPF는 기본적으로 CAP_BPF아니면 Root privileged를 요구하는 경우가 많다. 이 경우 단순히 그냥 namespace만 manual하게 체크할 수 있도록 하는 hook을 추가하면 될지 않을까 하는 생각이 든다.
Correctness측면에서의 의문은, 또한 Namespace로 묶는 vBPF Sniffer는 결국 Interrupt context -> task의 backward mapping인데, 이 부분이 모든 BPF Program type마다 다를텐데, 이를 어떻게 일이 태깅 하냐의 문제가 생길 것이라 생각한다. 또한 논문에서 주장하는 TCB에서 DoS가 빠져있는데, DoS는 Per-process context에서 매우 중요한 Threat model이다. 이를 뺴면 안된다고 생각한다. 또한 만약 Process에서 먼저 실행되지 않는 Context가 있으면 이는 어떻게 처리할 것인가? Kfunc는 이미 2022년 이후로 Default가 되었고, Helper function만 지원한다는 것도 큰 Limitation이다. Concurrency는 어떻게 할지도 의문이 남는다. RCU Lock을 잡고 Resource를 건들고 있다고 해보자. State overlay같은 경우에는 두개의 복사본이 있는 것인데, RCU Lock을 두 스레드가 공유하고 있다면? Merge할때 분명히 관련하여 문제가 생길것이라고 생각한다.
성능면에서는, 그리고 namespace lookup에 10ns-100ns가 걸린다고 하였는데, 이는 100Gb/s가 넘어가는 현재 시스템에 적용하기에는 큰 오버헤드이다.
최종적으로 여러 Per-tenent policy가 Global policy랑 Conflict될때 어떻게 Sound하게 처리할지에 대한 이야기가 없다. 만약 Global policy상 이 Process는 schedule out시켜야 하는데 per-tenent eBPF program이 schedule out시키지 않아야 한다고 주장하면 어떻게 할 것인가? Virtualization이 이 모든 Specific case를 보장할 수 있을 것인가? 어떤식으로 보장되는지 Restrict할수 있을 것인가?
Major Concerns
논문에는 또한 State overlay의 Semantic이 정의되어 있지 않다. 논문은 tenant별 patch가 어떻게 capture되고, 적용되며, 다시 되돌려지는지는 설명한다. 그러나 이러한 virtual modification이 실제 kernel의 canonical state와 어떻게 상호작용하는지는 불분명하다. 특히 여러 tenant가 동일한 shared object를 수정하는 경우, 그중 어떤 변경사항이 이후 kernel execution에 실제로 반영되는지가 명확하지 않다.
더 중요한 문제는 tenant patch와 kernel이 정상적으로 수행하는 state update가 서로 어떻게 조정되는가이다. 예를 들어 overlay가 적용되는 사이 또는 동시에 kernel이 같은 object를 수정한다면, 두 변경사항 중 어떤 것이 우선하며 어떻게 병합되는지 설명되어 있지 않다. Discussion에서는 현재 설계가 exclusive access를 가정하며 RCU reader를 명시적으로 다루지 않는다고 인정하고 있지만, exclusive access만으로는 kernel과 tenant가 충돌하는 update를 어떻게 commit하거나 revert할지는 다루지 않는다.
- ↑ Section 2.2 From Platform to Tenant eBPF