Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/man/man3/seccomp_attr_set.3
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,18 @@ A flag to specify if libseccomp should request wait killable semantics when
possible. Defaults to off
.RI ( value
== 0).
.TP
.B SCMP_FLTATR_ACT_ENOSYS
Action to take when an unknown (too new) syscall is invoked. Used in
conjunction with SCMP_FLTATR_CTL_KVER. Defaults to SCMP_ACT_ERRNO(38)
(ENOSYS). If desired behavior differs from the default, then this attribute
must be set prior to setting SCMP_FLTATR_CTL_KVERMAX.
.TP
.B SCMP_FLTATR_CTL_KVERMAX
Maximum kernel version understood by the user application. Syscalls from
newer kernel versions will return with the action in SCMP_FLTATR_ACT_ENOSYS.
Once SCMP_FLTATR_CTL_KVERMAX is set, no more rules can be added to the
filter. Attempting to add more rules will result in -EINVAL.
.\" //////////////////////////////////////////////////////////////////////////
.SH RETURN VALUE
.\" //////////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 1 addition & 1 deletion include/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
# along with this library; if not, see <http://www.gnu.org/licenses>.
#

include_HEADERS = seccomp.h seccomp-syscalls.h
include_HEADERS = seccomp.h seccomp-syscalls.h seccomp-kvers.h
111 changes: 111 additions & 0 deletions include/seccomp-kvers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* Seccomp Library
*
* Copyright (c) 2025 Oracle and/or its affiliates.
* Author: Tom Hromatka <tom.hromatka@oracle.com>
*/

/*
* This library is free software; you can redistribute it and/or modify it
* under the terms of version 2.1 of the GNU Lesser General Public License as
* published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses>.
*/

#ifndef _SECCOMP_H
#error "do not include seccomp-kvers.h directly, use seccomp.h instead"
#endif

/**
* Kernel versions
*/
enum scmp_kver {
__SCMP_KV_NULL = 0,
SCMP_KV_UNDEF = 1,
SCMP_KV_3_0 = 2,
SCMP_KV_3_1 = 3,
SCMP_KV_3_2 = 4,
SCMP_KV_3_3 = 5,
SCMP_KV_3_4 = 6,
SCMP_KV_3_5 = 7,
SCMP_KV_3_6 = 8,
SCMP_KV_3_7 = 9,
SCMP_KV_3_8 = 10,
SCMP_KV_3_9 = 11,
SCMP_KV_3_10 = 12,
SCMP_KV_3_11 = 13,
SCMP_KV_3_12 = 14,
SCMP_KV_3_13 = 15,
SCMP_KV_3_14 = 16,
SCMP_KV_3_15 = 17,
SCMP_KV_3_16 = 18,
SCMP_KV_3_17 = 19,
SCMP_KV_3_18 = 20,
SCMP_KV_3_19 = 21,
SCMP_KV_4_0 = 22,
SCMP_KV_4_1 = 23,
SCMP_KV_4_2 = 24,
SCMP_KV_4_3 = 25,
SCMP_KV_4_4 = 26,
SCMP_KV_4_5 = 27,
SCMP_KV_4_6 = 28,
SCMP_KV_4_7 = 29,
SCMP_KV_4_8 = 30,
SCMP_KV_4_9 = 31,
SCMP_KV_4_10 = 32,
SCMP_KV_4_11 = 33,
SCMP_KV_4_12 = 34,
SCMP_KV_4_13 = 35,
SCMP_KV_4_14 = 36,
SCMP_KV_4_15 = 37,
SCMP_KV_4_16 = 38,
SCMP_KV_4_17 = 39,
SCMP_KV_4_18 = 40,
SCMP_KV_4_19 = 41,
SCMP_KV_4_20 = 42,
SCMP_KV_5_0 = 43,
SCMP_KV_5_1 = 44,
SCMP_KV_5_2 = 45,
SCMP_KV_5_3 = 46,
SCMP_KV_5_4 = 47,
SCMP_KV_5_5 = 48,
SCMP_KV_5_6 = 49,
SCMP_KV_5_7 = 50,
SCMP_KV_5_8 = 51,
SCMP_KV_5_9 = 52,
SCMP_KV_5_10 = 53,
SCMP_KV_5_11 = 54,
SCMP_KV_5_12 = 55,
SCMP_KV_5_13 = 56,
SCMP_KV_5_14 = 57,
SCMP_KV_5_15 = 58,
SCMP_KV_5_16 = 59,
SCMP_KV_5_17 = 60,
SCMP_KV_5_18 = 61,
SCMP_KV_5_19 = 62,
SCMP_KV_6_0 = 63,
SCMP_KV_6_1 = 64,
SCMP_KV_6_2 = 65,
SCMP_KV_6_3 = 66,
SCMP_KV_6_4 = 67,
SCMP_KV_6_5 = 68,
SCMP_KV_6_6 = 69,
SCMP_KV_6_7 = 70,
SCMP_KV_6_8 = 71,
SCMP_KV_6_9 = 72,
SCMP_KV_6_10 = 73,
SCMP_KV_6_11 = 74,
SCMP_KV_6_12 = 75,
SCMP_KV_6_13 = 76,
SCMP_KV_6_14 = 77,
SCMP_KV_6_15 = 78,
SCMP_KV_6_16 = 79,
__SCMP_KV_MAX,
};
7 changes: 7 additions & 0 deletions include/seccomp-syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@
#define __PNR_getpagesize -10249
#define __PNR_riscv_hwprobe -10250
#define __PNR_uretprobe -10251
#define __PNR_open_tree_attr -10252

/*
* libseccomp syscall definitions
Expand Down Expand Up @@ -1356,6 +1357,12 @@
#define __SNR_open_tree __PNR_open_tree
#endif

#ifdef __NR_open_tree_attr
#define __SNR_open_tree_attr __NR_open_tree_attr
#else
#define __SNR_open_tree_attr __PNR_open_tree_attr
#endif

#define __SNR_openat __NR_openat

#define __SNR_openat2 __NR_openat2
Expand Down
12 changes: 12 additions & 0 deletions include/seccomp.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ enum scmp_filter_attr {
*/
SCMP_FLTATR_API_SYSRAWRC = 9, /**< return the system return codes */
SCMP_FLTATR_CTL_WAITKILL = 10, /**< request wait killable semantics */
SCMP_FLTATR_ACT_ENOSYS = 11, /**< action to take when an unknown
* (newer than this filter) syscall is
* invoked. Must be used in conjunction
* with SCMP_FLTATR_CTL_KVER
*/
SCMP_FLTATR_CTL_KVERMAX = 12, /**< maximum kernel version understood
* by the user application. syscalls
* from newer kernel versions will
* return with the action in
* SCMP_FLTATR_ACT_UNKNOWN
*/
_SCMP_FLTATR_MAX,
};

Expand Down Expand Up @@ -898,6 +909,7 @@ int seccomp_precompute(const scmp_filter_ctx ctx);
#define __NR_SCMP_UNDEF -2

#include <seccomp-syscalls.h>
#include <seccomp-kvers.h>

#ifdef __cplusplus
}
Expand Down
24 changes: 23 additions & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,27 @@ if ENABLE_PYTHON
SUBDIRS += python
endif

GET_MAX_SYSCALL= ./arch-get-max-syscall-num.py
AM_CFLAGS = \
-DMAX_SYSCALL_NUM_X86=`${GET_MAX_SYSCALL} -a x86` \
-DMAX_SYSCALL_NUM_X86_64=`${GET_MAX_SYSCALL} -a x86_64` \
-DMAX_SYSCALL_NUM_X32=`${GET_MAX_SYSCALL} -a x32` \
-DMAX_SYSCALL_NUM_ARM=`${GET_MAX_SYSCALL} -a arm` \
-DMAX_SYSCALL_NUM_AARCH64=`${GET_MAX_SYSCALL} -a aarch64` \
-DMAX_SYSCALL_NUM_LOONGARCH64=`${GET_MAX_SYSCALL} -a loongarch64` \
-DMAX_SYSCALL_NUM_M68K=`${GET_MAX_SYSCALL} -a m68k` \
-DMAX_SYSCALL_NUM_MIPS=`${GET_MAX_SYSCALL} -a mips` \
-DMAX_SYSCALL_NUM_MIPS64=`${GET_MAX_SYSCALL} -a mips64` \
-DMAX_SYSCALL_NUM_MIPS64N32=`${GET_MAX_SYSCALL} -a mips64n32` \
-DMAX_SYSCALL_NUM_PARISC=`${GET_MAX_SYSCALL} -a parisc` \
-DMAX_SYSCALL_NUM_PARISC64=`${GET_MAX_SYSCALL} -a parisc64` \
-DMAX_SYSCALL_NUM_PPC=`${GET_MAX_SYSCALL} -a ppc` \
-DMAX_SYSCALL_NUM_PPC64=`${GET_MAX_SYSCALL} -a ppc64` \
-DMAX_SYSCALL_NUM_RISCV64=`${GET_MAX_SYSCALL} -a riscv64` \
-DMAX_SYSCALL_NUM_S390=`${GET_MAX_SYSCALL} -a s390` \
-DMAX_SYSCALL_NUM_S390X=`${GET_MAX_SYSCALL} -a s390x` \
-DMAX_SYSCALL_NUM_SH=`${GET_MAX_SYSCALL} -a sh`

SOURCES_ALL = \
api.c system.h system.c helper.h helper.c \
gen_pfc.h gen_pfc.c gen_bpf.h gen_bpf.c \
Expand Down Expand Up @@ -49,7 +70,8 @@ SOURCES_ALL = \

EXTRA_DIST = \
arch-syscall-validate arch-syscall-check \
arch-gperf-generate syscalls.csv syscalls.perf.template
arch-gperf-generate syscalls.csv syscalls.perf.template \
arch-get-max-syscall-num.py

TESTS = arch-syscall-check

Expand Down
16 changes: 14 additions & 2 deletions src/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -363,10 +363,14 @@ API int seccomp_merge(scmp_filter_ctx ctx_dst, scmp_filter_ctx ctx_src)
if (db_col_valid(col_dst) || db_col_valid(col_src))
return _rc_filter(-EINVAL);

/* NOTE: only the default action, NNP, and TSYNC settings must match */
/* NOTE: only the default action, NNP, TSYNC, and kernel version
* settings must match */
if ((col_dst->attr.act_default != col_src->attr.act_default) ||
(col_dst->attr.nnp_enable != col_src->attr.nnp_enable) ||
(col_dst->attr.tsync_enable != col_src->attr.tsync_enable))
(col_dst->attr.tsync_enable != col_src->attr.tsync_enable) ||
(col_dst->attr.act_enosys != col_src->attr.act_enosys) ||
(col_dst->attr.kvermax != col_src->attr.kvermax))

return _rc_filter(-EINVAL);

return _rc_filter(db_col_merge(col_dst, col_src));
Expand Down Expand Up @@ -590,6 +594,14 @@ API int seccomp_rule_add_array(scmp_filter_ctx ctx,
if (action == col->attr.act_default)
return _rc_filter(-EACCES);

if (col->attr.kvermax != SCMP_KV_UNDEF)
/* Currently libseccomp does not support overwriting rules
* that have already been added to the filter. The maximum
* supported kernel version feature adds a rule for each
* syscall. Thus we can't support adding any more syscalls
* after that value is set */
return _rc_filter(-EINVAL);

return _rc_filter(db_col_rule_add(col, 0, action,
syscall, arg_cnt, arg_array));
}
Expand Down
145 changes: 145 additions & 0 deletions src/arch-build-kver-tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#!/usr/bin/env python3
#
# Seccomp Library program to build the kernel version tables
#
# Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
# Author: Tom Hromatka <tom.hromatka@oracle.com>
#

#
# This library is free software; you can redistribute it and/or modify it
# under the terms of version 2.1 of the GNU Lesser General Public License as
# published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, see <http://www.gnu.org/licenses>.
#

#######################################################
#### WARNING - to generate proper headers for x32, you
#### must install the glibc 32-bit headers
####
#### apt install libc6-dev-x32
####
#######################################################

from subprocess import TimeoutExpired
import subprocess
import argparse
import os

kernel_versions = ['3.0', '3.1', '3.2', '3.3', '3.4', '3.5', '3.6', '3.7',
'3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14',
'3.15', '3.16', '3.17', '3.18', '3.19', '4.0', '4.1',
'4.2', '4.3', '4.4', '4.5', '4.6', '4.7', '4.8', '4.9',
'4.10', '4.11', '4.12', '4.13', '4.14', '4.15', '4.16',
'4.17', '4.18', '4.19', '4.20', '5.0', '5.1', '5.2',
'5.3', '5.4', '5.5', '5.6', '5.7', '5.8', '5.9', '5.10',
'5.11', '5.12', '5.13', '5.14', '5.15', '5.16', '5.17',
'5.18', '5.19', '6.0', '6.1', '6.2', '6.3', '6.4', '6.5',
'6.6', '6.7', '6.8', '6.9', '6.10', '6.11', '6.12',
'6.13', '6.14', '6.15', '6.16']

def parse_args():
parser = argparse.ArgumentParser('Script to populate the syscalls.csv kernel versions',
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('-d', '--datapath', required=True, type=str, default=None,
help="Path to the local copy of @hrw's syscalls-table tool")
parser.add_argument('-k', '--kernelpath', required=True, type=str, default=None,
help="Path to the kernel source directory")
parser.add_argument('-V', '--versions', required=False, type=str, default=None,
help="Comma-separated list of kernel versions to build, e.g "
"3.0,6.1,6.10. If not specified all known kernel version "
"tables are built")
parser.add_argument('-v', '--verbose', action='store_true',
help='Show verbose warnings')

args = parser.parse_args()

if not args.versions:
args.versions = kernel_versions
else:
args.versions = args.versions.split(',')

return args

def run(command, verbose=False, shell=False, timeout=None):
if shell:
if isinstance(command, str):
# nothing to do. command is already formatted as a string
pass
elif isinstance(command, list):
command = ' '.join(command)
else:
raise ValueError('Unsupported command type')

subproc = subprocess.Popen(command, shell=shell,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)

if timeout:
try:
out, err = subproc.communicate(timeout=timeout)
ret = subproc.returncode

out = out.strip().decode('UTF-8')
err = err.strip().decode('UTF-8')
except TimeoutExpired as timeout:
if timeout.stdout:
out = timeout.stdout.strip().decode('UTF-8')
else:
out = ''
if timeout.stderr:
err = timeout.stderr.strip().decode('UTF-8')
else:
err = ''

if len(err):
ret = -1
else:
ret = 0
else:
out, err = subproc.communicate()
ret = subproc.returncode

out = out.strip().decode('UTF-8')
err = err.strip().decode('UTF-8')

if verbose:
if not shell:
command = ' '.join(command)
print('run:\n\tcmd = {}\n\tret = {}\n\tstdout = {}\n\tstderr = {}\n'.format(
command, ret, out, err))

return ret, out, err

def main(args):
for kver in args.versions:
print('Building version table for kernel {}'.format(kver))

checkout_cmd = 'cd {};git checkout v{}'.format(args.kernelpath, kver)
ret, out, err = run(checkout_cmd, shell=True)
if ret != 0:
raise KeyError('Failed to checkout v{}: {}'.format(kver, ret))

update_cmd = 'cd {};bash scripts/update-tables.sh {}'.format(
args.datapath, args.kernelpath)
ret, out, err = run(update_cmd, shell=True)
if ret != 0:
raise RuntimeError('Failed to update tables: {}'.format(ret))

src_path = os.path.join(args.datapath, 'data/tables')
dest_path = os.path.join(os.getcwd(), 'tables-{}'.format(kver))
cp_cmd = 'cp -r {} {}'.format(src_path, dest_path)
ret, out, err = run(cp_cmd, shell=True)
if ret != 0:
raise RuntimeError('Table copy failed: {}'.format(ret))

if __name__ == '__main__':
args = parse_args()
main(args)
Loading
Loading