From 5b7b3fa9a4d8ca4ad08d6648752e28ee66f0ffae Mon Sep 17 00:00:00 2001 From: Ophestra Date: Mon, 3 Feb 2025 18:10:29 +0900 Subject: helper/seccomp: implement reader interface via pipe This also does not require the libc tmpfile call. BPF programs emitted by libseccomp seems to be deterministic. The tests would catch regressions as it verifies the program against known good output backed by manual testing. Signed-off-by: Ophestra --- helper/seccomp/export.go | 55 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 8 deletions(-) (limited to 'helper/seccomp/export.go') diff --git a/helper/seccomp/export.go b/helper/seccomp/export.go index 1bcb7146..4ac620bc 100644 --- a/helper/seccomp/export.go +++ b/helper/seccomp/export.go @@ -1,17 +1,56 @@ package seccomp import ( - "io" + "io/fs" "os" + "runtime" + "sync" + "sync/atomic" ) -func Export(opts SyscallOpts) (f *os.File, err error) { - if f, err = tmpfile(); err != nil { - return +type exporter struct { + opts SyscallOpts + r, w *os.File + + prepareOnce sync.Once + prepareErr error + closeErr atomic.Pointer[error] + exportErr <-chan error +} + +func (e *exporter) prepare() error { + e.prepareOnce.Do(func() { + if r, w, err := os.Pipe(); err != nil { + e.prepareErr = err + return + } else { + e.r, e.w = r, w + } + + ec := make(chan error, 1) + go func() { ec <- exportFilter(e.w.Fd(), e.opts); close(ec); _ = e.closeWrite() }() + e.exportErr = ec + runtime.SetFinalizer(e, (*exporter).closeWrite) + }) + return e.prepareErr +} + +func (e *exporter) closeWrite() error { + if !e.closeErr.CompareAndSwap(nil, &fs.ErrInvalid) { + return *e.closeErr.Load() } - if err = exportFilter(f.Fd(), opts); err != nil { - return + if e.w == nil { + return fs.ErrInvalid } - _, err = f.Seek(0, io.SeekStart) - return + err := e.w.Close() + e.closeErr.Store(&err) + + // no need for a finalizer anymore + runtime.SetFinalizer(e, nil) + + return err +} + +func newExporter(opts SyscallOpts) *exporter { + return &exporter{opts: opts} } -- cgit v1.3.1