È stato presentato il progetto bpftime, che sviluppa un runtime e una macchina virtuale per l'esecuzione di gestori eBPF nello spazio utente. Bpftime consente l'esecuzione completa di programmi eBPF per tracciamento e interazione con i processi, utilizzando funzionalità come uprobe e intercettazione programmata delle chiamate di sistema. Si osserva che, grazie all'eliminazione di passaggi di contesto non necessari, bpftime consente di ottenere una riduzione dieci volte più bassa delle spese generali rispetto all'utilizzo delle funzionalità uprobe e uretprobe fornite dal kernel Linux. Inoltre, bpftime semplifica notevolmente il debug, può potenzialmente essere utilizzato su sistemi privi di kernel Linux e non richiede privilegi elevati per caricare applicazioni eBPF nel kernel. Il codice del progetto è scritto nei linguaggi C/C++ e distribuito con licenza MIT.
L'intercettazione delle chiamate di sistema e l'integrazione dei controlli uprobe sono state realizzate utilizzando una tecnica di riscrittura del codice eseguibile (binary rewriting), in cui le chiamate alle funzioni di sistema, ai punti di entrata e alle funzioni locali vengono sostituite da un passaggio ai gestori di debug tramite la modifica del codice macchina dell'applicazione in esecuzione, risultando significativamente più efficace rispetto all'organizzazione dell'intercettazione con uprobe a livello del kernel Linux.
Sono supportate operazioni di sostituzione o modifica delle funzioni, attacco di gestori (hook) e filtri, reindirizzamento, blocco o sostituzione dei parametri delle chiamate di sistema, intercettazione dei punti di entrata e uscita dalle funzioni, oltre alla sostituzione del gestore tramite uno spostamento arbitrario nel codice. Bpftime può essere attaccato a qualsiasi processo in esecuzione nel sistema senza necessità di riavviarli o ricompilarli. L'inserimento di bpftime nei processi può avvenire per processi in esecuzione tramite ptrace, e per quelli caricati tramite LD_PRELOAD.
All'interno di bpftime si sviluppa un runtime che consente di attaccare programmi eBPF ai punti di tracciamento delle chiamate di sistema e uprobe; la macchina virtuale eBPF con JIT per l'esecuzione isolata di programmi eBPF a livello del processo utente (è supportata anche la compilazione AOT); un processo in background per interagire con il kernel e organizzare la compatibilità con la sottosistema uprobe del kernel (bpftime supporta la modalità di caricamento di eBPF nello spazio utente dal kernel per consentire la cooperazione con i programmi eBPF nel kernel, utilizzati, ad esempio, per la gestione di kprobe o per impostare filtri di rete).
La macchina virtuale eBPF è presentata sotto forma di libreria dinamica e fornisce un'API simile a ubpf, che consente di utilizzarla per integrare la funzionalità eBPF in altri progetti. Per l'aggregazione totale dei dati da più processi è supportata la creazione di eBPF Map condivisi, collocati nella memoria condivisa. Insieme a bpftime è possibile utilizzare gestori eBPF standard, scritti per l'uso nel kernel, e per la compilazione possono essere utilizzati strumenti standard basati su clang e libbpf.
Con bpftime nello spazio utente possono essere eseguiti sistemi di tracciamento come BCC, bpftrace e Deepflow. Ad esempio, è stato dimostrato l'uso dello script sslsniff dal framework BCC per analizzare e salvare il traffico crittografato in nginx. Nei test condotti, le prestazioni di nginx durante l'esecuzione di sslsniff a livello kernel scendono del 58%, mentre eseguendo il gestore nello spazio utente diminuiscono del 12,3%.
Architettura di tracciamento dei processi utilizzando l'originale eBPF nel kernel:

Architettura di tracciamento nello spazio utente utilizzando bpftime:

Modalità ibrida, in cui bpftime lavora insieme a eBPF nel kernel, ad esempio per impostare filtri di rete o per portare specifici gestori nello spazio utente:

Tra i piani futuri si segnala: la possibilità di sostituzione delle eccezioni (Fault Injection); il patching a caldo (Hot Patching) per modificare la logica operativa o correggere errori nelle build binarie; la creazione di un modulo per Nginx, che permetta di creare estensioni mediante programmi eBPF (ad esempio, per la selezione dinamica dei percorsi, caching, applicazione di politiche di sicurezza e bilanciamento del carico); l'espansione delle capacità del sottosistema FUSE (ad esempio, la creazione di estensioni a FS per caching o gestione degli accessi, sotto forma di programmi eBPF).
Fonte: opennet.ru
