Repository navigation
XDP Verifier cannot guarantee access to array is safe #765
Replies: 3 comments 7 replies
|
@RyanGlScott Can you please look into this? I think we could change if needed to be modulo length of the array.If the delay it adds is not tolerable (which I doubt), we could make that an option of the C backend. |
|
I have been spending some time trying to familiarize myself with how to use eBPF in order to figure out how robust the proposed fix in #765 (comment) is. In the process of doing so, I believe I've encountered a Copilot spec for which the proposed fix does not work. This may just reflect my lack of understanding of eBPF, however, so I wanted to double-check here. First, some preliminaries. Let's devise a Copilot spec which exhibits the indexing-related eBPF problem and would be fixed if the proposed fix above were implemented: Details{-# LANGUAGE NoImplicitPrelude #-}
module Main (main) where
import Copilot.Compile.C99
import Language.Copilot
bools :: Stream Bool
bools = [False, True] ++ not bools
spec :: Spec
spec = trigger "example" bools []
main :: IO ()
main = do
spec' <- reify spec
compile "example" spec'When run (with Details#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "example_types.h"
#include "example.h"
static bool s0[(2)] = {(false), (true)};
static size_t s0_idx = (0);
static bool s0_get(size_t x) {
return (s0)[((s0_idx) + (x)) % ((size_t)(2))];
}
static bool s0_gen(void) {
return !((s0_get)((0)));
}
static bool example_0_guard(void) {
return (s0_get)((0));
}
void step(void) {
bool s0_tmp;
if ((example_0_guard)()) {
{(example)();}
};
(s0_tmp) = ((s0_gen)());
((s0)[s0_idx]) = (s0_tmp);
(s0_idx) = (((s0_idx) + ((size_t)(1))) % ((size_t)(2)));
}We will pair this generated code with the following, handwritten entrypoint code ( Details#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include "example.c"
void example() {}
SEC("socket")
int main(void) {
step();
return 0;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";(Note that the use of On my Ubuntu 24.04 virtual machine, the easiest way to observe an eBPF-related verifier issue is to use On the other hand, if I apply the following change to --- example-old.c 2026-10-11 14:14:36.015474311 +0000
+++ example.c 2026-10-11 14:14:45.545386496 +0000
@@ -28,6 +28,6 @@
{(example)();}
};
(s0_tmp) = ((s0_gen)());
- ((s0)[s0_idx]) = (s0_tmp);
+ ((s0)[s0_idx % 2]) = (s0_tmp);
(s0_idx) = (((s0_idx) + ((size_t)(1))) % ((size_t)(2)));
}Then the Where things get strange is if I tweak the Copilot spec to introduce a delay of three samples instead of two: --- Spec-old.hs 2026-10-11 14:12:59.890273390 +0000
+++ Spec.hs 2026-10-11 14:15:22.775950134 +0000
@@ -5,7 +5,7 @@
import Language.Copilot
bools :: Stream Bool
-bools = [False, True] ++ not bools
+bools = [False, True, False] ++ not bools
spec :: Spec
spec = trigger "example" bools []For the sake of completeness, here is the Copilot-generated C code ( #include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "example_types.h"
#include "example.h"
static bool s0[(3)] = {(false), (true), (false)};
static size_t s0_idx = (0);
static bool s0_get(size_t x) {
return (s0)[((s0_idx) + (x)) % ((size_t)(3))];
}
static bool s0_gen(void) {
return !((s0_get)((0)));
}
static bool example_0_guard(void) {
return (s0_get)((0));
}
void step(void) {
bool s0_tmp;
if ((example_0_guard)()) {
{(example)();}
};
(s0_tmp) = ((s0_gen)());
((s0)[s0_idx]) = (s0_tmp);
(s0_idx) = (((s0_idx) + ((size_t)(1))) % ((size_t)(3)));
}Once again, if you run the eBPF-related commands above, then the verifier will reject it. I attempted to apply the same fix to this generated C code, this time using --- example-old.c 2026-10-11 14:16:02.062710592 +0000
+++ example.c 2026-10-11 14:16:09.052379632 +0000
@@ -28,6 +28,6 @@
{(example)();}
};
(s0_tmp) = ((s0_gen)());
- ((s0)[s0_idx]) = (s0_tmp);
+ ((s0)[s0_idx % 3]) = (s0_tmp);
(s0_idx) = (((s0_idx) + ((size_t)(1))) % ((size_t)(3)));
}Surprisingly, the verifier will still reject this code even after the fix is applied! It's not obvious to me why the array length would matter here, but it does. I haven't managed to figure out a way to get the verifier to accept this particular version of the monitor code. Any ideas? |
|
After talking a bit more to a colleague who knows more about eBPF than I do, I am increasingly convinced that if we want the code that Copilot generates to pass the eBPF verifier, then we should be using the Unfortunately,
This means that fixing this is likely not as simple as tweaking a single line of the C code. It would still worth doing, but this would likely need to be a more invasive "BPF code generation mode" that affects mutiple parts of |
Uh oh!
There was an error while loading. Please reload this page.
I'm using a Copilot spec to create an XDP (eBPF) program and I run into an error where the XDP verifier does not consider the index to be guaranteed within bounds of the array.
A similar piece of generated c code is below:
Specifically it complains about this line:
If instead, it is written as:
then it works.
All reactions