Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,168 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../../disclaimer-zh_CN.rst
:Original: Documentation/userspace-api/accelerators/ocxl.rst
:翻译:
李睿 Rui Li <me@lirui.org>
=====================================
OpenCAPI (开放相干加速器处理器接口)
=====================================
*OpenCAPI: Open Coherent Accelerator Processor Interface*
OpenCAPI是处理器和加速器之间的一个接口,致力于达到低延迟和高带宽。该规范
由 `OpenCAPI Consortium <http://opencapi.org/>`_ 开发。
它允许加速器(可以是FPGA、ASIC等)使用虚拟地址连贯地访问主机内存。一个OpenCAPI
设备也可以托管它自己的内存,并可以由主机访问。
OpenCAPI在Linux中称为“ocxl”,它作为“cxl”(用于powerpc的IBM CAPI接口的驱动)的
开放、处理器无关的演进,这么命名是为了避免与ISDN CAPI子系统相混淆。
高层视角
========
OpenCAPI定义了一个在物理链路层上实现的数据链路层(TL)和传输层(TL)。任何
实现DL和TL的处理器或者设备都可以开始共享内存。
::
+-----------+ +-------------+
| | | |
| | | Accelerated |
| Processor | | Function |
| | +--------+ | Unit | +--------+
| |--| Memory | | (AFU) |--| Memory |
| | +--------+ | | +--------+
+-----------+ +-------------+
| |
+-----------+ +-------------+
| TL | | TLX |
+-----------+ +-------------+
| |
+-----------+ +-------------+
| DL | | DLX |
+-----------+ +-------------+
| |
| PHY |
+---------------------------------------+
Processor:处理器
Memory:内存
Accelerated Function Unit:加速功能单元
设备发现
========
OpenCAPI依赖一个在设备上实现的与PCI类似的配置空间。因此主机可以通过查询
配置空间来发现AFU。
OpenCAPI设备在Linux中被当作类PCI设备(有一些注意事项)。固件需要对硬件进行
抽象,就好像它是一个PCI链路。许多已有的PCI架构被重用:在模拟标准PCI时,
设备被扫描并且BAR(基址寄存器)被分配。像“lspci”的命令因此可以被用于查看
哪些设备可用。
配置空间定义了可以在物理适配器上可以被找到的AFU,比如它的名字、支持多少内
存上下文、内存映射IO(MMIO)区域的大小等。
MMIO
====
OpenCAPI为每个AFU定义了两个MMIO区域:
* 全局MMIO区域,保存和整个AFU相关的寄存器。
* 每个进程的MMIO区域,对于每个上下文固定大小。
AFU中断
=======
OpenCAPI拥有AFU向主机进程发送中断的可能性。它通过定义在传输层的“intrp_req”
来完成,指定一个定义中断的64位对象句柄。
驱动允许一个进程分配中断并获取可以传递给AFU的64位对象句柄。
字符设备
========
驱动为每个在物理设备上发现的AFU创建一个字符设备。一个物理设备可能拥有多个
功能,一个功能可以拥有多个AFU。不过编写这篇文档之时,只对导出一个AFU的设备
测试过。
字符设备可以在 /dev/ocxl/ 中被找到,其命名为:
/dev/ocxl/<AFU 名称>.<位置>.<索引>
<AFU 名称> 是一个最长20个字符的名称,和在AFU配置空间中找到的相同。
<位置>由驱动添加,可在系统有不止一个相同的OpenCAPI设备时帮助区分设备。
<索引>也是为了在少见情况下帮助区分AFU,即设备携带多个同样的AFU副本时。
Sysfs类
=======
添加了代表AFU的ocxl类。查看/sys/class/ocxl。布局在
Documentation/ABI/testing/sysfs-class-ocxl 中描述。
用户API
=======
打开
----
基于在配置空间中找到的AFU定义,AFU可能支持在多个内存上下文中工作,这种情况
下相关的字符设备可以被不同进程多次打开。
ioctl
-----
OCXL_IOCTL_ATTACH:
附加调用进程的内存上下文到AFU,以允许AFU访问其内存。
OCXL_IOCTL_IRQ_ALLOC:
分配AFU中断,返回标识符。
OCXL_IOCTL_IRQ_FREE:
释放之前分配的AFU中断。
OCXL_IOCTL_IRQ_SET_FD:
将一个事件文件描述符和AFU中断关联,因此用户进程可以在AFU发送中断时收到通
知。
OCXL_IOCTL_GET_METADATA:
从卡中获取配置信息,比如内存映射IO区域的大小、AFU版本和当前上下文的进程
地址空间ID(PASID)。
OCXL_IOCTL_ENABLE_P9_WAIT:
允许AFU唤醒执行“等待”的用户空间进程。返回信息给用户空间,允许其配置AFU。
注意这只在POWER9上可用。
OCXL_IOCTL_GET_FEATURES:
报告用户空间可用的影响OpenCAPI的CPU特性。
mmap
----
一个进程可以mmap每个进程的MMIO区域来和AFU交互。
@@ -0,0 +1,22 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../../disclaimer-zh_CN.rst
:Original: Documentation/userspace-api/ebpf/index.rst
:翻译:
李睿 Rui Li <me@lirui.org>
eBPF 用户空间API
================
eBPF是一种在Linux内核中提供沙箱化运行环境的机制,它可以在不改变内核源码或加载
内核模块的情况下扩展运行时和编写工具。eBPF程序能够被附加到各种内核子系统中,包
括网络,跟踪和Linux安全模块(LSM)等。
关于eBPF的内部内核文档,请查看 Documentation/bpf/index.rst 。
.. toctree::
:maxdepth: 1
syscall
@@ -0,0 +1,29 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../../disclaimer-zh_CN.rst
:Original: Documentation/userspace-api/ebpf/syscall.rst
:翻译:
李睿 Rui Li <me@lirui.org>
eBPF Syscall
------------
:作者:
- Alexei Starovoitov <ast@kernel.org>
- Joe Stringer <joe@wand.net.nz>
- Michael Kerrisk <mtk.manpages@gmail.com>
bpf syscall的主要信息可以在 `man-pages`_ 中的 `bpf(2)`_ 找到。
bpf() 子命令参考
~~~~~~~~~~~~~~~~
子命令在以下内核代码中:
include/uapi/linux/bpf.h
.. Links:
.. _man-pages: https://www.kernel.org/doc/man-pages/
.. _bpf(2): https://man7.org/linux/man-pages/man2/bpf.2.html
@@ -0,0 +1,80 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst
:Original: Documentation/userspace-api/futex2.rst
:翻译:
李睿 Rui Li <me@lirui.org>
======
futex2
======
:作者: André Almeida <andrealmeid@collabora.com>
futex,或者称为快速用户互斥锁(fast user mutex),是一组允许用户空间创建高性能同步
机制的系统调用,比如用户空间中的互斥锁,信号量和条件变量。C标准库,如glibc,使用它作
为实现更多高级接口的方式,如pthreads。
futex2是初代futex系统调用的后续版本,旨在克服原有接口的限制。
用户API
=======
``futex_waitv()``
-----------------
等待一个futex数组,可由其中任意一个唤醒::
futex_waitv(struct futex_waitv *waiters, unsigned int nr_futexes,
unsigned int flags, struct timespec *timeout, clockid_t clockid)
struct futex_waitv {
__u64 val;
__u64 uaddr;
__u32 flags;
__u32 __reserved;
};
用户空间设置一个struct futex_waitv数组(最多128项),设置 ``uaddr`` 为等待的
地址, ``val`` 为期望值, ``flags`` 为指定的类型(如private)和futex的大小。
``__reserved`` 需要置为0,但是它可用作未来扩展。指向数组第一个元素的指针作为
``waiters`` 传递。如果 ``waiters`` 或任何的 ``uaddr`` 地址无效,将返回 ``-EFAULT`` 。
如果用户空间拥有32位的指针,那么需要做显式转换来保证高位清零。 ``uintptr_t`` 设计
得很精巧,在32/64位的指针上都正常工作。
``nr_futexes`` 指定了数组的大小。不在[1,128]区间内的值会使系统调用返回 ``-EINVAL`` 。
系统调用的 ``flags`` 参数需要置0,但可用作未来扩展。
对于每个 ``waiters`` 数组中的项,在 ``uaddr`` 的当前值会和 ``val`` 比较。如果
不一致,系统调用会撤销截至目前完成的所有工作,并返回 ``-EAGAIN`` 。如果所有测试
和验证都通过,系统调用会等待直到以下情况之一发生:
- 指定的timeout超时,返回 ``-ETIMEOUT`` 。
- 一个信号被传递给睡眠中的任务,返回 ``-ERESTARTSYS`` 。
- 某个列表中的futex被唤醒,返回那个被唤醒的futex的索引。
关于如何使用接口的例子可以在 ``tools/testing/selftests/futex/functional/futex_waitv.c``
中找到。
超时
----
``struct timespec *timeout`` 是一个指向绝对超时时间的可选参数。你需要在 ``clockid``
参数中指定要使用的时钟类型。支持 ``CLOCK_MONOTONIC`` 和 ``CLOCK_REALTIME`` 。这个
系统调用只接受64位的timespec结构体。
futex的类型
-----------
futex既可以是私有的也可以是共享的。私有用于多个进程共享同样的内存空间,并且futex的虚拟
地址对所有进程都是一样的。这允许在内核中进行优化。要使用私有futex,需要在futex标志中指定
``FUTEX_PRIVATE_FLAG`` 。对于那些不在同一内存空间共享的进程,可以让同一个futex拥有不同
的虚拟地址(例如使用基于文件的共享内存),这需要不同的内部机制来使得正确进入队列。这是默认
的行为,而且对私有futex和共享futex都适用。
futex可以是不同的大小:8,16,32或64位。目前只支持32位大小的futex,并且需要通过 ``FUTEX_32``
标志指定。
@@ -0,0 +1,47 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst
:Original: Documentation/userspace-api/index.rst
:翻译:
李睿 Rui Li <me@lirui.org>
=========================
Linux 内核用户空间API指南
=========================
.. _man-pages: https://www.kernel.org/doc/man-pages/
尽管许多用户空间API的文档被记录在别处(特别是在 man-pages_ 项目中),
在代码树中仍然可以找到有关用户空间的部分信息。这个手册意在成为这些信息
聚集的地方。
.. toctree::
:caption: 目录
:maxdepth: 2
no_new_privs
seccomp_filter
accelerators/ocxl
ebpf/index
sysfs-platform_profile
futex2
TODOList:
* landlock
* unshare
* spec_ctrl
* ioctl/index
* iommu
* media/index
* netlink/index
* vduse
.. only:: subproject and html
Indices
=======
* :ref:`genindex`
@@ -0,0 +1,57 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst
:Original: Documentation/userspace-api/no_new_privs.rst
:翻译:
李睿 Rui Li <me@lirui.org>
============
无新权限标志
============
execve系统调用可以给一个新启动的程序授予它的父程序本没有的权限。最明显的两个
例子就是setuid/setgid控制程序和文件的能力。为了避免父程序也获得这些权限,内
核和用户代码必须小心避免任何父程序可以颠覆子程序的情况。比如:
- 程序在setuid后,动态装载器处理 ``LD_*`` 环境变量的不同方式。
- 对于非特权程序,chroot是不允许的,因为这会允许 ``/etc/passwd`` 在继承
chroot的程序眼中被替换。
- 执行代码对ptrace有特殊处理。
这些都是临时性的修复。 ``no_new_privs`` 位(从 Linux 3.5 起)是一个新的通
用的机制来保证一个进程安全地修改其执行环境并跨execve持久化。任何任务都可以设
置 ``no_new_privs`` 。一旦该位被设置,它会在fork、clone和execve中继承下去
,并且不能被撤销。在 ``no_new_privs`` 被设置的情况下, ``execve()`` 将保证
不会授予权限去做任何没有execve调用就不能做的事情。比如, setuid 和 setgid
位不会再改变 uid 或 gid;文件能力不会被添加到授权集合中,并且Linux安全模块(
LSM)不会在execve调用后放松限制。
设置 ``no_new_privs`` 使用::
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
不过要小心,Linux安全模块(LSM)也可能不会在 ``no_new_privs`` 模式下收紧约束。
(这意味着一个一般的服务启动器在执行守护进程前就去设置 ``no_new_privs`` 可能
会干扰基于LSM的沙箱。)
请注意, ``no_new_privs`` 并不能阻止不涉及 ``execve()`` 的权限变化。一个拥有
适当权限的任务仍然可以调用 ``setuid(2)`` 并接收 SCM_RIGHTS 数据报。
目前来说, ``no_new_privs`` 有两大使用场景:
- 为seccomp模式2沙箱安装的过滤器会跨execve持久化,并能够改变新执行程序的行为。
非特权用户因此在 ``no_new_privs`` 被设置的情况下只允许安装这样的过滤器。
- ``no_new_privs`` 本身就能被用于减少非特权用户的攻击面。如果所有以某个 uid
运行的程序都设置了 ``no_new_privs`` ,那么那个 uid 将无法通过攻击 setuid,
setgid 和使用文件能力的二进制来提权;它需要先攻击一些没有被设置 ``no_new_privs``
位的东西。
将来,其他潜在的危险的内核特性可能被非特权任务利用,即使 ``no_new_privs`` 被置位。
原则上,当 ``no_new_privs`` 被置位时, ``unshare(2)`` 和 ``clone(2)`` 的几个选
项将是安全的,并且 ``no_new_privs`` 加上 ``chroot`` 是可以被认为比 chroot本身危
险性小得多的。
@@ -0,0 +1,293 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst
:Original: Documentation/userspace-api/seccomp_filter.rst
:翻译:
李睿 Rui Li <me@lirui.org>
==================================
Seccomp BPF (基于过滤器的安全计算)
==================================
*Seccomp: SECure COMPuting*
介绍
====
大量系统调用被暴露给每个用户空间进程,但其中又有许多系统调用在进程的整个生命
周期中都没被使用。随着系统调用的改变和成熟,缺陷被找到并消除。允许某一部分应
用程序仅能访问一部分系统调用是有好处的,这会缩小内核暴露给应用程序的面积。
系统调用过滤器就是为这些应用程序而生的。
Seccomp过滤提供了一种为进程指定一个处理系统调用的过滤器的方法。这个过滤器体
现为一个伯克利包过滤器(BPF)程序,就像套接字过滤器一样,不同在于前者处理的
数据和正在进行的系统调用有关:系统调用号和系统调用参数。这样使用一种长期与
用户空间和直接数据打交道的语言来表达系统调用过滤成为了可能。
此外,BPF让seccomp用户不再成为在系统调用干预框架(system call interposition
frameworks)中常见的检查-使用竞态攻击(TOCTOU)的受害者。BPF程序可能无法解引
用指针,这就限制了所有过滤器仅能直接评估系统调用参数。
这不是什么
==========
系统调用过滤并不是一个沙箱。它提供了一种明确定义的机制来最小化内核暴露面。它
旨在成为一个沙箱开发者使用的工具。除此之外,逻辑行为和信息流的策略应该结合其他
系统加固手段或者可能由你选择的内核安全模块(LSM)来管理。易于表达的动态过滤器
为这条路提供了更多选择(比如避免病态的大小或者选择允许 socketcall() 中的多路
系统调用),但将其理解为更完整的沙箱解决方案是错误的。
用法
====
添加了一个额外的seccomp模式,它可以使用和严格seccomp相同的 prctl(2) 调用来启用。
如果架构有 ``CONFIG_HAVE_ARCH_SECCOMP_FILTER`` 标志,那么可以添加以下过滤器:
``PR_SET_SECCOMP``:
现在需要添加一个额外的参数来指定使用BPF程序的新过滤器。
BPF程序将在反应系统调用号、参数和其他元数据的seccomp_data结构体之上执行。
BPF程序必须返回允许的值之一来告知内核应该采取什么行动。
用法::
prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, prog);
'prog' 参数是一个指向 sock_fprog 结构体的指针,其中包含了过滤器程序。如
果程序是无效的,该调用会返回 -1 并设置 errno 为 ``EINVAL`` 。
如果 ``fork`` / ``clone`` 和 ``execve`` 被 @prog 所允许,任何子进程都将
受到和父进程相同的过滤器和系统调用ABI的约束。
在调用之前,进程必须调用 ``prctl(PR_SET_NO_NEW_PRIVS, 1)`` 或者在它的
命名空间内以 ``CAP_SYS_ADMIN`` 权限运行。如果以上条件不满足,会返回
``-EACCES`` 。这一要求保证了过滤器程序不能用于比安装过滤器的进程拥有更高
权限的子进程。
另外,如果 ``prctl(2)`` 被安装的过滤器所允许,就可以叠加额外的过滤器。这会增
加评估时间,但是可以进一步降低执行进程时的攻击面。
以上调用在成功时返回0,失败时返回一个非零的值。
返回值
======
一个seccomp过滤器可能返回下列任意值。如果多个过滤器存在,评估一个指定系统调用的
返回值总会使用最高优先级的值。(比如 ``SECCOMP_RET_KILL_PROCESS`` 总是被优先
返回。)
按照优先级排序,它们是:
``SECCOMP_RET_KILL_PROCESS``:
使得整个进程立即结束而不执行系统调用。进程的退出状态 (``status & 0x7f``) 将
是 ``SIGSYS`` ,而不是 ``SIGKILL`` 。
``SECCOMP_RET_KILL_THREAD``:
使得线程立即结束而不执行系统调用。线程的退出状态 (``status & 0x7f``) 将是
是 ``SIGSYS`` ,而不是 ``SIGKILL`` 。
``SECCOMP_RET_TRAP``:
使得内核向触发进程发送一个 ``SIGSYS`` 信号而不执行系统调用。
``siginfo->si_call_addr`` 会展示系统调用指令的位置, ``siginfo->si_syscall``
和 ``siginfo->si_arch`` 会指出试图进行的系统调用。程序计数器会和发生了系统
调用的一样(即它不会指向系统调用指令)。返回值寄存器会包含一个与架构相关的值——
如果恢复执行,需要将其设为合理的值。(架构依赖性是因为将其替换为 ``-ENOSYS``
会导致一些有用的信息被覆盖。)
返回值的 ``SECCOMP_RET_DATA`` 部分会作为 ``si_errno`` 传递。
由seccomp触发的 ``SIGSYS`` 会有一个 ``SYS_SECCOMP`` 的 si_code 。
``SECCOMP_RET_ERRNO``:
使得返回值的低16位作为errno传递给用户空间,而不执行系统调用。
``SECCOMP_RET_USER_NOTIF``:
使得一个 ``struct seccomp_notif`` 消息被发送到已附加的用户空间通知文件描述
符。如果没有被附加则返回 ``-ENOSYS`` 。下面会讨论如何处理用户通知。
``SECCOMP_RET_TRACE``:
当返回的时候,这个值会使得内核在执行系统调用前尝试去通知一个基于 ``ptrace()``
的追踪器。如果没有追踪器, ``-ENOSYS`` 会返回给用户空间,并且系统调用不会执行。
如果追踪器通过 ``ptrace(PTRACE_SETOPTIONS)`` 请求了 ``PTRACE_O_TRACESECCOMP``,
那么它会收到 ``PTRACE_EVENT_SECCOMP`` 通知。BPF程序返回值的 ``SECCOMP_RET_DATA``
部分会通过 ``PTRACE_GETEVENTMSG`` 提供给追踪器。
追踪器可以通过改变系统调用号到-1来跳过系统调用。或者追踪器可以改变系统调用号到
一个有效值来改变请求的系统调用。如果追踪器请求跳过系统调用,那么系统调用将返回
追踪器放在返回值寄存器中的值。
在追踪器被通知后,seccomp检查不会再次运行。(这意味着基于seccomp的沙箱必须禁止
ptrace的使用,甚至其他沙箱进程也不行,除非非常小心;ptrace可以通过这个机制来逃
逸。)
``SECCOMP_RET_LOG``:
使得系统调用在被记录之后再运行。这应该被应用开发者用来检查他们的程序需要哪些
系统调用,而不需要反复通过多个测试和开发周期来建立清单。
只有在 actions_logged sysctl 字符串中出现 "log" 时,这个操作才会被记录。
``SECCOMP_RET_ALLOW``:
使得系统调用被执行。
如果多个追踪器存在,评估系统调用的返回值将永远使用最高优先级的值。
优先级只通过 ``SECCOMP_RET_ACTION`` 掩码来决定。当多个过滤器返回相同优先级的返回
值时,只有来自最近安装的过滤器的 ``SECCOMP_RET_DATA`` 会被返回。
隐患
====
最需要避免的隐患是在过滤系统调用号时却不检查架构值。因为在任何支持多个系统调用
约定的架构上,系统调用号可能根据具体调用而不同。如果不同调用约定中的调用号有重叠,
那么过滤器的检查可能被滥用。请总是检查架构值!
例子
====
``samples/seccomp/`` 文件夹包含了x86专用和更通用的使用高层宏接口来生成BPF程序的
例子。
用户空间通知
============
``SECCOMP_RET_USER_NOTIF`` 返回值会让seccomp过滤器传递一个特定的系统调用给用户
空间处理。这可能会对像容器管理器的程序有用,它们希望拦截特定的系统调用(如 ``mount()``,
``finit_module()`` 等等)并改变其行为。
传递 ``SECCOMP_FILTER_FLAG_NEW_LISTENER`` 参数给 ``seccomp()`` 系统调用可以取
得通知文件描述符:
.. code-block:: c
fd = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, &prog);
成功情况下会返回一个对过滤器监听的文件描述符,然后可以通过 ``SCM_RIGHTS`` 或类似
的方式传递。需要注意的是,过滤器文件描述符针对的是一个特定的过滤器而不是特定的进程。
所以如果这个进程后来fork了,来自两个进程的通知都会出现在同一个过滤器文件描述符中。
对于过滤器文件描述符的读写也是同步的,所以一个过滤器文件描述符可以安全地拥有多个读者。
seccomp通知文件描述符由两个结构体组成:
.. code-block:: c
struct seccomp_notif_sizes {
__u16 seccomp_notif;
__u16 seccomp_notif_resp;
__u16 seccomp_data;
};
struct seccomp_notif {
__u64 id;
__u32 pid;
__u32 flags;
struct seccomp_data data;
};
struct seccomp_notif_resp {
__u64 id;
__s64 val;
__s32 error;
__u32 flags;
};
``struct seccomp_notif_sizes`` 结构体可以用于确定seccomp通知中各种结构体的大小。
``struct seccomp_data`` 的大小可能未来会改变,所以需要使用下面的代码:
.. code-block:: c
struct seccomp_notif_sizes sizes;
seccomp(SECCOMP_GET_NOTIF_SIZES, 0, &sizes);
来决定需要分配的多种结构体的大小。请查看 samples/seccomp/user-trap.c 中的例子。
用户可以通过 ``ioctl(SECCOMP_IOCTL_NOTIF_RECV)`` (或 ``poll()``) 读取seccomp
通知文件描述符来接收一个 ``struct seccomp_notif`` ,其中包含五个成员:结构体的
输入长度,每个过滤器唯一的 ``id`` , 触发请求进程的 ``pid`` (如果进程的pid命名空
间对于监听者的pid命名空间不可见的话,可能为0)。通知还包含传递给seccomp的 ``data``
和一个过滤器标志。在调用ioctl前结构体应该被清空。
之后用户空间可以根据这些信息决定做什么,并通过 ``ioctl(SECCOMP_IOCTL_NOTIF_SEND)``
发送一个响应,表示应该给用户空间返回什么。 ``struct seccomp_notif_resp`` 的 ``id``
成员应该和 ``struct seccomp_notif`` 的 ``id`` 一致。
用户空间也可以通过 ``ioctl(SECCOMP_IOCTL_NOTIF_ADDFD)`` 向通知进程添加文件描述
符。 ``struct seccomp_notif_addfd`` 的 ``id`` 成员应该和 ``struct seccomp_notif``
的 ``id`` 保持一致。 ``newfd_flags`` 标志可以被用于在通知进程的文件描述符上设置
O_CLOEXEC 等标志。如果监督者(supervisor)向文件描述符注入一个特定的数字,可以使用
``SECCOMP_ADDFD_FLAG_SETFD`` 标志,并设置 ``newfd`` 成员为要使用的特定数字。
如果文件描述符已经在通知进程中打开,那它将被替换。监督者也可以添加一个文件描述符,
并使用 ``SECCOMP_ADDFD_FLAG_SEND`` 标志原子响应,返回值将是注入的文件描述符编号。
通知进程可以被抢占,导致通知被终止。这可能在尝试代表通知进程执行长时间且通常可重试
(如挂载文件系统)的操作时导致问题。另外,在安装过滤器的时候,
``SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV`` 可以被设置。这个标志使得当监督者收到用
户通知时,通知进程会忽略非致命信号,直到响应被发送。在用户空间收到通知之前发出的信号
将被正常处理。
值得注意的是, ``struct seccomp_data`` 包含了系统调用寄存器参数的值,但是不包含指向
内存的指针。进程的内存可以通过 ``ptrace()`` 或 ``/proc/pid/mem`` 由合适的特权跟踪
访问。但是,需要注意避免之前提到的TOCTOU攻击:所有从被跟踪者内存中读到的参数都应该先
读到追踪器的内存中,再做出策略决定。这样就可以对系统调用的参数做原子决定。
Sysctls
=======
Seccomp的sysctl文件可以在 ``/proc/sys/kernel/seccomp/`` 文件夹中找到。这里有对文件
夹中每个文件的描述:
``actions_avail``:
以字符串形式保存seccomp返回值(参考上文的 ``SECCOMP_RET_*`` 宏)的只读有序
列表。从左往右按照最少许可返回值到最多许可返回值排序。
这个列表代表了内核支持的seccomp返回值集合。一个用户空间程序可以使用这个列表来在
程序建立时确定在 ``seccomp.h`` 中找到的动作是否和当前运行内核实际支持的动作有所
不同。
``actions_logged``:
允许被记录的seccomp返回值(参考上文的 ``SECCOMP_RET_*`` 宏)的可读写有序列表。
对文件写入不需要是有序的,但从文件读取将采用与actions_avail sysctl一致的方式排序。
``allow`` 字符串在 ``actions_logged`` sysctl中不被接收,因为不可能记录
``SECCOMP_RET_ALLOW`` 动作。尝试向sysctl写入 ``allow`` 会导致返回一个EINVAL。
添加架构支持
============
请查看 ``arch/Kconfig`` 了解权威要求。总的来说如果一个架构同时支持ptrace_event和
seccomp,那么它将可以通过较小的修改支持seccomp过滤器: ``SIGSYS`` 支持和seccomp
返回值检查。然后必须将 ``CONFIG_HAVE_ARCH_SECCOMP_FILTER`` 添加到它的架构特定
的Kconfig中。
注意事项
========
vDSO可能导致一些系统调用完全在用户空间中运行,当你在不同机器上跑程序时可能导致回退
到真正系统调用的意外发生。为了在x86上最小化这些意外的发生,请确保你在测试时把
``/sys/devices/system/clocksource/clocksource0/current_clocksource`` 设置为
``acpi_pm`` 之类的值。
在x86-64上,vsyscall模拟默认开启。(vsyscalls是vDSO调用的传统变体。)目前,模拟
vsyscalls会遵守seccomp,但是有一些奇怪情况:
- ``SECCOMP_RET_TRAP`` 的返回值会设置一个指向给定vsyscall入口的 ``si_call_addr``,
而不是'syscall'指令之后的地址。任何想重新开始调用的代码都需要注意 (a) ret指令
已被模拟,(b) 试图恢复系统调用将再次触发标准vsyscall模拟安全检查,使得恢复系统
调用在大部分情况下没有意义。
- ``SECCOMP_RET_TRACE`` 的返回值将像往常一样给追踪器发出信号,但是系统调用可能不能
使用orig_rax寄存器改变为另一个系统调用。可能只能改变为-1来跳过当前模拟的调用。
任何其他改变都可能终止进程。追踪器看到的rip值将是系统调用的入口地址;这和正常行为
不同。追踪器禁止修改rip或者rsp。(不要依赖其他改变来终止进程,它们可能正常工作。
比如在一些内核中,选择一个只存在于未来内核中的系统调用将被正确模拟,返回一个
``-ENOSYS`` 。)
要检测这个古怪的行为,可以检查 ``addr & ~0x0C00 == 0xFFFFFFFFFF600000``。(对于
``SECCOMP_RET_TRACE`` ,使用rip。对于 ``SECCOMP_RET_TRAP`` ,使用
``siginfo->si_call_addr`` 。)不要检测其他条件:未来内核可能会改进vsyscall模拟,
当前内核在vsyscall=native模式下会有不同表现,但在这些情况下, ``0xF...F600{0,4,8,C}00``
处的指令将不是系统调用。
请注意,现代系统根本不可能使用vsyscalls —— 它们是一种遗留功能,而且比标准系统调用
慢得多。 新的代码将使用vDSO,而vDSO发出的系统调用与正常的系统调用没有区别。
@@ -0,0 +1,40 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst
:Original: Documentation/userspace-api/sysfs-platform_profile.rst
:翻译:
李睿 Rui Li <me@lirui.org>
==========================================================
平台配置文件选择(如 /sys/firmware/acpi/platform_profile)
==========================================================
现代系统中平台性能、温度、风扇和其他硬件相关的特性通常是可以动态配置的。平台
配置通常会根据当前的状态由一些自动机制(很可能存在于内核之外)来自动调整。
这些平台自动调整机制通常能够被配置成多个平台配置文件中的一个,要么偏向节能运
行,要么偏向性能运行。
platform_profile属性的目的是提供一个通用的sysfs API来选择这些平台自动配置
机制的配置文件。
需要注意的是,这个API只能用作选择平台配置文件,用来监测所产生的性能特征并不
是其目标。监测性能最好使用设备/供应商提供的工具,比如turbostat。
具体来说,当选择高性能配置文件时,真实能达到的性能可能受制于多种因素,比如:
其他组件的发热,房间温度,笔记本底部的自由空气流动等。让用户空间知道任何阻碍
达到要求性能水平的局部最优条件,显然不是这个API的目标。
由于数字本身并不能代表一个配置文件会调整的多个变量(功耗,发热等),这个API
使用字符串来描述多种配置文件。为了保证用户空间能够获得一致的体验,
sysfs-platform_profile ABI 文档定义了一个固定的配置文件名集合。驱动程序
*必须* 将它们内置的配置文件表示映射到这个固定的集合中。
如果映射时没有很好的匹配,可以添加一个新的配置文件名称。驱动希望引入的新配置
文件名称时必须:
1. 解释为什么无法使用已有的配置文件名称。
2. 添加一个新的配置文件名称,以及预期行为的清晰描述,保存到
sysfs-platform_profile ABI文档中。