Skip to content

Commit 3b37f01

Browse files
committed
bpf: pfc: Add maximum supported kernel version logic
Add support for an application to specify the maximum kernel version it currently supports. Any syscalls that have been added to a kernel version newer than this specified version will return the unknown action. The unknown action defaults to returning ENOSYS, but it can be overridden via the filter attribute SCMP_FLTATR_ACT_ENOSYS. When the maximum supported kernel version is enabled, libseccomp will create a filter as follows: * Users explicitly declare rules for syscalls. No changes here from previous behavior * The default action provided via seccomp_init() will still be used for all syscalls that existed as of the user-specified supported kernel * Any syscalls that did not exist at the time of the user-specified supported kernel will return the unknown action. By default libseccomp sets this to return ENOSYS, but it can be overridden via the filter attribute SCMP_FLTATR_ACT_ENOSYS. Below is a rough pseudo-code outline of a typical usage of this feature: seccomp_init() seccomp_add_rules() (optional but recommended) seccomp_attr_set( binary tree ) seccomp_attr_set( max supported kernel version, e.g. SCMP_KV_6_5 ) (optional) seccomp_attr_set( default unknown action ) seccomp_load() seccomp_release() Fixes: #11 Signed-off-by: Tom Hromatka <tom.hromatka@oracle.com>
1 parent f83fef9 commit 3b37f01

7 files changed

Lines changed: 191 additions & 5 deletions

File tree

src/api.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,10 +363,14 @@ API int seccomp_merge(scmp_filter_ctx ctx_dst, scmp_filter_ctx ctx_src)
363363
if (db_col_valid(col_dst) || db_col_valid(col_src))
364364
return _rc_filter(-EINVAL);
365365

366-
/* NOTE: only the default action, NNP, and TSYNC settings must match */
366+
/* NOTE: only the default action, NNP, TSYNC, and kernel version
367+
* settings must match */
367368
if ((col_dst->attr.act_default != col_src->attr.act_default) ||
368369
(col_dst->attr.nnp_enable != col_src->attr.nnp_enable) ||
369-
(col_dst->attr.tsync_enable != col_src->attr.tsync_enable))
370+
(col_dst->attr.tsync_enable != col_src->attr.tsync_enable) ||
371+
(col_dst->attr.act_enosys != col_src->attr.act_enosys) ||
372+
(col_dst->attr.kvermax != col_src->attr.kvermax))
373+
370374
return _rc_filter(-EINVAL);
371375

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

597+
if (col->attr.kvermax != SCMP_KV_UNDEF)
598+
/* Currently libseccomp does not support overwriting rules
599+
* that have already been added to the filter. The maximum
600+
* supported kernel version feature adds a rule for each
601+
* syscall. Thus we can't support adding any more syscalls
602+
* after that value is set */
603+
return _rc_filter(-EINVAL);
604+
593605
return _rc_filter(db_col_rule_add(col, 0, action,
594606
syscall, arg_cnt, arg_array));
595607
}

src/arch.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,3 +477,46 @@ int arch_filter_rule_add(struct db_filter *db,
477477
free(rule_dup);
478478
return rc;
479479
}
480+
481+
int arch_add_kver_rule(struct db_filter *db, struct db_api_rule_list *rule,
482+
enum scmp_kver supported_kver)
483+
{
484+
struct db_sys_list *s_iter;
485+
enum scmp_kver syscall_added_ver;
486+
const char *syscall_name;
487+
int rc = 0;
488+
489+
db_list_foreach(s_iter, db->syscalls) {
490+
if (rule->syscall == s_iter->num)
491+
/* Do not overwrite existing rules */
492+
return 0;
493+
}
494+
495+
syscall_name = (db->arch->syscall_resolve_num_raw)(rule->syscall);
496+
if (syscall_name == NULL)
497+
/* This syscall number is invalid in this architecture */
498+
return 0;
499+
500+
syscall_added_ver = (db->arch->syscall_num_kver)(rule->syscall);
501+
if (syscall_added_ver > supported_kver)
502+
/* This syscall is newer than the kernel version supported by
503+
* the application. Don't add it to the rule, and therefore
504+
* it will be subject to the attr->act_unknown action */
505+
return 0;
506+
507+
rc = arch_syscall_translate(db->arch, &rule->syscall);
508+
if (rc < 0)
509+
return 0;
510+
511+
if (db->arch->rule_add == NULL) {
512+
/* syscalls < -1 require a db->arch->rule_add() function */
513+
if (rule->syscall < -1 && rule->strict)
514+
return 0;
515+
516+
rc = db_rule_add(db, rule);
517+
} else {
518+
rc = (db->arch->rule_add)(db, rule);
519+
}
520+
521+
return rc;
522+
}

src/arch.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,6 @@ int arch_syscall_rewrite(const struct arch_def *arch, int *syscall);
136136
int arch_filter_rule_add(struct db_filter *db,
137137
const struct db_api_rule_list *rule);
138138

139+
int arch_add_kver_rule(struct db_filter *db, struct db_api_rule_list *rule,
140+
enum scmp_kver supported_kver);
139141
#endif

src/db.c

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1381,7 +1381,7 @@ uint32_t db_col_attr_read(const struct db_filter_col *col,
13811381
int db_col_attr_set(struct db_filter_col *col,
13821382
enum scmp_filter_attr attr, uint32_t value)
13831383
{
1384-
int rc = 0;
1384+
int rc = 0, iter;
13851385

13861386
switch (attr) {
13871387
case SCMP_FLTATR_ACT_DEFAULT:
@@ -1458,6 +1458,11 @@ int db_col_attr_set(struct db_filter_col *col,
14581458
col->attr.wait_killable_recv = (value ? 1 : 0);
14591459
break;
14601460
case SCMP_FLTATR_ACT_ENOSYS:
1461+
/* SCMP_FLTATR_ACT_ENOSYS must be set prior to setting
1462+
* SCMP_FLTATR_CTL_KVERMAX */
1463+
if (col->attr.kvermax != SCMP_KV_UNDEF)
1464+
return -EINVAL;
1465+
14611466
if (db_col_action_valid(col, value) == 0)
14621467
col->attr.act_enosys = value;
14631468
else
@@ -1467,7 +1472,16 @@ int db_col_attr_set(struct db_filter_col *col,
14671472
case SCMP_FLTATR_CTL_KVERMAX:
14681473
if (value <= SCMP_KV_UNDEF || value >=__SCMP_KV_MAX)
14691474
return -EINVAL;
1475+
14701476
col->attr.kvermax = value;
1477+
db_col_precompute_reset(col);
1478+
1479+
/* Create a rule for every syscall that existed up to and
1480+
* prior to attr.kver. libseccomp capably handles duplicate
1481+
* rules for a syscall, so if the user adds more rules after
1482+
* setting this attribute, libseccomp will simply replace
1483+
* the rules added by the unknown-syscall logic */
1484+
rc = db_add_known_syscalls(col);
14711485
break;
14721486
default:
14731487
rc = -EINVAL;
@@ -2720,3 +2734,110 @@ void db_col_precompute_reset(struct db_filter_col *col)
27202734
gen_bpf_release(col->prgm_bpf);
27212735
col->prgm_bpf = NULL;
27222736
}
2737+
2738+
static int get_max_syscall_num(struct db_filter *filter)
2739+
{
2740+
if (!filter)
2741+
return -1;
2742+
2743+
switch (filter->arch->token) {
2744+
case SCMP_ARCH_X86:
2745+
return MAX_SYSCALL_NUM_X86;
2746+
case SCMP_ARCH_X86_64:
2747+
return MAX_SYSCALL_NUM_X86_64;
2748+
case SCMP_ARCH_X32:
2749+
return MAX_SYSCALL_NUM_X32;
2750+
case SCMP_ARCH_ARM:
2751+
return MAX_SYSCALL_NUM_ARM;
2752+
case SCMP_ARCH_AARCH64:
2753+
return MAX_SYSCALL_NUM_AARCH64;
2754+
case SCMP_ARCH_LOONGARCH64:
2755+
return MAX_SYSCALL_NUM_LOONGARCH64;
2756+
case SCMP_ARCH_M68K:
2757+
return MAX_SYSCALL_NUM_M68K;
2758+
case SCMP_ARCH_MIPS:
2759+
case SCMP_ARCH_MIPSEL:
2760+
return MAX_SYSCALL_NUM_MIPS;
2761+
case SCMP_ARCH_MIPS64:
2762+
case SCMP_ARCH_MIPSEL64:
2763+
return MAX_SYSCALL_NUM_MIPS64;
2764+
case SCMP_ARCH_MIPS64N32:
2765+
case SCMP_ARCH_MIPSEL64N32:
2766+
return MAX_SYSCALL_NUM_MIPS64N32;
2767+
case SCMP_ARCH_PARISC:
2768+
return MAX_SYSCALL_NUM_PARISC;
2769+
case SCMP_ARCH_PARISC64:
2770+
return MAX_SYSCALL_NUM_PARISC64;
2771+
case SCMP_ARCH_PPC64:
2772+
case SCMP_ARCH_PPC64LE:
2773+
return MAX_SYSCALL_NUM_PPC64;
2774+
case SCMP_ARCH_PPC:
2775+
return MAX_SYSCALL_NUM_PPC;
2776+
case SCMP_ARCH_S390X:
2777+
return MAX_SYSCALL_NUM_S390X;
2778+
case SCMP_ARCH_S390:
2779+
return MAX_SYSCALL_NUM_S390;
2780+
case SCMP_ARCH_RISCV64:
2781+
return MAX_SYSCALL_NUM_RISCV64;
2782+
case SCMP_ARCH_SH:
2783+
case SCMP_ARCH_SHEB:
2784+
return MAX_SYSCALL_NUM_SH;
2785+
default:
2786+
return -1;
2787+
}
2788+
}
2789+
2790+
int db_add_known_syscalls(struct db_filter_col *col)
2791+
{
2792+
struct db_api_rule_list *rule = NULL;
2793+
ssize_t chain_size;
2794+
struct db_api_arg *chain = NULL;
2795+
int rc, iter, syscall_num, max_syscall_num;
2796+
2797+
chain_size = sizeof(*chain) * ARG_COUNT_MAX;
2798+
chain = zmalloc(chain_size);
2799+
if (chain == NULL)
2800+
return -ENOMEM;
2801+
2802+
/* create a checkpoint */
2803+
rc = db_col_transaction_start(col, false);
2804+
if (rc != 0)
2805+
goto add_failure;
2806+
2807+
for (iter = 0; iter < col->filter_cnt; iter++) {
2808+
max_syscall_num = get_max_syscall_num(col->filters[iter]);
2809+
if (max_syscall_num < 0)
2810+
goto add_failure;
2811+
2812+
for (syscall_num = 0; syscall_num < max_syscall_num; syscall_num++) {
2813+
rule = _db_rule_new(1, col->attr.act_default,
2814+
syscall_num, chain);
2815+
if (rule == NULL) {
2816+
rc = -ENOMEM;
2817+
goto add_failure;
2818+
}
2819+
2820+
rc = arch_add_kver_rule(col->filters[iter], rule,
2821+
col->attr.kvermax);
2822+
if (rc != 0)
2823+
goto add_failure;
2824+
2825+
free(rule);
2826+
rule = NULL;
2827+
}
2828+
}
2829+
2830+
add_failure:
2831+
/* commit the transaction or abort */
2832+
if (rc == 0)
2833+
db_col_transaction_commit(col, false);
2834+
else
2835+
db_col_transaction_abort(col, false);
2836+
2837+
if (rule != NULL)
2838+
free(rule);
2839+
if (chain != NULL)
2840+
free(chain);
2841+
2842+
return 0;
2843+
}

src/db.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,5 @@ void db_col_precompute_reset(struct db_filter_col *col);
232232

233233
int db_rule_add(struct db_filter *db, const struct db_api_rule_list *rule);
234234

235+
int db_add_known_syscalls(struct db_filter_col *col);
235236
#endif

src/gen_bpf.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2019,7 +2019,10 @@ static int _gen_bpf_build_bpf(struct bpf_state *state,
20192019
state->bad_arch_hsh = b_badarch->hash;
20202020

20212021
/* generate the default action */
2022-
b_default = _gen_bpf_action(state, NULL, state->attr->act_default);
2022+
if (state->attr->kvermax != SCMP_KV_UNDEF)
2023+
b_default = _gen_bpf_action(state, NULL, state->attr->act_enosys);
2024+
else
2025+
b_default = _gen_bpf_action(state, NULL, state->attr->act_default);
20232026
if (b_default == NULL)
20242027
return -ENOMEM;
20252028
rc = _hsh_add(state, &b_default, 0);

src/gen_pfc.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,11 @@ static int _gen_pfc_arch(const struct db_filter_col *col,
458458
_indent(fds, 1);
459459
fprintf(fds, "# default action\n");
460460
_indent(fds, 1);
461-
_pfc_action(fds, col->attr.act_default);
461+
462+
if (col->attr.kvermax != SCMP_KV_UNDEF)
463+
_pfc_action(fds, col->attr.act_enosys);
464+
else
465+
_pfc_action(fds, col->attr.act_default);
462466

463467
arch_return:
464468
while (p_head != NULL) {

0 commit comments

Comments
 (0)