前言
对于部分架构,出于设备较不常见或者设备性能较低等原因,很多时候开发者并不会选择在该架构下进行打包与分发,而是选择使用QEMU等模拟器进行跨架构构建。
QEMU主要有两种模式,一种是全系统模拟,另一种是用户模式模拟。全系统模拟是指QEMU模拟整个系统,包括CPU、内存、外设等,而用户模式模拟则是只模拟用户空间,不模拟内核空间。笔者在之前的测试中,发现对龙架构使用QEMU全系统模拟时,-smp
只能指定为1-4,无法充分利用多核性能;在最近的版本中-smp
似乎已经可以指定为更多核心,但是据社团的其他同学称,即使指定了更多的核心数,实际上多核性能利用也并不充分。
因此,笔者决定尝试使用QEMU用户模式模拟,以充分利用多核性能。
binfmt_misc
带来的权限问题
在笔者的测试中,如果在用户模式下使用QEMU,在容器中从root切换到普通用户再提权时,会出现如下错误:
sudo: effective uid is not 0, is /usr/sbin/sudo on a file system with the 'nosuid' option set or an NFS file system without root privileges?
这一报错极具迷惑性,实际上提权失败的原因与文件系统、挂载参数等完全无关。笔者后续也去排查了systemd-nspawn
、sudo
等工具,并没有解决问题。后来,笔者阅读了Arch Linux维护者Felix Yan的Arch Ports RFC,发现了问题的所在:
Requires unsafe “C” flag for binfmt to correctly invoke suid binaries for our toolchain to work.
这一问题的根源在于binfmt_misc
的flags参数。binfmt_misc
是Linux内核的一个功能,可以在内核中注册一个二进制格式解释器,当内核遇到一个二进制文件时,会调用这个解释器来解释这个文件。
对于龙架构的QEMU用户模式模拟,binfmt_misc
的默认注册内容为:
~> cat /usr/lib/binfmt.d/qemu-loongarch64-static.conf
:qemu-loongarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02\x01:\xff\xff\xff\xff\xff\xff\xff\xfc\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-loongarch64-static:FP
根据binfmt_misc
的文档,注册的二进制类型的基本格式为:
:name:type:offset:magic:mask:interpreter:flags
其中,flags
参数可以指定为:
P
- 保留原始argv[0]
- 默认情况下,
binfmt_misc
会用二进制文件的完整路径覆盖原始的argv[0]
(命令名称)。 - 使用此标志后,
binfmt_misc
会在参数列表中添加一个额外的参数来保存原始的argv[0]
。- 例如,如果将解释器设置为
/bin/foo
,并且运行blah
(位于/usr/bin
),则内核会用以下参数执行/bin/foo
:["/bin/foo", "/usr/bin/blah", "blah"]
- 解释器需要理解这种格式,并用以下参数执行
/usr/bin/blah
:["blah"]
- 例如,如果将解释器设置为
- 默认情况下,
O
- 打开可执行文件- 默认情况下,
binfmt_misc
会将二进制文件的完整路径作为参数传递给解释器。 - 使用此标志后,
binfmt_misc
会打开该文件并传递文件描述符作为参数,而不是完整路径。 - 这允许解释器执行不可读的二进制文件,但需谨慎使用,因为解释器可能泄露不可读文件的內容。
- 默认情况下,
C
- 凭据- 当前,
binfmt_misc
根据解释器计算新进程的凭据和安全令牌。 - 使用此标志后,这些属性会根据二进制文件本身来计算。
- 此标志隐含了
O
标志。 - 谨慎使用,因为使用
binfmt_misc
运行由root
用户拥有的suid
二进制文件时,解释器将以root
权限运行。
- 当前,
F
- 固定二进制文件binfmt_misc
的默认行为是在调用misc
格式文件时延迟启动二进制文件。- 然而,这种方式在挂载命名空间和
chroot
环境下可能无法正常工作。
- 然而,这种方式在挂载命名空间和
F
模式会在安装模拟器时立即打开二进制文件,并使用打开的镜像启动模拟器。- 这意味着只要安装了模拟器,无论环境如何变化,它都始终可用。
因此,为了解决sudo
提权失败的问题,需要在binfmt_misc
的注册中添加C
标志。即,编辑/usr/lib/binfmt.d/qemu-loongarch64-static.conf
,在末尾添加C
:
conf
:qemu-loongarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02\x01:\xff\xff\xff\xff\xff\xff\xff\xfc\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-loongarch64-static:FPC
`
需要注意的是,C
标志有一定的安全风险,需要确保环境安全。