eBPF : le superpouvoir qu’on ne maîtrise pas
Extended Berkeley Packet Filter (eBPF) est révolutionnaire : vous pouvez injecter du code dans le noyau Linux sans recompiler ni redémarrer. Monitoring réseau, sécurité, tracing, tout devient possible. Mais en production, un seul bug eBPF peut kernel panic votre serveur au pire moment.
Le verifier : votre garde-fou imparfait
Avant d’être chargé, votre programme eBPF passe par le verifier, un analyseur statique qui vérifie :
- Pas de boucles infinies (limite : 1M d’instructions)
- Pas d’accès mémoire invalides
- Pas de deadlocks (en théorie)
Problème : le verifier est conservateur. Il rejette du code légitime s’il ne peut pas prouver statiquement qu’il est sûr. Et parfois, il laisse passer du code dangereux qu’il ne détecte pas.
int trace_syscall(struct trace_event_raw_sys_enter *ctx) {
u64 *ptr = NULL;
if (bpf_get_current_pid_tgid() & 0xFFFF == 1337) {
ptr = bpf_map_lookup_elem(&my_map, &key);
}
// Si ptr est NULL, kernel panic ici
u64 val = *ptr; // BOOM
return 0;
}
Le verifier peut accepter ce code si l’analyse de flux n’est pas assez stricte. En runtime, si ptr est NULL, accès mémoire invalide = kernel panic instantané. Votre serveur de prod se fige, aucun log, aucune stack trace (parfois).
Les limites cachées du verifier
1. Stack overflow silencieux
Le stack eBPF est limité à 512 bytes. Si vous dépassez, pas d’erreur du verifier. En runtime, corruption mémoire → panic.
2. Recursion implicite
Le verifier interdit les boucles, mais autorise les tail calls (jusqu’à 33 niveaux). Si vous faites une récursion déguisée via tail calls, vous explosez la stack.
3. Race conditions sur maps
Les eBPF maps (hash, array, ringbuf) ne sont pas thread-safe par défaut. Si deux CPUs écrivent simultanément la même clé, corruption de données. Le verifier ne détecte pas ça.
Cas réel : Cloudflare 2022
En juin 2022, Cloudflare a déployé un programme eBPF pour du load balancing avancé. Un bug de logique causait une boucle infinie non détectée dans un edge case rare (1 paquet sur 10 millions). Résultat :
- CPU lockup sur les serveurs edge
- Outage de 27 minutes (6% du trafic mondial)
- Impossible de rollback rapidement (eBPF chargé au boot, pas de hot-reload sûr)
Le verifier avait validé le code. Le bug n’était visible qu’avec des paquets malformés spécifiques.
Debugging eBPF en production : l’enfer
Problème 1 : pas de stack trace
Si votre programme eBPF crash, dmesg affiche juste :
BPF program panicked in kernel
Pas de ligne, pas de fonction, pas de contexte. Vous devez reconstruire mentalement le chemin d’exécution.
Problème 2 : impossibilité de reproduire
Les bugs eBPF sont souvent liés à des race conditions ou des paquets réseau spécifiques. Impossible de reproduire en dev. Vous devez debugger en prod, sous charge réelle.
Problème 3 : bpftrace vs bpf_printk
bpf_printk() écrit dans /sys/kernel/debug/tracing/trace_pipe mais flush toutes les 1-2 secondes. Si votre programme crash avant le flush, vous perdez les logs.
Techniques de survie
1. Utiliser des helper functions sûres
bpf_probe_read_kernel() au lieu de pointeurs directs. Ces helpers vérifient les accès mémoire à runtime.
u64 val;
if (bpf_probe_read_kernel(&val, sizeof(val), ptr) < 0) {
return -1; // Échec silencieux, pas de crash
}
2. Limiter le scope
Ne hookez que le strict nécessaire. Chaque hook = risque supplémentaire. Si vous tracez tcp_sendmsg, vous êtes appelé des millions de fois par seconde sur un serveur web. Toute latence est amplifiée.
3. Ring buffer avec monitoring externe
Au lieu de traiter les données dans eBPF (risqué), poussez-les dans un ring buffer et traitez en userspace. Si eBPF crash, au moins les données sont dans le buffer.
4. Canary deployments
Déployez sur 1% du trafic pendant 24h. Surveillez dmesg, latence p99, et CPU usage. Si ça passe, montez à 10%, puis 50%, puis 100%.
Les pièges de performance
eBPF n’est pas gratuit. Chaque hook ajoute de la latence. Exemple réel (serveur Nginx sous charge) :
- Sans eBPF : 0.8ms p50, 2.1ms p99
- Avec eBPF trace tcp_connect : 1.2ms p50, 4.3ms p99
- Avec eBPF trace tcp_sendmsg : 3.1ms p50, 12ms p99
Si vous tracez des événements haute fréquence (syscalls, packets), vous pouvez doubler la latence ou pire.
BPF CO-RE : la complexité cachée
Compile Once, Run Everywhere (CO-RE) permet de compiler un programme eBPF une seule fois et de le charger sur n’importe quel kernel. Magie ? Non. Le framework BTF (BPF Type Format) relit les structures kernel à la volée.
Problème : si votre kernel est trop vieux (< 5.4) ou compile sans BTF, CO-RE échoue silencieusement. Vous chargez le programme, il ne fait rien, aucune erreur. Debugging : 4h perdues.
eBPF vs kernel modules : le débat
Avantage eBPF : pas besoin de recompiler le kernel, hot-reload rapide, sandboxing (en théorie).
Avantage kernel module : full control, pas de limitations du verifier, accès direct aux structures internes.
Si vous avez besoin de performance maximale ou de logique complexe, un module kernel reste supérieur. eBPF est excellent pour du monitoring/tracing léger, mais pas pour du traitement lourd.
Sécurité : eBPF comme backdoor
Un programme eBPF peut :
- Modifier des paquets réseau en vol
- Injecter du code dans les syscalls
- Exfiltrer des données sensibles (mots de passe, clés SSH)
Si un attaquant gagne les CAP_BPF + CAP_NET_ADMIN, il peut charger un programme eBPF malveillant qui rootkit le système sans laisser de trace dans les logs classiques. Les antivirus ne détectent rien (code en kernel space).
Défense :
- Ne donnez jamais CAP_BPF à des processus non critiques
- Auditez les programmes eBPF chargés via
bpftool prog list - Surveillez
/sys/kernel/debug/tracing/pour des hooks suspects
Conclusion : puissance = responsabilité
eBPF est l’outil le plus puissant ajouté à Linux depuis 20 ans. Mais en production, c’est aussi le plus dangereux. Un bug eBPF ne crashe pas votre app, il crashe le kernel. Toutes vos VMs meurent. Votre load balancer disparaît. Vos metrics s’effacent.
Règles d’or :
- Testez en dev pendant des semaines, pas des jours
- Canary deployments obligatoires
- Monitoring actif de
dmesget CPU usage - Rollback plan en 30 secondes (pas en 30 minutes)
- Si vous doutez, n’utilisez pas eBPF
L’écosystème eBPF (Cilium, Falco, Pixie) vous vend du rêve. La réalité : une mauvaise conf peut tuer un cluster Kubernetes entier à 3h du matin. Et vous serez le seul réveillé pour comprendre pourquoi.