Skip to content

Commit 3f0e47f

Browse files
vapierpcmoore
authored andcommitted
api: extend BPF export API to write to a memory buffer
The API to export to a fd is helpful, but for tools that want to generate & read the BPF program, outputting to a buffer would be much more helpful. Signed-off-by: Mike Frysinger <vapier@gentoo.org> Reviewed-by: Tom Hromatka <tom.hromatka@oracle.com> [PM: rename seccomp_export_bpf_buf() to seccomp_export_bpf_mem()] [PM: 'make check-syntax' fixes] Signed-off-by: Paul Moore <paul@paul-moore.com>
1 parent 50da6c1 commit 3f0e47f

9 files changed

Lines changed: 139 additions & 0 deletions

File tree

doc/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ dist_man3_MANS = \
2828
man/man3/seccomp_attr_get.3 \
2929
man/man3/seccomp_attr_set.3 \
3030
man/man3/seccomp_export_bpf.3 \
31+
man/man3/seccomp_export_bpf_mem.3 \
3132
man/man3/seccomp_export_pfc.3 \
3233
man/man3/seccomp_init.3 \
3334
man/man3/seccomp_load.3 \

doc/man/man3/seccomp_export_bpf.3

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ seccomp_export_bpf, seccomp_export_pfc \- Export the seccomp filter
1313
.sp
1414
.BI "int seccomp_export_bpf(const scmp_filter_ctx " ctx ", int " fd ");"
1515
.BI "int seccomp_export_pfc(const scmp_filter_ctx " ctx ", int " fd ");"
16+
.BI "int seccomp_export_bpf_mem(const scmp_filter_ctx " ctx ", void *" buf ", size_t *" len ");"
1617
.sp
1718
Link with \fI\-lseccomp\fP.
1819
.fi
@@ -42,6 +43,26 @@ is the value returned by the call to
4243
While the two output formats are guaranteed to be functionally equivalent for
4344
the given seccomp filter configuration, the filter instructions, and their
4445
ordering, are not guaranteed to be the same in both the BPF and PFC formats.
46+
.P
47+
The
48+
.BR seccomp_export_bpf_mem ()
49+
function is largely the same as
50+
.BR seccomp_export_bpf (),
51+
but instead of writing to a file descriptor, the program will be written to the
52+
.I buf
53+
pointer provided by the caller. The
54+
.I len
55+
argument must be initialized with the size of the
56+
.I buf
57+
buffer. If the program was valid,
58+
.I len
59+
will be updated with its size in bytes. If
60+
.I buf
61+
was too small to hold the program,
62+
.I len
63+
can be consulted to determine the required size. Passing a NULL
64+
.I buf
65+
may also be used to query the required size ahead of time.
4566
.\" //////////////////////////////////////////////////////////////////////////
4667
.SH RETURN VALUE
4768
.\" //////////////////////////////////////////////////////////////////////////
@@ -59,6 +80,9 @@ Invalid input, either the context or architecture token is invalid.
5980
.TP
6081
.B -ENOMEM
6182
The library was unable to allocate enough memory.
83+
.TP
84+
.B -ERANGE
85+
The provided buffer was too small.
6286
.P
6387
If the \fISCMP_FLTATR_API_SYSRAWRC\fP filter attribute is non-zero then
6488
additional error codes may be returned to the caller; these additional error
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.so man3/seccomp_export_bpf.3

include/seccomp.h.in

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include <elf.h>
2727
#include <inttypes.h>
28+
#include <stdlib.h>
2829
#include <asm/unistd.h>
2930
#include <linux/audit.h>
3031
#include <linux/types.h>
@@ -816,6 +817,19 @@ int seccomp_export_pfc(const scmp_filter_ctx ctx, int fd);
816817
*/
817818
int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd);
818819

820+
/**
821+
* Generate seccomp Berkeley Packet Filter (BPF) code and export it to a buffer
822+
* @param ctx the filter context
823+
* @param buf the destination buffer
824+
* @param len on input the length of the buffer, on output the number of bytes
825+
* in the program
826+
*
827+
* This function generates seccomp Berkeley Packer Filter (BPF) code and writes
828+
* it to the given buffer. Returns zero on success, negative values on failure.
829+
*
830+
*/
831+
int seccomp_export_bpf_mem(const scmp_filter_ctx ctx, void *buf, size_t *len);
832+
819833
/*
820834
* pseudo syscall definitions
821835
*/

src/api.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ static int _rc_filter(int err)
8383
* requested operation */
8484
case -EOPNOTSUPP:
8585
/* NOTE: operation is not supported */
86+
case -ERANGE:
87+
/* NOTE: provided buffer is too small */
8688
case -ESRCH:
8789
/* NOTE: operation failed due to multi-threading */
8890
return err;
@@ -731,3 +733,35 @@ API int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd)
731733

732734
return 0;
733735
}
736+
737+
/* NOTE - function header comment in include/seccomp.h */
738+
API int seccomp_export_bpf_mem(const scmp_filter_ctx ctx, void *buf,
739+
size_t *len)
740+
{
741+
int rc;
742+
size_t buf_len;
743+
struct db_filter_col *col;
744+
struct bpf_program *program;
745+
746+
if (_ctx_valid(ctx) || !len)
747+
return _rc_filter(-EINVAL);
748+
col = (struct db_filter_col *)ctx;
749+
750+
rc = gen_bpf_generate(col, &program);
751+
if (rc < 0)
752+
return _rc_filter(rc);
753+
buf_len = *len;
754+
*len = BPF_PGM_SIZE(program);
755+
756+
rc = 0;
757+
if (buf) {
758+
/* If we have a big enough buffer, write the program. */
759+
if (*len > buf_len)
760+
rc = _rc_filter(-ERANGE);
761+
else
762+
memcpy(buf, program->blks, *len);
763+
}
764+
gen_bpf_release(program);
765+
766+
return rc;
767+
}

src/python/libseccomp.pxd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ cdef extern from "seccomp.h":
167167

168168
int seccomp_export_pfc(scmp_filter_ctx ctx, int fd)
169169
int seccomp_export_bpf(scmp_filter_ctx ctx, int fd)
170+
int seccomp_export_bpf_mem(const scmp_filter_ctx ctx, void *buf,
171+
size_t *len)
170172

171173
# kate: syntax python;
172174
# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off;

src/python/seccomp.pyx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,12 @@ Example:
8080
__author__ = 'Paul Moore <paul@paul-moore.com>'
8181
__date__ = "3 February 2017"
8282

83+
from cpython cimport array
8384
from cpython.version cimport PY_MAJOR_VERSION
8485
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t
8586
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t
8687
from libc.stdlib cimport free
88+
import array
8789
import errno
8890

8991
cimport libseccomp
@@ -1044,5 +1046,28 @@ cdef class SyscallFilter:
10441046
if rc != 0:
10451047
raise RuntimeError(str.format("Library error (errno = {0})", rc))
10461048

1049+
def export_bpf_mem(self):
1050+
""" Export the filter in BPF format.
1051+
1052+
Description:
1053+
Return the filter in Berkeley Packet Filter (BPF) as bytes.
1054+
The output is identical to what is loaded into the Linux Kernel.
1055+
"""
1056+
cdef size_t len = 0
1057+
1058+
# Figure out how big the program is.
1059+
rc = libseccomp.seccomp_export_bpf_mem(self._ctx, NULL, <size_t *>&len)
1060+
if rc != 0:
1061+
raise RuntimeError(str.format("Library error (errno = {0})", rc))
1062+
1063+
# Get the program.
1064+
cdef array.array data = array.array('b', bytes(len))
1065+
cdef char[:] program = data
1066+
rc = libseccomp.seccomp_export_bpf_mem(self._ctx, <void *>&program[0],
1067+
<size_t *>&len)
1068+
if rc != 0:
1069+
raise RuntimeError(str.format("Library error (errno = {0})", rc))
1070+
return program
1071+
10471072
# kate: syntax python;
10481073
# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off;

tests/11-basic-basic_errors.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,39 @@ int main(int argc, char *argv[])
175175
seccomp_release(ctx);
176176
ctx = NULL;
177177

178+
/* seccomp_export_bpf_mem errors */
179+
char buf[1024];
180+
size_t buf_len = sizeof(buf);
181+
rc = seccomp_export_bpf_mem(ctx, buf, &buf_len);
182+
if (rc != -EINVAL)
183+
return -1;
184+
185+
ctx = seccomp_init(SCMP_ACT_KILL);
186+
if (ctx == NULL)
187+
return -1;
188+
rc = seccomp_export_bpf_mem(ctx, buf, NULL);
189+
if (rc != -EINVAL)
190+
return -1;
191+
rc = seccomp_export_bpf_mem(ctx, NULL, NULL);
192+
if (rc != -EINVAL)
193+
return -1;
194+
195+
rc = seccomp_export_bpf_mem(ctx, NULL, &buf_len);
196+
if (rc != 0)
197+
return -1;
198+
rc = seccomp_export_bpf_mem(ctx, buf, &buf_len);
199+
if (rc != 0)
200+
return -1;
201+
rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
202+
if (rc != 0)
203+
return -1;
204+
buf_len = 0;
205+
rc = seccomp_export_bpf_mem(ctx, buf, &buf_len);
206+
if (rc != -ERANGE)
207+
return -1;
208+
seccomp_release(ctx);
209+
ctx = NULL;
210+
178211
/* seccomp_attr_* errors */
179212
ctx = seccomp_init(SCMP_ACT_ALLOW);
180213
if (ctx == NULL)

tests/11-basic-basic_errors.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ def test():
8787
except RuntimeError:
8888
pass
8989

90+
# This shouldn't throw any errors.
91+
f = SyscallFilter(ALLOW)
92+
f.add_rule(KILL, "read")
93+
ret = f.export_bpf_mem()
94+
9095
test()
9196

9297
# kate: syntax python;

0 commit comments

Comments
 (0)