• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

drakenclimber / libseccomp / 12716868856

10 Jan 2025 08:37PM UTC coverage: 87.592% (-0.08%) from 87.676%
12716868856

push

github

drakenclimber
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_UNKNOWN.

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_UNKNOWN.

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()

Signed-off-by: Tom Hromatka <tom.hromatka@oracle.com>

8 of 65 new or added lines in 5 files covered. (12.31%)

96 existing lines in 4 files now uncovered.

2739 of 3127 relevant lines covered (87.59%)

288204.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.04
/src/arch.c
1
/**
2
 * Enhanced Seccomp Architecture/Machine Specific Code
3
 *
4
 * Copyright (c) 2012,2018 Red Hat <pmoore@redhat.com>
5
 * Author: Paul Moore <paul@paul-moore.com>
6
 */
7

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

22
#include <elf.h>
23
#include <errno.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <asm/bitsperlong.h>
27
#include <linux/audit.h>
28
#include <stdbool.h>
29

30
#include <seccomp.h>
31

32
#include "arch.h"
33
#include "arch-x86.h"
34
#include "arch-x86_64.h"
35
#include "arch-x32.h"
36
#include "arch-arm.h"
37
#include "arch-aarch64.h"
38
#include "arch-loongarch64.h"
39
#include "arch-m68k.h"
40
#include "arch-mips.h"
41
#include "arch-mips64.h"
42
#include "arch-mips64n32.h"
43
#include "arch-parisc.h"
44
#include "arch-parisc64.h"
45
#include "arch-ppc.h"
46
#include "arch-ppc64.h"
47
#include "arch-riscv64.h"
48
#include "arch-s390.h"
49
#include "arch-s390x.h"
50
#include "arch-sh.h"
51
#include "db.h"
52
#include "system.h"
53

54
#define default_arg_offset(x)                (offsetof(struct seccomp_data, args[x]))
55

56
#if __i386__
57
const struct arch_def *arch_def_native = &arch_def_x86;
58
#elif __x86_64__
59
#ifdef __ILP32__
60
const struct arch_def *arch_def_native = &arch_def_x32;
61
#else
62
const struct arch_def *arch_def_native = &arch_def_x86_64;
63
#endif /* __ILP32__ */
64
#elif __arm__
65
const struct arch_def *arch_def_native = &arch_def_arm;
66
#elif __aarch64__
67
const struct arch_def *arch_def_native = &arch_def_aarch64;
68
#elif __loongarch_lp64
69
const struct arch_def *arch_def_native = &arch_def_loongarch64;
70
#elif __m68k__
71
const struct arch_def *arch_def_native = &arch_def_m68k;
72
#elif __mips__ && _MIPS_SIM == _MIPS_SIM_ABI32
73
#if __MIPSEB__
74
const struct arch_def *arch_def_native = &arch_def_mips;
75
#elif __MIPSEL__
76
const struct arch_def *arch_def_native = &arch_def_mipsel;
77
#endif /* _MIPS_SIM_ABI32 */
78
#elif __mips__ && _MIPS_SIM == _MIPS_SIM_ABI64
79
#if __MIPSEB__
80
const struct arch_def *arch_def_native = &arch_def_mips64;
81
#elif __MIPSEL__
82
const struct arch_def *arch_def_native = &arch_def_mipsel64;
83
#endif /* _MIPS_SIM_ABI64 */
84
#elif __mips__ && _MIPS_SIM == _MIPS_SIM_NABI32
85
#if __MIPSEB__
86
const struct arch_def *arch_def_native = &arch_def_mips64n32;
87
#elif __MIPSEL__
88
const struct arch_def *arch_def_native = &arch_def_mipsel64n32;
89
#endif /* _MIPS_SIM_NABI32 */
90
#elif __hppa64__ /* hppa64 must be checked before hppa */
91
const struct arch_def *arch_def_native = &arch_def_parisc64;
92
#elif __hppa__
93
const struct arch_def *arch_def_native = &arch_def_parisc;
94
#elif __PPC64__
95
#ifdef __BIG_ENDIAN__
96
const struct arch_def *arch_def_native = &arch_def_ppc64;
97
#else
98
const struct arch_def *arch_def_native = &arch_def_ppc64le;
99
#endif
100
#elif __PPC__
101
const struct arch_def *arch_def_native = &arch_def_ppc;
102
#elif __s390x__ /* s390x must be checked before s390 */
103
const struct arch_def *arch_def_native = &arch_def_s390x;
104
#elif __s390__
105
const struct arch_def *arch_def_native = &arch_def_s390;
106
#elif __riscv && __riscv_xlen == 64
107
const struct arch_def *arch_def_native = &arch_def_riscv64;
108
#elif __sh__
109
#ifdef __BIG_ENDIAN__
110
const struct arch_def *arch_def_native = &arch_def_sheb;
111
#else
112
const struct arch_def *arch_def_native = &arch_def_sh;
113
#endif
114
#else
115
#error the arch code needs to know about your machine type
116
#endif /* machine type guess */
117

118
/**
119
 * Validate the architecture token
120
 * @param arch the architecture token
121
 *
122
 * Verify the given architecture token; return zero if valid, -EINVAL if not.
123
 *
124
 */
125
int arch_valid(uint32_t arch)
453,157✔
126
{
127
        return (arch_def_lookup(arch) ? 0 : -EINVAL);
29,309✔
128
}
129

130
/**
131
 * Lookup the architecture definition
132
 * @param token the architecture token
133
 *
134
 * Return the matching architecture definition, returns NULL on failure.
135
 *
136
 */
137
const struct arch_def *arch_def_lookup(uint32_t token)
488,713✔
138
{
139
        switch (token) {
488,713✔
140
        case SCMP_ARCH_X86:
141
                return &arch_def_x86;
142
        case SCMP_ARCH_X86_64:
305,101✔
143
                return &arch_def_x86_64;
305,101✔
144
        case SCMP_ARCH_X32:
7,215✔
145
                return &arch_def_x32;
7,215✔
146
        case SCMP_ARCH_ARM:
7,201✔
147
                return &arch_def_arm;
7,201✔
148
        case SCMP_ARCH_AARCH64:
17,850✔
149
                return &arch_def_aarch64;
17,850✔
150
        case SCMP_ARCH_LOONGARCH64:
17,898✔
151
                return &arch_def_loongarch64;
17,898✔
152
        case SCMP_ARCH_M68K:
3,354✔
153
                return &arch_def_m68k;
3,354✔
154
        case SCMP_ARCH_MIPS:
2,626✔
155
                return &arch_def_mips;
2,626✔
156
        case SCMP_ARCH_MIPSEL:
9,999✔
157
                return &arch_def_mipsel;
9,999✔
158
        case SCMP_ARCH_MIPS64:
6,212✔
159
                return &arch_def_mips64;
6,212✔
160
        case SCMP_ARCH_MIPSEL64:
7,848✔
161
                return &arch_def_mipsel64;
7,848✔
162
        case SCMP_ARCH_MIPS64N32:
4,676✔
163
                return &arch_def_mips64n32;
4,676✔
164
        case SCMP_ARCH_MIPSEL64N32:
4,367✔
165
                return &arch_def_mipsel64n32;
4,367✔
166
        case SCMP_ARCH_PARISC:
4,696✔
167
                return &arch_def_parisc;
4,696✔
168
        case SCMP_ARCH_PARISC64:
6,240✔
169
                return &arch_def_parisc64;
6,240✔
170
        case SCMP_ARCH_PPC:
4,644✔
171
                return &arch_def_ppc;
4,644✔
172
        case SCMP_ARCH_PPC64:
4,834✔
173
                return &arch_def_ppc64;
4,834✔
174
        case SCMP_ARCH_PPC64LE:
27,453✔
175
                return &arch_def_ppc64le;
27,453✔
176
        case SCMP_ARCH_S390:
4,726✔
177
                return &arch_def_s390;
4,726✔
178
        case SCMP_ARCH_S390X:
7,468✔
179
                return &arch_def_s390x;
7,468✔
180
        case SCMP_ARCH_RISCV64:
10,482✔
181
                return &arch_def_riscv64;
10,482✔
182
        case SCMP_ARCH_SHEB:
1,876✔
183
                return &arch_def_sheb;
1,876✔
184
        case SCMP_ARCH_SH:
9,995✔
185
                return &arch_def_sh;
9,995✔
186
        }
187

188
        return NULL;
×
189
}
190

191
/**
192
 * Lookup the architecture definition by name
193
 * @param arch_name the architecture name
194
 *
195
 * Return the matching architecture definition, returns NULL on failure.
196
 *
197
 */
198
const struct arch_def *arch_def_lookup_name(const char *arch_name)
7,708✔
199
{
200
        if (strcmp(arch_name, "x86") == 0)
7,708✔
201
                return &arch_def_x86;
202
        else if (strcmp(arch_name, "x86_64") == 0)
7,332✔
203
                return &arch_def_x86_64;
204
        else if (strcmp(arch_name, "x32") == 0)
6,686✔
205
                return &arch_def_x32;
206
        else if (strcmp(arch_name, "arm") == 0)
6,368✔
207
                return &arch_def_arm;
208
        else if (strcmp(arch_name, "aarch64") == 0)
6,050✔
209
                return &arch_def_aarch64;
210
        else if (strcmp(arch_name, "loongarch64") == 0)
5,656✔
211
                return &arch_def_loongarch64;
212
        else if (strcmp(arch_name, "m68k") == 0)
5,264✔
213
                return &arch_def_m68k;
214
        else if (strcmp(arch_name, "mips") == 0)
4,986✔
215
                return &arch_def_mips;
216
        else if (strcmp(arch_name, "mipsel") == 0)
4,708✔
217
                return &arch_def_mipsel;
218
        else if (strcmp(arch_name, "mips64") == 0)
4,354✔
219
                return &arch_def_mips64;
220
        else if (strcmp(arch_name, "mipsel64") == 0)
4,076✔
221
                return &arch_def_mipsel64;
222
        else if (strcmp(arch_name, "mips64n32") == 0)
3,762✔
223
                return &arch_def_mips64n32;
224
        else if (strcmp(arch_name, "mipsel64n32") == 0)
3,484✔
225
                return &arch_def_mipsel64n32;
226
        else if (strcmp(arch_name, "parisc64") == 0)
3,170✔
227
                return &arch_def_parisc64;
228
        else if (strcmp(arch_name, "parisc") == 0)
2,892✔
229
                return &arch_def_parisc;
230
        else if (strcmp(arch_name, "ppc") == 0)
2,614✔
231
                return &arch_def_ppc;
232
        else if (strcmp(arch_name, "ppc64") == 0)
2,296✔
233
                return &arch_def_ppc64;
234
        else if (strcmp(arch_name, "ppc64le") == 0)
2,018✔
235
                return &arch_def_ppc64le;
236
        else if (strcmp(arch_name, "s390") == 0)
1,582✔
237
                return &arch_def_s390;
238
        else if (strcmp(arch_name, "s390x") == 0)
1,264✔
239
                return &arch_def_s390x;
240
        else if (strcmp(arch_name, "riscv64") == 0)
946✔
241
                return &arch_def_riscv64;
242
        else if (strcmp(arch_name, "sheb") == 0)
632✔
243
                return &arch_def_sheb;
244
        else if (strcmp(arch_name, "sh") == 0)
354✔
245
                return &arch_def_sh;
354✔
246

247
        return NULL;
248
}
249

250
/**
251
 * Determine the argument offset for the lower 32 bits
252
 * @param arch the architecture definition
253
 * @param arg the argument number
254
 *
255
 * Determine the correct offset for the low 32 bits of the given argument based
256
 * on the architecture definition.  Returns the offset on success, negative
257
 * values on failure.
258
 *
259
 */
260
int arch_arg_offset_lo(const struct arch_def *arch, unsigned int arg)
233,434✔
261
{
262
        if (arch_valid(arch->token) < 0)
233,434✔
263
                return -EDOM;
264

265
        switch (arch->endian) {
233,434✔
266
        case ARCH_ENDIAN_LITTLE:
212,338✔
267
                return default_arg_offset(arg);
212,338✔
268
                break;
21,096✔
269
        case ARCH_ENDIAN_BIG:
21,096✔
270
                return default_arg_offset(arg) + 4;
21,096✔
271
                break;
272
        default:
273
                return -EDOM;
274
        }
275
}
276

277
/**
278
 * Determine the argument offset for the high 32 bits
279
 * @param arch the architecture definition
280
 * @param arg the argument number
281
 *
282
 * Determine the correct offset for the high 32 bits of the given argument
283
 * based on the architecture definition.  Returns the offset on success,
284
 * negative values on failure.
285
 *
286
 */
287
int arch_arg_offset_hi(const struct arch_def *arch, unsigned int arg)
190,414✔
288
{
289
        if (arch_valid(arch->token) < 0 || arch->size != ARCH_SIZE_64)
190,414✔
290
                return -EDOM;
291

292
        switch (arch->endian) {
190,414✔
293
        case ARCH_ENDIAN_LITTLE:
182,854✔
294
                return default_arg_offset(arg) + 4;
182,854✔
295
                break;
7,560✔
296
        case ARCH_ENDIAN_BIG:
7,560✔
297
                return default_arg_offset(arg);
7,560✔
298
                break;
299
        default:
300
                return -EDOM;
301
        }
302
}
303

304
/**
305
 * Determine the argument offset
306
 * @param arch the architecture definition
307
 * @param arg the argument number
308
 *
309
 * Determine the correct offset for the given argument based on the
310
 * architecture definition.  Returns the offset on success, negative values on
311
 * failure.
312
 *
313
 */
314
int arch_arg_offset(const struct arch_def *arch, unsigned int arg)
50,420✔
315
{
316
        return arch_arg_offset_lo(arch, arg);
50,420✔
317
}
318

319
/**
320
 * Resolve a syscall name to a number
321
 * @param arch the architecture definition
322
 * @param name the syscall name
323
 *
324
 * Resolve the given syscall name to the syscall number based on the given
325
 * architecture.  Returns the syscall number on success, including negative
326
 * pseudo syscall numbers; returns __NR_SCMP_ERROR on failure.
327
 *
328
 */
329
int arch_syscall_resolve_name(const struct arch_def *arch, const char *name)
177,604✔
330
{
331
        if (arch->syscall_resolve_name)
15,023✔
332
                return (*arch->syscall_resolve_name)(arch, name);
117,796✔
333
        if (arch->syscall_resolve_name_raw)
59,808✔
334
                return (*arch->syscall_resolve_name_raw)(name);
59,808✔
335

336
        return __NR_SCMP_ERROR;
337
}
338

339
/**
340
 * Resolve a syscall number to a name
341
 * @param arch the architecture definition
342
 * @param num the syscall number
343
 *
344
 * Resolve the given syscall number to the syscall name based on the given
345
 * architecture.  Returns a pointer to the syscall name string on success,
346
 * including pseudo syscall names; returns NULL on failure.
347
 *
348
 */
349
const char *arch_syscall_resolve_num(const struct arch_def *arch, int num)
178,233✔
350
{
351
        if (arch->syscall_resolve_num)
15,640✔
352
                return (*arch->syscall_resolve_num)(arch, num);
7,020✔
353
        if (arch->syscall_resolve_num_raw)
171,213✔
354
                return (*arch->syscall_resolve_num_raw)(num);
171,213✔
355

356
        return NULL;
357
}
358

359
/**
360
 * Translate the syscall number
361
 * @param arch the architecture definition
362
 * @param syscall the syscall number
363
 *
364
 * Translate the syscall number, in the context of the native architecture, to
365
 * the provided architecture.  Returns zero on success, negative values on
366
 * failure.
367
 *
368
 */
369
int arch_syscall_translate(const struct arch_def *arch, int *syscall)
263,397✔
370
{
371
        int sc_num;
263,397✔
372
        const char *sc_name;
263,397✔
373

374
        /* special handling for syscall -1 */
375
        if (*syscall == -1)
263,397✔
376
                return 0;
377

378
        if (arch->token != arch_def_native->token) {
263,379✔
379
                sc_name = arch_syscall_resolve_num(arch_def_native, *syscall);
162,593✔
380
                if (sc_name == NULL)
162,593✔
381
                        return -EFAULT;
12✔
382

383
                sc_num = arch_syscall_resolve_name(arch, sc_name);
162,581✔
384
                if (sc_num == __NR_SCMP_ERROR)
162,581✔
385
                        return -EFAULT;
×
386

387
                *syscall = sc_num;
162,581✔
388
        }
389

390
        return 0;
391
}
392

393
/**
394
 * Rewrite a syscall value to match the architecture
395
 * @param arch the architecture definition
396
 * @param syscall the syscall number
397
 *
398
 * Syscalls can vary across different architectures so this function rewrites
399
 * the syscall into the correct value for the specified architecture. Returns
400
 * zero on success, -EDOM if the syscall is not defined for @arch, and negative
401
 * values on failure.
402
 *
403
 */
404
int arch_syscall_rewrite(const struct arch_def *arch, int *syscall)
1,402✔
405
{
406
        int sys = *syscall;
1,402✔
407

408
        if (sys >= -1) {
1,402✔
409
                /* we shouldn't be here - no rewrite needed */
410
                return 0;
411
        } else if (sys > -100) {
145✔
412
                /* -2 to -99 are reserved values */
413
                return -EINVAL;
414
        } else if (sys > -10000) {
145✔
415
                /* rewritable syscalls */
416
                if (arch->syscall_rewrite)
86✔
417
                        (*arch->syscall_rewrite)(arch, syscall);
86✔
418
        }
419

420
        /* syscalls not defined on this architecture */
421
        if ((*syscall) < 0)
145✔
422
                return -EDOM;
59✔
423
        return 0;
424
}
425

426
/**
427
 * Add a new rule to the specified filter
428
 * @param db the seccomp filter db
429
 * @param strict the rule
430
 *
431
 * This function adds a new argument/comparison/value to the seccomp filter for
432
 * a syscall; multiple arguments can be specified and they will be chained
433
 * together (essentially AND'd together) in the filter.  When the strict flag
434
 * is true the function will fail if the exact rule can not be added to the
435
 * filter, if the strict flag is false the function will not fail if the
436
 * function needs to adjust the rule due to architecture specifics.  Returns
437
 * zero on success, negative values on failure.
438
 *
439
 * It is important to note that in the case of failure the db may be corrupted,
440
 * the caller must use the transaction mechanism if the db integrity is
441
 * important.
442
 *
443
 */
444
int arch_filter_rule_add(struct db_filter *db,
263,315✔
445
                         const struct db_api_rule_list *rule)
446
{
447
        int rc = 0;
263,315✔
448
        int syscall;
263,315✔
449
        struct db_api_rule_list *rule_dup = NULL;
263,315✔
450

451
        /* create our own rule that we can munge */
452
        rule_dup = db_rule_dup(rule);
263,315✔
453
        if (rule_dup == NULL)
263,315✔
454
                return -ENOMEM;
455

456
        /* translate the syscall */
457
        rc = arch_syscall_translate(db->arch, &rule_dup->syscall);
263,315✔
458
        if (rc < 0)
263,315✔
459
                goto rule_add_return;
12✔
460
        syscall = rule_dup->syscall;
263,303✔
461

462
        /* add the new rule to the existing filter */
463
        if (syscall == -1 || db->arch->rule_add == NULL) {
263,303✔
464
                /* syscalls < -1 require a db->arch->rule_add() function */
465
                if (syscall < -1 && rule_dup->strict) {
186,647✔
466
                        rc = -EDOM;
1✔
467
                        goto rule_add_return;
1✔
468
                }
469
                rc = db_rule_add(db, rule_dup);
186,646✔
470
        } else
471
                rc = (db->arch->rule_add)(db, rule_dup);
76,656✔
472

473
rule_add_return:
263,315✔
474
        /* NOTE: another reminder that we don't do any db error recovery here,
475
         * use the transaction mechanism as previously mentioned */
476
        if (rule_dup != NULL)
263,315✔
477
                free(rule_dup);
263,315✔
478
        return rc;
263,315✔
479
}
480

NEW
481
int arch_add_kver_rule(struct db_filter *db, struct db_api_rule_list *rule,
×
482
                       enum scmp_kver supported_kver, bool *added)
483
{
NEW
484
        struct db_sys_list *s_iter;
×
NEW
485
        enum scmp_kver syscall_added_ver;
×
NEW
486
        const char *syscall_name;
×
NEW
487
        bool exists = false;
×
NEW
488
        int rc;
×
489

NEW
490
        *added = false;
×
491

NEW
492
        db_list_foreach(s_iter, db->syscalls) {
×
NEW
493
                if (rule->syscall == s_iter->num)
×
494
                        /* Do not overwrite existing rules */
NEW
495
                        exists = true;
×
496
        }
497

NEW
498
        if (exists)
×
499
                return 0;
500

NEW
501
        syscall_name = (db->arch->syscall_resolve_num_raw)(rule->syscall);
×
NEW
502
        if (syscall_name == NULL)
×
503
                /* This syscall number is invalid in this architecture */
504
                return 0;
505

NEW
506
        syscall_added_ver = (db->arch->syscall_num_kver)(rule->syscall);
×
NEW
507
        if (syscall_added_ver > supported_kver)
×
508
                /* This syscall is newer than the kernel version supported by
509
                 * the application.  Don't add it to the rule, and therefore
510
                 * it  will be subject to the attr->act_unknown action */
511
                return 0;
512

NEW
513
        *added = true;
×
NEW
514
        if (db->arch->rule_add == NULL)
×
NEW
515
                rc = db_rule_add(db, rule);
×
516
        else
NEW
517
                rc = (db->arch->rule_add)(db, rule);
×
518

519
        return rc;
520
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc