Skip to content

Commit 29f1245

Browse files
mejedilmb
authored andcommitted
prog: get context out from syscall program
Linux syscall program errors on non-nil ctxOut, uses ctxIn for both input and output [1]. We have separate Context and ContextOut fields in RunOptions. It should be possible to capture output from syscall programs which is currently not: non-nil ContextOut triggers EINVAL, while Context is (expectedly) not updated. Implement the following semantics: either Context, or ContextOut or both can be non-nil. Map to ctxIn internally, complain if Context and ContextOut lengths are different. [1] https://elixir.bootlin.com/linux/v6.15.2/source/net/bpf/test_run.c#L1530 Signed-off-by: Nick Zavaritsky <[email protected]>
1 parent cfddc9e commit 29f1245

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

prog.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,16 @@ func (p *Program) run(opts *RunOptions) (uint32, time.Duration, error) {
868868
Cpu: opts.CPU,
869869
}
870870

871+
if p.Type() == Syscall && ctxIn != nil && ctxOut != nil {
872+
// Linux syscall program errors on non-nil ctxOut, uses ctxIn
873+
// for both input and output. Shield the user from this wart.
874+
if len(ctxIn) != len(ctxOut) {
875+
return 0, 0, errors.New("length mismatch: Context and ContextOut")
876+
}
877+
attr.CtxOut, attr.CtxSizeOut = sys.TypedPointer[uint8]{}, 0
878+
ctxOut = ctxIn
879+
}
880+
871881
retry:
872882
for {
873883
err := sys.ProgRun(&attr)

prog_linux_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/cilium/ebpf/asm"
1414
"github.com/cilium/ebpf/internal"
15+
"github.com/cilium/ebpf/internal/sys"
1516
"github.com/cilium/ebpf/internal/testutils"
1617
"github.com/cilium/ebpf/internal/unix"
1718
)
@@ -135,3 +136,37 @@ func TestProgramVerifierLogLinux(t *testing.T) {
135136
})
136137
qt.Assert(t, qt.IsTrue(len(prog.VerifierLog) > minVerifierLogSize))
137138
}
139+
140+
func TestProgramTestRunSyscall(t *testing.T) {
141+
testutils.SkipOnOldKernel(t, "5.14", "BPF_PROG_TYPE_SYSCALL")
142+
143+
prog := mustNewProgram(t, &ProgramSpec{
144+
Type: Syscall,
145+
Flags: sys.BPF_F_SLEEPABLE,
146+
License: "MIT",
147+
Instructions: []asm.Instruction{
148+
// fn (ctx *u64) { *ctx++; return *ctx }
149+
asm.LoadMem(asm.R0, asm.R1, 0, asm.DWord),
150+
asm.Add.Imm(asm.R0, 1),
151+
asm.StoreMem(asm.R1, 0, asm.R0, asm.DWord),
152+
asm.Return(),
153+
},
154+
}, nil)
155+
156+
// only Context
157+
rc, err := prog.Run(&RunOptions{Context: uint64(42)})
158+
testutils.SkipIfNotSupported(t, err)
159+
if err != nil {
160+
t.Fatal(err)
161+
}
162+
qt.Assert(t, qt.Equals(rc, 43))
163+
164+
// Context and ContextOut
165+
out := uint64(0)
166+
rc, err = prog.Run(&RunOptions{Context: uint64(99), ContextOut: &out})
167+
if err != nil {
168+
t.Fatal(err)
169+
}
170+
qt.Assert(t, qt.Equals(rc, 100))
171+
qt.Assert(t, qt.Equals(out, 100))
172+
}

0 commit comments

Comments
 (0)