LWN: trusted_for() 没能被合并!

本文回顾了trusted_for()系统调用从提议到合并困难的过程,讨论了其功能、命名争议和不同方案的比较,焦点在于Linus Torvalds的反对和可能的替代方案。

关注了就能看到更多这么棒的文章哦~

trusted_for() bounces off the merge window

By Jake Edge
April 12, 2022
DeepL assisted translation
https://lwn.net/Articles/890957/

在我们上一次关注 trusted_for() 系统调用的提案时,看起来还是很快就能合入 mainline 了。这个功能是用来允许用户空间解释器(user-space interpreters)和其他一些工具来询问内核是否 "信任" 一个文件可以执行的。当时还是 2020 年 10 月。在那之后,这个 patch 已经更新了多次,进入了 linux-next,而且 Mickaël Salaün 为 5.18 合并窗口提出了一个 pull request。但是,看起来故事还没有结束,因为 Linus Torvalds 拒绝合入 trusted_for(),部分原因是他不喜欢这个名字,当然也有其他原因。虽然他并不反对它所提供的功能,但他也强烈认为不应该用一个新的系统调用来解决。

Background

该 patch 自 2018 年首次推出以来,已经经历了 18 版。它一开始是 openat2() 系统调用的一个新标志(O_MAYEXEC)。想法是相当直接的:内核在文件可以执行之前是要对其实施一些安全检查的,但各种工具可以直接读取文件就来执行文件。因此这些文件并没有受到同样的安全检查,因为内核根本不知道它们里面包含的是要执行的代码。于是 Salaün 就希望找到一种方法,能将同样的检查应用于那些被打开之后实际上是用来执行的文件。

显然,用户空间需要参与进来,因为内核不可能知道被打开的文件是否会被用来执行,毕竟绝大多数的文件打开后都不是用来执行的。Python 以及其他工具都很希望能支持那些包含有代码的文件进行安全检查(例如 PEP 578),但显然会有很多工具都需要能告知内核它们的意图,有些工具可能会反对这样做,或者没有什么兴趣。在一些 locked-down 系统中,希望所有工具都需要进行这种检查来符合规范,这个功能就很有价值了。

在这个过程中,虚拟文件系统(VFS)层的维护者 Al Viro 抱怨说,openat2() 并不是进行这种检查的最合适的位置。他建议用一个新的系统调用来代替。下一版本的 patch 就把 faccessat2() 的系统调用改为 AT_INTERPRETED flag 了,但 Viro 认为这并没有什么改进,并再次建议采用新的系统调用。

经过一轮关于名字的争论之后,Salaün 决定采用 trusted_for()。随后的修改主要是一些表面改动了,或者为最新内核来更新代码。目前它看起来和我们一年半前的文章中的形式几乎一样:

int trusted_for(const int fd, const int usage, const unsigned int flags);

这个调用将检查 fd 所代表的文件,看它是否允许使用(这里 TRUSTED_FOR_EXECUTION 是目前唯一定义的选项)flags 尚未使用。如果文件被信任,那么就返回 0,如果不被信任就返回 EACCESS。然而,默认情况下,trusted_for() 实际上不做任何事情,但有一个新的 fs.trusted_for_policy sysctl 开关可以设置,让它检查用 noexec 挂载的文件系统上的文件、没有执行权限的文件,或两种情况共存。

No merge

在 5.18 合并窗口关闭而没有合入 trusted_for()之后,Salaün 和 Kees Cook 都问到了这个状态。事实证明,Torvalds 并不希望看到一个新的、非标准的系统调用,"除了一个随意的'未来会用的 flag',这就是一个没有任何语义的完全随意的接口"。Salaün 不同意语义不明确的说法;"我认为语义定义得很好:'这个新的系统调用使用户空间能够询问内核:这个文件描述符的内容是否被信任用于此目的?"

Torvalds 也有一些其他的抱怨:

系统调用 想要做到的 功能基本上可以用 access()(和 faccessat())的一个新标志来解决。一个非常类似于 X_OK 的标志。

[……]这是不可能被合并的,不管是谁想出了那个恶心的 "trusted_for()" (信任什么?谁来信任?为什么?) 都应该照照镜子。

如果你给 access()添加一个新的 X_OK 变体,也许就能成功。

access()(和 faccessat2())的 X_OK 标志用于确定进程是否有权限执行一个特定文件,它使用的是真实的用户 ID 和组 ID(而不是 effective ID,这些在使用了 set-user-ID 的程序里可能是不同的值)。对于 faccessat2(),AT_EACCESS flag 可以用来检查 effective ID。不过正如 Salaün 所指出的,Torvalds 的建议类似于 Salaün 早先为 faccessat2()所做的 AT_INTERPRETED,他愿意回到这种机制,并想知道 Torvalds 是否更喜欢这种方法。

Torvalds 看了先前的补丁,他说这是一个更合理的方法,尽管他有一些具体的问题和建议。他想知道为什么不能使用一个新的 mode bit,也许叫 EXECVE_OK,而不是新增一个 AT_INTERPRETED flag。这样,它就可以用在缺乏 flag 参数的 access()和 faccessat2() 了。考虑到所要检查的内容,其实这种做法更有效果。目前为这些系统调用定义的 mode bit 检查的是读、写或可执行访问权限。

Salaün 同意,使用 mode bit 是一个更好的选择。Torvalds 在补丁中指出的其他一些怪异之处,是由于这是当初的早期版本,在 Viro 的反对下,很快就放弃了这条路。Salaün 计划修改 patch 并重新提交,尽管人们猜测 Viro 仍然会有同样的反对意见,所以这一次能走多远并不清楚。此外,如果增加进一步的检查,如 Linux 安全模块(LSM)访问限制或文件完整性验证,可能会通过 fs.trusted_for_policy(换一个新的名字)上的额外 bit 来完成,但这需要 access()/faccessat2() 增加代码来实际进行这些检查。

Bikeshed history

Ted Ts'o 建议,将该功能的演变历史加入到 changelog 中:

建议一下,对于像这个一样被严重 bike-sheded 概念来说,可以写一个 "立法历史",或者 "bike shed history",这可能会有帮助。

不仅仅是一个邮件列表讨论的 link 链接,而是一个简短的总结,例如,为什么我们从 open flag O_MAYEXEC 转向使用 faccessat(2)。我查找了一下,但在深入邮件历史中之后,我还是没能找到理由。[…]

可能当所有这些都被摆出来的时候,我们可以重新审视之前的 design 决策,因为 "那个用来支持这种特例的 bike-shed request 是不合理的",或者 "哦,好的,这就是为什么我们需要像这样完全通用的解决方案"。

其中一些信息包含在实际添加系统调用的 patch 中,尽管它主要只是列出了每个版本的变化,而没有提供很多 Ts'o 正在寻找的那种解释。LWN 这篇文章和之前的两篇文章可能也有助于填补其中的一些缺失。

总的来说,这是一个相当简单的功能,可以在专门的环境中提供一些有用的功能。但是,它实际上会放在哪里,一直是相当难以解决的问题。考虑到 Torvalds 的偏好,从而回到把它放在 access()和 faccessat2()中的计划,看起来应该会有一个合理的未来,但我们必须看看第 19 版(及以后)的 patch set 是如何处理的。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

4e52fbe1e752b234744b6af456d97488.png

int battery_type_check(int *battery_type) { int value = 0; int ret = 0; int ret_value = 0; int battery_id = 0; int battery_type_check = 0; if (batt_id == NULL) { bm_debug("[battery_type_check]: batt_id iio channel is null []\n"); *battery_type = BAT_TYPE__ATL_4400mV; battery_id = 0; return battery_id; } ret = iio_read_channel_processed(batt_id, &ret_value); if (ret < 0) bm_debug( "[battery_type_check] read channel err = %d,\n", ret); bm_debug( "[battery_type_check]: ret = %d,ret_value[%d]\n", ret, ret_value); value = ret_value; if(is_fuelgauge_apply() == true) { switch(battery_id) case 0: if (value >= BAT_TYPE_COS_4450mV_ADC_MIN && value <= BAT_TYPE_COS_4450mV_ADC_MAX) { *battery_type = BAT_TYPE__COS_4450mV; battery_id = BAT_COS_BATT_ID; break; case 1: if (value >= BAT_TYPE_ATL_4450mV_ADC_MIN && value <= BAT_TYPE_ATL_4450mV_ADC_MAX) { *battery_type = BAT_TYPE__ATL_4450mV; battery_id = BAT_ATL_BATT_ID; break; case 2: if (value >= BAT_TYPE_LWN_4450mV_ADC_MIN && value <= BAT_TYPE_LWN_4450mV_ADC_MAX) { *battery_type = BAT_TYPE__LWN_4450mV; battery_id = BAT_LWN_BATT_ID; break; case 3: *battery_type = BAT_TYPE__UNKNOWN; battery_id = BAT_ENC_BATT_ID; break; default:【兼容原来的方案】 if (value >= BAT_TYPE_ATL_4450mV_ADC_MIN && value <= BAT_TYPE_ATL_4450mV_ADC_MAX) { *battery_type = BAT_TYPE__ATL_4450mV; battery_id = BAT_ATL_BATT_ID; } else if (value >= BAT_TYPE_LWN_4450mV_ADC_MIN && value <= BAT_TYPE_LWN_4450mV_ADC_MAX) { *battery_type = BAT_TYPE__LWN_4450mV; battery_id = BAT_LWN_BATT_ID; } else if (value >= BAT_TYPE_COS_4450mV_ADC_MIN && value <= BAT_TYPE_COS_4450mV_ADC_MAX) { *battery_type = BAT_TYPE__COS_4450mV; battery_id = BAT_COS_BATT_ID; } else { *battery_type = BAT_TYPE__UNKNOWN; battery_id = BAT_ENC_BATT_ID; } } else { *battery_type = BAT_TYPE__UNKNOWN; battery_id = BAT_ENC_BATT_ID; } printk(KERN_ERR "[battery_type_check]: adc_value[%d], battery_type[%d], g_fg_battery_id[%d]\n", value, *battery_type, battery_id); return battery_id; }
08-21
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值