名称

seccomp-在进程的安全计算状态下运行

另外参见

bpfc(1),strace(1),bpf(2),prctl(2),ptrace(2),sigaction(2),proc(5),signal(7),socket(7)

libseccomp库中的各个页面,包括:scmp_sys_resolver(1),seccomp_export_bpf(3),seccomp_init(3),seccomp_load(3)和seccomp_rule_add(3)。

内核源文件Documentation / networking / filter.txt和Documentation / userspace-api / seccomp_filter.rst(或Linux 4.13之前的Documentation / prctl / seccomp_filter.txt)。

McCanne,S.和Jacobson,V.(1992),BSD数据包过滤器:一种用于用户级数据包捕获的新架构,USENIX 1993年冬季会议论文集

SECCOMP - Linux手册页

Linux程序员手册 第2部分
更新日期: 2020-06-09

返回值

成功时,seccomp()返回0。错误时,如果使用SECCOMP_FILTER_FLAG_TSYNC,则返回值为导致同步失败的线程的ID。 (此ID是clone(2)和gettid(2)返回的类型的内核线程ID。)在其他错误上,返回-1,并设置errno来指示错误原因。

遵循规范

seccomp()系统调用是非标准的Linux扩展。

备注

您可能更喜欢使用libseccomp库,而不是手工编写seccomp过滤器,如下面的示例所示,该库提供了用于生成seccomp过滤器的前端。

/ proc / [pid] / status文件的Seccomp字段提供了一种查看进程的seccomp模式的方法。参见proc(5)。

seccomp()提供了prctl(2)PR_SET_SECCOMP操作(不支持标志)提供的功能的超集。

从Linux 4.4开始,可以使用ptrace(2)PTRACE_SECCOMP_GET_FILTER操作来转储进程的seccomp过滤器。

Architecture support for seccomp BPF

以下架构提供对seccomp BPF过滤的架构支持:

*
x86-64,i386,x32(从Linux 3.5开始)
*
ARM(自Linux 3.8起)
*
s390(从Linux 3.8开始)
*
MIPS(从Linux 3.16开始)
*
ARM-64(从Linux 3.19开始)
*
PowerPC(从Linux 4.3开始)
*
Tile(从Linux 4.3开始)
*
PA-RISC(自Linux 4.6起)

Caveats

在将seccomp过滤器应用于程序时,需要考虑各种细微之处,包括以下内容:

*
一些传统的系统调用在许多体系结构的vdso(7)中都有用户空间实现。值得注意的示例包括clock_gettime(2),gettimeofday(2)和time(2)。在这样的体系结构上,针对这些系统调用的seccomp过滤将无效。 (但是,在某些情况下,vdso(7)实现可能会回退到调用真正的系统调用,在这种情况下,seccomp筛选器将看到系统调用。)
*
Seccomp filtering is based on system call numbers. However, applications typically do not directly invoke system calls, but instead call wrapper functions in the C library which in turn invoke the system calls. Consequently, one must be aware of the following:
*
一些传统系统调用的glibc包装器实际上可能在内核中采用不同名称的系统调用。例如,exit(2)包装函数实际上使用了exit_group(2)系统调用,而fork(2)包装函数实际上调用了clone(2)。
*
包装器功能的行为可能会根据体系结构上提供的系统调用范围而有所不同。换句话说,相同的包装函数可以在不同的体系结构上调用不同的系统调用。
*
最后,包装函数的行为可以在glibc版本之间改变。例如,在较旧的版本中,open(2)的glibc包装器函数调用了相同名称的系统调用,但是从glibc 2.26开始,该实现切换为在所有体系结构上都调用openat(2)。

以上几点的结果是,可能有必要过滤系统调用而不是预期的。第2节中的各种手册页在标题为C库/内核差异的小节中提供了有关包装函数与基础系统调用之间差异的有用详细信息。

此外,请注意,当seccomp筛选器的应用程序可能导致应用程序可能需要执行的合法操作意外失败时,甚至有可能导致应用程序中的错误。如果这些错误发生在很少使用的应用程序代码路径中,则在测试seccomp筛选器时可能不容易发现这些错误。

Seccomp-specific BPF details

请注意以下特定于seccomp过滤器的BPF详细信息:

*
不支持BPF_H和BPF_B大小修饰符:所有操作都必须加载和存储(4字节)字(BPF_W)。
*
要访问seccomp_data缓冲区的内容,请使用BPF_ABS寻址模式修饰符。
*
BPF_LEN寻址模式修饰符产生立即模式操作数,其值是seccomp_data缓冲区的大小。

语法

#include <linux/seccomp.h>
#include <linux/filter.h>
#include <linux/audit.h>
#include <linux/signal.h>
#include <sys/ptrace.h>

int seccomp(unsigned int operation, unsigned int flags, void *args);

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/

说明

seccomp()系统调用在调用进程的安全计算(seccomp)状态下运行。

当前,Linux支持以下操作值:

SECCOMP_SET_MODE_STRICT
允许调用线程进行的唯一系统调用是read(2),write(2),_ exit(2)(但不包括exit_group(2))和sigreturn(2)。其他系统调用导致SIGKILL信号的传递。严格的安全计算模式对于可能需要执行不受信任的字节码(可能是通过从管道或套接字读取的数据)的数字运算应用程序很有用。
请注意,尽管调用线程无法再调用sigprocmask(2),但可以使用sigreturn(2)阻止除SIGKILL和SIGSTOP之外的所有信号。这意味着,例如alarm(2)不足以限制进程的执行时间。相反,为了可靠地终止该过程,必须使用SIGKILL。这可以通过使用SIGEV_SIGNAL和sigev_signo设置为SIGKILL的timer_create(2)或通过使用setrlimit(2)设置RLIMIT_CPU的硬限制来完成。
仅当内核配置为启用CONFIG_SECCOMP时,此操作才可用。
标志的值必须为0,而args必须为NULL。
此操作在功能上与调用相同:
prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);
SECCOMP_SET_MODE_FILTER
允许的系统调用由指向通过args传递的伯克利分组过滤器(BPF)的指针定义。此参数是指向structsock_fprog的指针;它可以设计为过滤任意系统调用和系统调用参数。如果过滤器无效,则seccomp()失败,并在errno中返回EINVAL。
如果过滤器允许fork(2)或clone(2),则任何子进程都将被限制在与父进程相同的系统调用过滤器中。如果允许execve(2),则在调用execve(2)时将保留现有过滤器。
为了使用SECCOMP_SET_MODE_FILTER操作,调用线程必须在其用户名称空间中具有CAP_SYS_ADMIN功能,或者该线程必须已经设置了no_new_privs位。如果该线程的祖先尚未设置该位,则该线程必须进行以下调用:
prctl(PR_SET_NO_NEW_PRIVS, 1);
否则,SECCOMP_SET_MODE_FILTER操作将失败,并以errno返回EACCES。该要求确保了无特权的进程无法应用恶意过滤器,然后使用execve(2)调用设置用户ID或其他特权程序,从而有可能破坏该程序。 (例如,此类恶意过滤器可能会导致尝试使用setuid(2)将调用者的用户ID设置为非零值,而不是实际进行系统调用而返回0。因此,该程序可能会被欺骗以保留超级用户特权在可能影响它做危险事情的情况下,因为它实际上并未放弃特权。)
如果附加的过滤器允许使用prctl(2)或seccomp(),则可以添加其他过滤器。这将增加评估时间,但可以在执行线程期间进一步减少攻击面。
仅当内核配置为启用CONFIG_SECCOMP_FILTER时,SECCOMP_SET_MODE_FILTER操作才可用。
当标志为0时,此操作在功能上与调用相同:
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, args);
The recognized flags

are:

SECCOMP_FILTER_FLAG_TSYNC
添加新过滤器时,将调用过程的所有其他线程同步到同一seccomp过滤器树。 "过滤器树"是连接到线程的过滤器的有序列表。 (从此角度来看,在单独的seccomp()调用中附加相同的过滤器会导致不同的过滤器。)
如果任何线程无法同步到同一过滤器树,则该调用将不会附加新的seccomp过滤器,并且将失败,并返回找到的第一个无法同步的线程ID。如果同一进程中的另一个线程在SECCOMP_MODE_STRICT中,或者如果它已将新的seccomp筛选器附加到其自身(与调用线程的筛选器树不同),则同步将失败。
SECCOMP_FILTER_FLAG_LOG(since Linux 4.14)
除SECCOMP_RET_ALLOW之外的所有过滤器返回操作均应记录。管理员可以通过阻止通过/ proc / sys / kernel / seccomp / actions_logged文件记录特定操作来覆盖此过滤器标志。
SECCOMP_FILTER_FLAG_SPEC_ALLOW(since Linux 4.17)
禁用推测性存储绕过缓解。
SECCOMP_GET_ACTION_AVAIL(since Linux 4.14)
测试以查看内核是否支持某个操作。该操作有助于确认内核知道最近添加的过滤器返回操作,因为内核将所有未知操作视为SECCOMP_RET_KILL_PROCESS。
标志的值必须为0,并且args必须是指向无符号32位过滤器返回操作的指针。

Filters

通过SECCOMP_SET_MODE_FILTER添加过滤器时,args指向过滤器程序:

struct sock_fprog {
    unsigned short      len;    /* Number of BPF instructions */
    struct sock_filter *filter; /* Pointer to array of
                                   BPF instructions */
};

每个程序必须包含一个或多个BPF指令:

struct sock_filter {            /* Filter block */
    __u16 code;                 /* Actual filter code */
    __u8  jt;                   /* Jump true */
    __u8  jf;                   /* Jump false */
    __u32 k;                    /* Generic multiuse field */
};

当执行指令时,BPF程序将可用的系统调用信息(即,使用BPF_ABS寻址模式)作为以下形式的(只读)缓冲区进行操作:

struct seccomp_data {
    int   nr;                   /* System call number */
    __u32 arch;                 /* AUDIT_ARCH_* value
                                   (see <linux/audit.h>) */
    __u64 instruction_pointer;  /* CPU instruction pointer */
    __u64 args[6];              /* Up to 6 system call arguments */
};

因为系统调用的编号在体系结构之间有所不同,并且某些体系结构(例如x86-64)允许用户空间代码使用多个体系结构的调用约定(并且所使用的约定可能在使用execve(2)的进程的整个生命周期中有所不同。 )以执行采用不同约定的二进制文件),通常必须验证arch字段的值。

强烈建议尽可能使用允许列表方法,因为这种方法更加健壮和简单。每当添加潜在危险的系统调用时(或如果危险标志或选项被拒绝列出,则必须添加危险标志或选项),都必须更新拒绝列表,并且通常可以在不改变其含义的情况下更改值的表示形式,从而导致拒绝列表绕过。另请参见下面的警告。

对于所有调用约定,arch字段并不是唯一的。 x86-64 ABI和x32 ​​ABI都使用AUDIT_ARCH_X86_64作为arch,并且它们在同一处理器上运行。而是在系统调用号上使用掩码__X32_SYSCALL_BIT来区分两个ABI。

这意味着策略必须拒绝带有__X32_SYSCALL_BIT的所有系统调用,或者它必须识别设置和不设置__X32_SYSCALL_BIT的系统调用。设置__X32_SYSCALL_BIT的恶意程序可以绕过基于nr拒绝的系统调用列表,该列表也不包含设置了__X32_SYSCALL_BIT的nr值。

此外,Linux 5.4之前的内核错误地允许512-547范围内的nr以及与__X32_SYSCALL_BIT进行或运算的相应非x32 syscall。例如,nr == 521和nr ==(101 | __X32_SYSCALL_BIT)将导致内核中可能带有混淆的x32-vs-x86_64语义的ptrace(2)调用。打算在Linux 5.4之前的内核上使用的策略必须确保它们拒绝或正确处理这些系统调用。在Linux 5.4和更高版本上,此类系统调用将失败,并显示错误ENOSYS,而无需执行任何操作。

Instruction_pointer字段提供了执行系统调用的机器语言指令的地址。结合使用/ proc / [pid] / maps来执行检查,该检查基于程序的哪个区域(映射)进行了系统调用,这可能很有用。 (也许,明智的做法是锁定mmap(2)和mprotect(2)系统调用,以防止程序破坏此类检查。)

在检查args的值时,请记住,参数通常在被处理之前,而是在seccomp检查之后被静默截断。例如,如果在x86-64内核上使用i386 ABI,则会发生这种情况:尽管内核通常不会超出参数的32位最低位,但是完整的64位寄存器的值将出现在seccomp数据中。一个不太令人惊讶的示例是,如果x86-64 ABI用于执行接受类型为int的参数的系统调用,则该参数寄存器中最重要的一半将被系统调用忽略,但在seccomp数据中可见。

seccomp过滤器返回一个由两部分组成的32位值:最高有效的16位(对应于由常量SECCOMP_RET_ACTION_FULL定义的掩码)包含以下列出的"操作"值之一;最低有效的16位(由常量SECCOMP_RET_DATA定义)是与该返回值关联的"数据"。

如果存在多个过滤器,则所有过滤器都将按照它们添加到过滤器树的相反顺序执行-即,首先执行最近安装的过滤器。 (请注意,即使较早的过滤器之一返回SECCOMP_RET_KILL也会调用所有过滤器。这样做是为了简化内核代码,并通过避免检查这种不常见的情况来稍微加快过滤器集合的执行速度。 )用于评估给定系统调用的返回值是通过执行所有过滤器返回的具有最高优先级的优先动作值(及其伴随数据)。

按照优先级从高到低的顺序,seccomp过滤器可能返回的操作值是:

SECCOMP_RET_KILL_PROCESS(since Linux 4.14)
该值导致进程立即终止,并带有核心转储。系统调用未执行。与下面的SECCOMP_RET_KILL_THREAD相比,该线程组中的所有线程都被终止。 (有关线程组的讨论,请参见clone(2)中对CLONE_THREAD标志的描述。)
该过程似乎被SIGSYS信号终止了。即使已经为SIGSYS注册了信号处理程序,在这种情况下,该处理程序也将被忽略,并且该过程始终会终止。对于正在等待该进程的父进程(使用waitpid(2)或类似方法),返回的wstatus将指示其子进程已被SIGSYS信号终止。
SECCOMP_RET_KILL_THREAD(or SECCOMP_RET_KILL)
该值导致立即终止进行系统调用的线程。系统调用未执行。同一线程组中的其他线程将继续执行。
线程终止,好像被SIGSYS信号杀死了一样。请参阅上面的SECCOMP_RET_KILL_PROCESS。
在Linux 4.11之前,以这种方式终止的任何进程都不会触发coredump(即使在signal(7)中将SIGSYS记录为具有默认的以core dump终止的动作)。从Linux 4.11开始,如果以此方式终止,则单线程进程将转储核心。
在Linux 4.14中添加了SECCOMP_RET_KILL_PROCESS时,添加了SECCOMP_RET_KILL_THREAD作为SECCOMP_RET_KILL的同义词,以便更清楚地区分这两个动作。
SECCOMP_RET_TRAP
This value results in the kernel sending a thread-directed SIGSYS

signal to the triggering thread.
(The system call is not executed.)
Various fields will be set in the
soirnfo_t

structure (see
sigaction(2))

associated with signal:

*
si_signo将包含SIGSYS。
*
si_call_addr将显示系统调用指令的地址。
*
si_syscall和si_arch将指示尝试了哪个系统调用。
*
si_code将包含SYS_SECCOMP。
*
si_errno将包含过滤器返回值的SECCOMP_RET_DATA部分。
程序计数器将好像发生了系统调用一样(即程序计数器将不会指向系统调用指令)。返回值寄存器将包含与体系结构相关的值;如果恢复执行,请将其设置为适合系统调用的内容。 (体系结构依赖性是因为用ENOSYS替换它可能会覆盖一些有用的信息。)
SECCOMP_RET_ERRNO
此值导致过滤器返回值的SECCOMP_RET_DATA部分作为errno值传递到用户空间,而无需执行系统调用。
SECCOMP_RET_TRACE
返回时,此值将导致内核在执行系统调用之前尝试通知基于ptrace(2)的跟踪器。如果不存在跟踪器,则系统调用不会执行,并且会返回错误状态,并将errno设置为ENOSYS。
如果跟踪器使用ptrace(PTRACE_SETOPTIONS)请求PTRACE_O_TRACESECCOMP,则将通知该跟踪器。跟踪器将收到PTRACE_EVENT_SECCOMP的通知,并且过滤器的返回值的SECCOMP_RET_DATA部分将通过PTRACE_GETEVENTMSG对跟踪器可用。
跟踪器可以通过将系统调用号更改为-1来跳过系统调用。或者,跟踪程序可以通过将系统调用更改为有效的系统调用号码来更改请求的系统调用。如果跟踪器要求跳过系统调用,则系统调用将显示为返回跟踪器放入返回值寄存器中的值。
在内核4.8之前,通知跟踪器后将不再运行seccomp检查。 (这意味着,在较旧的内核上,基于seccomp的沙箱一定不能允许使用ptrace(2)-甚至是其他沙箱进程-无需格外小心; ptracers可以使用此机制从seccomp沙箱中逸出。)
SECCOMP_RET_LOG(since Linux 4.14)
此值导致在记录过滤器返回操作之后执行系统调用。管理员可以通过/ proc / sys / kernel / seccomp / actions_logged文件覆盖此操作的日志记录。
SECCOMP_RET_ALLOW
该值导致系统调用被执行。

如果指定了除上述操作以外的操作值,则将过滤器操作视为SECCOMP_RET_KILL_PROCESS(从Linux 4.14开始)或SECCOMP_RET_KILL_THREAD(在Linux 4.13和更低版本中)。

/proc interfaces

目录/ proc / sys / kernel / seccomp中的文件提供了其他seccomp信息和配置:

actions_avail(since Linux 4.14)
seccomp过滤器的只读排序列表以字符串形式返回操作。从左到右的顺序是从小到大。该列表表示内核支持的seccomp过滤器返回操作集。
actions_logged(since Linux 4.14)
允许记录seccomp过滤器返回操作的读写有序列表。对该文件的写入无需采用有序形式,但是从文件中读取的操作将与actions_avail文件相同。
重要的是要注意,当将审核子系统配置为审核任务时,actions_logged的值不会阻止记录某些筛选器返回操作。如果在actions_logged文件中未找到该动作,则最终是否决定对该任务的动作的最终决定权最终由审计子系统决定,以决定除SECCOMP_RET_ALLOW之外的所有过滤器返回动作。
由于无法记录SECCOMP_RET_ALLOW操作,因此无法在" actions_logged"文件中接受" allow"字符串。尝试向文件写入"允许"将失败,并显示错误EINVAL。

Audit logging of seccomp actions

从Linux 4.14开始,内核提供了将seccomp过滤器返回的操作记录在审核日志中的功能。内核根据操作类型,是否在actions_logged文件中存在该操作以及是否启用内核审核(例如,通过内核启动选项audit = 1)来决定是否记录操作。规则如下:

*
如果该操作是SECCOMP_RET_ALLOW,则不记录该操作。
*
否则,如果该操作是SECCOMP_RET_KILL_PROCESS或SECCOMP_RET_KILL_THREAD,并且该操作出现在actions_logged文件中,则将记录该操作。
*
否则,如果过滤器已请求日志记录(SECCOMP_FILTER_FLAG_LOG标志)并且该操作出现在actions_logged文件中,则将记录该操作。
*
否则,如果启用了内核审核并且正在审核进程(autrace(8)),则会记录该操作。
*
否则,不会记录该操作。

版本

seccomp()系统调用首先出现在Linux 3.17中。

错误说明

seccomp()可能由于以下原因而失败:

EACCES
调用方在其用户名称空间中没有CAP_SYS_ADMIN功能,或者在使用SECCOMP_SET_MODE_FILTER之前未设置no_new_privs。
EFAULT
args不是有效地址。
EINVAL
该内核版本或配置不支持该操作,或者不支持该操作。
EINVAL
指定的标志对于给定的操作无效。
EINVAL
操作包括BPF_ABS,但指定的偏移量未与32位边界对齐或超出sizeof(structseccomp_data)。
EINVAL
已经设置了安全计算模式,并且操作与现有设置不同。
EINVAL
操作指定了SECCOMP_SET_MODE_FILTER,但是args指向的过滤器程序无效,或者过滤器程序的长度为零或超过了BPF_MAXINSNS(4096)指令。
ENOMEM
内存不足。
ENOMEM
附加到调用线程的所有筛选器程序的总长度将超过MAX_INSNS_PER_PATH(32768)指令。请注意,出于计算此限制的目的,每个已经存在的过滤器程序都会产生4条指令的开销损失。
EOPNOTSUPP
该操作指定了SECCOMP_GET_ACTION_AVAIL,但内核不支持args指定的过滤器返回操作。
ESRCH
另一个线程在线程同步期间导致失败,但是无法确定其ID。

示例

下面的程序接受四个或更多参数。前三个参数是系统调用号,数字体系结构标识符和错误号。程序使用这些值构造一个BPF筛选器,该筛选器在运行时用于执行以下检查:

[1]
如果程序未在指定的体系结构上运行,则BPF筛选器会导致系统调用失败,并显示错误ENOSYS。
[2]
如果程序尝试使用指定的编号执行系统调用,则BPF筛选器会导致系统调用失败,并将errno设置为指定的错误编号。

其余命令行参数指定示例程序应尝试使用execv(3)(使用execve(2)系统调用的库函数)执行的程序的路径名和其他参数。该程序的一些示例运行如下所示。

首先,我们显示正在运行的体系结构(x86-64),然后构造一个shell函数,该函数在此体系结构上查找系统调用号:

$ uname -m
x86_64
$ syscall_nr() {
    cat /usr/src/linux/arch/x86/syscalls/syscall_64.tbl | \
    awk ' != "x32" &&  == "''" { print  }'
}

当BPF筛选器拒绝系统调用时(上述情况[2]),它将导致系统调用失败,并在命令行上指定错误号。在此处显示的实验中,我们将使用错误号99:

$ errno 99
EADDRNOTAVAIL 99 Cannot assign requested address

在下面的示例中,我们尝试运行命令whoami(1),但是BPF过滤器拒绝execve(2)系统调用,因此甚至不执行该命令:

$ syscall_nr execve
59
$ ./a.out
Usage: ./a.out <syscall_nr> <arch> <errno> <prog> [<args>]
Hint for <arch>: AUDIT_ARCH_I386: 0x40000003
                 AUDIT_ARCH_X86_64: 0xC000003E
$ ./a.out 59 0xC000003E 99 /bin/whoami
execv: Cannot assign requested address

在下一个示例中,BPF过滤器拒绝write(2)系统调用,因此,尽管它已成功启动,但是whoami(1)命令无法写入输出:

$ syscall_nr write
1
$ ./a.out 1 0xC000003E 99 /bin/whoami

在最后一个示例中,BPF筛选器拒绝whoami(1)命令未使用的系统调用,因此它能够成功执行并产生输出:

$ syscall_nr preadv
295
$ ./a.out 295 0xC000003E 99 /bin/whoami
cecilia

Program source

#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>

#define X32_SYSCALL_BIT 0x40000000

static int
install_filter(int syscall_nr, int t_arch, int f_errno)
{
    unsigned int upper_nr_limit = 0xffffffff;

    /* Assume that AUDIT_ARCH_X86_64 means the normal x86-64 ABI
       (in the x32 ABI, all system calls have bit 30 set in the
       'nr' field, meaning the numbers are >= X32_SYSCALL_BIT) */
    if (t_arch == AUDIT_ARCH_X86_64)
        upper_nr_limit = X32_SYSCALL_BIT - 1;

    struct sock_filter filter[] = {
        /* [0] Load architecture from 'seccomp_data' buffer into
               accumulator */
        BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
                 (offsetof(struct seccomp_data, arch))),

        /* [1] Jump forward 5 instructions if architecture does not
               match 't_arch' */
        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, t_arch, 0, 5),

        /* [2] Load system call number from 'seccomp_data' buffer into
               accumulator */
        BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
                 (offsetof(struct seccomp_data, nr))),

        /* [3] Check ABI - only needed for x86-64 in deny-list use
               cases.  Use BPF_JGT instead of checking against the bit
               mask to avoid having to reload the syscall number. */
        BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, upper_nr_limit, 3, 0),

        /* [4] Jump forward 1 instruction if system call number
               does not match 'syscall_nr' */
        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, syscall_nr, 0, 1),

        /* [5] Matching architecture and system call: don't execute
           the system call, and return 'f_errno' in 'errno' */
        BPF_STMT(BPF_RET | BPF_K,
                 SECCOMP_RET_ERRNO | (f_errno & SECCOMP_RET_DATA)),

        /* [6] Destination of system call number mismatch: allow other
               system calls */
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),

        /* [7] Destination of architecture mismatch: kill task */
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
    };

    struct sock_fprog prog = {
        .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])),
        .filter = filter,
    };

    if (seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog)) {
        perror("seccomp");
        return 1;
    }

    return 0;
}

int
main(int argc, char **argv)
{
    if (argc < 5) {
        fprintf(stderr, "Usage: "
                "%s <syscall_nr> <arch> <errno> <prog> [<args>]\n"
                "Hint for <arch>: AUDIT_ARCH_I386: 0x%X\n"
                "                 AUDIT_ARCH_X86_64: 0x%X\n"
                "\n", argv[0], AUDIT_ARCH_I386, AUDIT_ARCH_X86_64);
        exit(EXIT_FAILURE);
    }

    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
        perror("prctl");
        exit(EXIT_FAILURE);
    }

    if (install_filter(strtol(argv[1], NULL, 0),
                       strtol(argv[2], NULL, 0),
                       strtol(argv[3], NULL, 0)))
        exit(EXIT_FAILURE);

    execv(argv[4], &argv[4]);
    perror("execv");
    exit(EXIT_FAILURE);
}
日期:2019-08-20 17:59:18 来源:oir作者:oir