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

Handling argc==0 in the kernel

By Jonathan Corbet
January 28, 2022
DeepL assisted translation
https://lwn.net/Articles/882799/

现在大多数读者可能已经了解了被称为 CVE-2021-4034 的 Polkit 漏洞。Polkit 的 fix 方法相对简单,并且正在网络上广泛推行。不过,这个问题的根源来自对于程序在 Unix 类似系统上运行方式有误解。这个问题极有可能也存在于其他程序中,所以最好能找到一个更普遍的解决方案。要解决这个问题,最好是能在内核中完成,但看起来很难在不引起 regression 的情况下来绕过这种误解。

I'd like to have an argument, please

大多数开发者都熟悉 C 语言表达中的 main 函数的原型(prototype):

int main(int argc, char *argv[], char *envp[]);

在程序被调用时,命令行参数会位于 argv 中,环境变量在 envp 中。两者都是指向 char * 字符串类型的数组的指针,数组以 NULL 结尾。argv 中不为空的项目的数量放在 argc 中。不过这个 API 是用户空间看到的情况。当内核开始执行一个程序时情况并不是这样。在 Linux 上,这个程序本身也作为指针传递给 argv 数组。envp 数组会紧跟着 argv 里面的 NULL 值来开始。因此,C 程序中在进入 main()时下面的语句结果会是 true:

envp == argv + argc + 1

按惯例,argv[0] 就是正在执行的程序的名称,许多程序都依赖这个惯例。然而,这个约定只是一个约定而已,并不是一个保证。argv 的实际内容完全由调用 execve() 来运行程序的人控制,而且调用者并不是必须要把程序名放在 argv[0] 位置的。

事实上,调用者根本不需要提供 argv[0]。如果传递给 execve()的 argv 数组是空的(或者 argv 指针是 NULL),那么新创建出来的进程代码中 argv 数组中的第一个指针将是 NULL,而 envp 数组会紧随其后。不幸的是,Polkit(或者更精确地说,是 setuid pkexec 工具)"认为" argv[0] 总是存在的,所以它通过从 argv[1]开始遍历 argv 数组来处理其他参数。如果完全没有提供参数的话,argv[1] 就是 envp 了,所以 pkexec 实际上是在遍历所有的环境变量。还会对这里的参数进行修改(pkexec 会对它的 argv 数组重新写入内容),pkexec 就可以被用来去改写其环境变量,从而绕过了针对 setuid 程序进行的对这些变量的检查工作。然后一切都完了。

这个问题并不新鲜,人们也早已意识到这类问题。Ryan Mallon 在 2013 年写了一篇关于这个问题的文章,指出 "它确实可以让 setuid 二进制文件产生一些特别行为"。他曾经还提交了一个 Polkit patch 来解决这个问题,但这个 patch 从未被合入。甚至来到更早的 2007 年的时候,Michael Kerrisk 就报了一个 bug,认为内核的行为是个错误,但该报告几乎没有经过什么讨论就被关闭了。因此这个问题一直存在到现在,最终导致管理员们现在忙着修补这个漏洞。

Toward a more general fix

修复这个问题本身很是简单,只要让 pkexec 检查 argc 来确保至少有一个参数就好。但肯定还有其他程序也会有着类似的假设。鉴于 argv[0] 含义已经是大家的共识了,那么允许程序运行时的 argv 数组为空,这种做法是否有意义。也许根本不合理,但当前的 API 有长久的历史,无法不经深思熟虑就去修改。

Ariadne Conill 用一个 patch 来开启了 linux-kernel 中的讨论,该 patch 直接禁止 argv 中完全没有内容的情况下调用 execve()。违规的调用者会得到 EFAULT 错误返回值。这就会确保 argv 不为空,从而解决问题,但这种做法又有着自己的问题。其中之一就是 Kees Cook 所发现的,实际上有相当多的代码在调用 execve()时的 argv 数组都是为空。Conill 认为这些属于 "偷懒写出来的 test case 代码,应该被 fix",但是哪怕这些代码无法正常运行了也算是 regression。另外,正如 Heikki Kallasjoki 和 Rich Felker 都指出的,POSIX 标准本身实际上允许 argv 数组为空。

Felker 还提出了一个会引入更少的 regression 的替代方案:只在特权切换的位置确保 argv 非空。换句话说,也就是当 execve() 调用是要运行一个 setuid 程序的时候。Cook 认为,只要有可能他都希望尽量避免把特权切换考虑进来。他提出了另一个解决方案:在全空的 argv 数组的末尾额外插入一个空指针,这样那些试图直接跳过 argv[0]的代码就会看到这里的空指针。不过事实证明这个解决方案也是行不通的:ABI 要求 envp 需要紧接着 argv 开始,而额外加入的这个 NULL 破坏了这一承诺。显然是有一些程序就依赖于这种排列方式的,如果有变化的话它们肯定无法正常执行。

还有一种方法,首先是由 Eric Biederman 提出的,就是用包含一个指向空字符串的单一指针的 argv 代替空的 argv。这个建议得到了一些支持(尽管到现在为止还没有人进行具体实现),但也引起了一些相关的担忧。可能会有一些程序会对使用 null 字符串的参数报错,或者可能会在 argc 为零时做一些特别的动作。改变 execve() 运行的程序所传递的参数数量看起来可能还是会造成一些意外的。

Cook 最终这样进行了总结:

鉴于我们已经发现一些依赖于 NULL argv 的代码,我认为我们很可能无法直接进行修改了,所以我们陷入了这个奇怪的两难境地,一方面试图拒绝我们可以拒绝的东西,另一方面又要为目前存在的问题想办法给出解决。

尽管如此,他还是继续声明他更加偏好最初的改动(就是直接禁止没有参数的 execve()调用,不过换成 EINVAL 返回值而不用 EFAULT),并建议对一个 Valgrind test 进行 fix,这个 test 是已知会因为该限制而被破坏的。Conill 提供了新的一版 patch,这次它在对调用 execve() 时因为 argv 为空而返回 EINVAL 之前会给出 warning。Cook 接受了这个 patch,说 "咱们来试试看会有什么问题";Biederman 表示赞同,说:"尤其是既然你已经表态愿意 fix 那些 tests 了"。

这就是截至目前的讨论情况,但还不确定这个问题最终会如何解决。最重要的是,这个 patch 还没有与 Linus Torvalds 商讨过,Linus 可能会因其会引入的潜在的 regression 风险而表示反对。毕竟这是一个 ABI 的变动,很可能会有一些代码被这个变动所破坏。事实上 BSD 系统已经禁止 argv 为空了,幸运的话,它已经帮助解决了大部分的担忧。如果不幸真的出现了 regression,那几乎肯定还需要再找另一个不同的解决方案了。

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

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

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

LWN:内核该如何处理argc为0的情况?相关推荐

  1. 2017云栖大会门票转让_「揭秘GP」云栖大会 | Greenplum 6.0 内核优化解读和7.0展望...

    9月25日,云栖大会在杭州阿里巴巴云栖小镇正式拉开序幕,三天会议期间,共吸引了200多位世界级科学家.400多家科技合作伙伴参与,科技展区面积超过3万平方米,共发布了1000多项顶尖技术. 云栖大会现 ...

  2. linux 返回非法指令,linux – ARM Cortex A7在内核模式下返回PMCCNTR = 0,在用户模式下返回非法指令(即使在PMUSERENR = 1之后)...

    我想在Raspberry Pi 2上读取循环计数寄存器(PMCCNTR),它有一个ARM Cortex A7内核.我为它编译了一个内核模块,如下所示: #include #include int in ...

  3. kmem 反编译linux内核_24小时学通Linux内核之如何处理输入输出操作

    Linux内核是如何将软硬件结合起来的呢?这里我们将一起探究内核与周围硬件主要是文件IO和硬件设备之间的关系,来解释这个问题.处理器与周围设备的通信依赖于一系列的电路电线,总线就是具有类似功能的电线, ...

  4. php电视直播cms系统_在线网络电视直播内核完整PHP版 v3.0

    程序又名外谍网在线网络电视高清直播内核简易PHP版 v2.3, 该程序的播放源采集自PPTV.乐视网.风云直播.华数直播等四个国内拥有电视直播权的稳定大站,可在线无刷新切换. 程序未包含任何广告,播放 ...

  5. LWN: 内核staging目录的驱动们最后下场如何?

    点击上方蓝色"Linux News搬运工"关注我们~ What happens to kernel staging-tree code By Jonathan Corbet sta ...

  6. android 6.0 内核版本,ZUI 1.6版发布 内核居然是Android 6.0.1

    原标题:ZUI 1.6版发布 内核居然是Android 6.0.1 随着春季多款机型的发布,去年的手机界新兵ZUK也闲不住了.ZUK的第一款机型ZUI Z1自发布以来就以后辈的身份锐意进取,在国内手机 ...

  7. 物联网操作系统Zephyr(内核篇)之2.0 内核服务之线程(1)

    Zephyr物联网操作系统专栏汇总 目录 1.生命周期 1.1 线程创建 1.2.线程的正常结束 1.3.线程的异常终止 1.4.线程挂起 2. 线程状态 3.线程堆栈对象 4. 仅内核堆栈 5.线程 ...

  8. 中断描述符表IDT以及Linux内核IDT表的初始化的基本情况

    IDT,Interrupt Descriptor Table,中断描述符表是CPU用来处理中断和程序异常的. 一 中断和IDT表概要 中断可以由硬件产生(称为外部中断),也可以由软件产生(称为内部中断 ...

  9. java 断网处理_如何处理浏览器的断网情况?

    好的断网处理会让人很舒适:lol的断线重连,王者荣耀的断线重连 可以确保游戏的继续进行 坏的断网处理甚至不处理会出bug:比如我手上的项目就出了个bug 业务人员表示非常苦恼 网络问题一直是一个很值得 ...

最新文章

  1. python range函数与numpy arange函数,xrange与range的区别
  2. Spring注解详解
  3. RecyclerView Adapter 所使用的数据list发生变化需要注意的事情
  4. python turtle库画图案-Python:turtle库的使用及图形绘制
  5. dom选择方法的区别
  6. 【jQuery学习】—jQuery对象的访问
  7. OpenCV-Laplacian边缘检测
  8. 你绝没看如此详细的PDF去水印教程
  9. CPU性能测试分析MIPS、DMIPS
  10. CTP接口python实现跨品种套利策略源码
  11. 15个漂亮的企业网站设计案例欣赏
  12. 前端基础-04-盒子模型
  13. MM、RMM、Bi_MM
  14. QQ浏览器app应用专区推荐系统
  15. 使用JLINK下载程序可以,但是调试不行
  16. Go语言下载安装教程|Goland配置教程|2021|Windows
  17. 不定积分知识结构图_大一上学期《高等数学》知识整理-第四章 不定积分
  18. Gartner发布商业模式创新框架
  19. Spark的安装(Standalone模式,高可用模式,基于Yarn模式)
  20. Qt编写输入法源码V2019,未采用Qt系统层输入法框架,独创输入切换机制

热门文章

  1. “山东味”的蘑菇在日本“生根发芽” 成全国最大食用菌菌棒出口基地!
  2. 西安文理学院计算机系冯丽,张岗亭(计算机科学系)老师 - 西安文理学院 - 院校大全...
  3. 视频画质不清晰的解决方法
  4. 感知世界触景生情——增强现实技术
  5. Markdown格式操作基础和快捷键
  6. 由QQ文件中转站超快速上传联想到
  7. 软件项目经理的职责和技能有哪些
  8. KubeSphere and Friends|12 月 14 日相约北京,不见不散
  9. js php 实现日历签到_js实现每日签到功能
  10. NoSQL数据库笔谈(1)