用户空间应用程序编程接口兼容性检查器:在Linux内核中检测用户空间破坏情况的自动化工具
发表于 2024-02-21 16:48:07

如何在Linux内核中保持与用户空间应用程序的向后兼容性?大多数维护人员均依赖于代码审核以及为测试目的而推出的更改。但是,由于缺乏经验丰富的审核人员和测试场,在构建时添加自动化工具可能有助于对兼容性进行的检查。

在高通创新中心(QUIC),我们开发出一种shell脚本程序,可以对各种补丁执行应用程序二进制接口(ABI)分析。我们在内部使用这种shell脚本程序,以便在构建时检测我们对Linux内核所进行的特定更改是否会破坏与用户空间应用程序编程接口(UAPI)的兼容性。我们已经将该工具上传到Linux内核社区,而该Linux内核社区也帮助我们对该shell脚本程序进行了改进。

在本篇博文中,本人会描述该脚本程序的工作方式,并提供若干示例,而您可以按照这些示例在自身的Linux内核开发过程中自动检测用户空间的破坏情况。您还可以发现关于用户空间和内核空间之间某些更困难边界的理念,比如模块参数和sysfs文件系统。

(本篇博文是本人在Linux管道开发人员大会上所做报告“利用自动化工具改进用户空间应用程序编程接口兼容性审核”的摘要。详情见本文末尾处。)

什么是用户空间应用程序编程接口?

很长一段时间以来,Linux内核社区在其用户空间接口中严格执行向后兼容策略。因此,用户已经能够对其内核进行升级,同时不需要担心其用户空间程序出现崩溃情况。用户在整个内核升级过程中可以确保稳定性,并且不需要重新编译任何内容。

但是,作为确保这种稳定性的代价,在可能破坏用户空间应用程序编程接口的更改对用户造成影响之前,内核开发人员需要对任何该类更改进行拦截。用户空间应用程序编程接口包括用户空间和内核之间的任何接口,例如系统调用、数据结构(在输入/输出控制中使用)、模块参数、sysfs文件和procfs文件。简而言之,如果某一接口可以在内核中更改并破坏与在用户空间所运行程序的兼容性,则应当将其视为用户空间应用程序编程接口。

传统上,内核开发人员有两种检测用户空间破坏情况的方法。其中一种方法是代码审核,即维护人员应当对各个补丁进行检查,以查看该补丁是否会破坏用户空间中的任何内容。另一种方法是将合并的补丁发布到整个Linux测试领域;如果任何用户空间程序崩溃,则向上游报告补丁,并在必要时还原该补丁。

这种情况导致搜索活动具有非常高的劳动密集性,因此自动化的时机已经成熟。我们开始添加工具和文档,以帮助内核开发人员更容易地找到潜在的用户空间破坏区域。

补丁是否会破坏与用户空间应用程序编程接口头文件的兼容性?

我们已经上传了check-uapi.sh脚本程序,该脚本程序可以检查在整个git提交命令范围内用户空间应用程序编程接口头文件的稳定性。该脚本程序的默认行为是检查与HEAD^1相比,最近的提交命令(或当前的脏更改)是否引入了任何应用系统二进制接口更改。

在高通创新中心,我们在自有的持续集成(CI)系统中使用该脚本程序来阻止未通过检查的Linux内核更改。如将该脚本程序作为本公司构建管道的组成部分将其添加到内核树中,则可以在执行代码之前自动分析导致用户空间应用程序编程接口破坏的补丁。该脚本程序为我们提供了有用、即时的反馈,我们可以利用其编写Linux内核的用户空间应用程序编程接口稳定策略代码。

该脚本程序的逻辑如下:

1. 在补丁之前和之后分别运行make headers_install。

2. 填充两个并行头文件树。

3. 在该类头文件树的所有用户空间应用程序编程接口头文件上运行abidiff (一种libabigail命令行工具)。

该脚本程序比较了在补丁应用之前和之后所修改头文件的应用系统二进制接口。如果以一种非向后兼容的方式修改了现有的用户空间应用程序编程接口,则脚本程序退出非零并生成一条信息。

例如,在该结构体foo中,我们在x和y之间添加了成员z:

https://developer.qualcomm.com/sites/default/files/attachments/uapi-001.png

脚本程序报告,y的偏移量发生了更改,而结构体的规模也发生了更改:

https://developer.qualcomm.com/sites/default/files/attachments/uapi-002.png

该类更改破坏了用户空间应用程序编程接口,因为该类更改并不向后兼容。

我们发现,在检查内核代码时,abidiff可能会过于热心,有时候会拒绝在内核代码中非常常见、并且不会损害向后兼容性的模式。为了确保abidiff能够抑制该类结果,我们使用正则表达式匹配来传送某些模式和条件(在此处,是指以COLOR_MAX结尾的分子变体):

https://developer.qualcomm.com/sites/default/files/attachments/uapi-003.png

下文中提供了使用check-uapi.sh脚本程序检测Linux内核开发中潜在用户空间应用程序编程接口破坏的七个示例。

示例1 – 添加一个简单的#define

此处,我们在头文件中添加#define FOO函数:

 

此项更改不会破坏任何内容,因此脚本程序会检查通过make headers_install安装的所有912个用户空间应用程序编程接口头文件,并生成一条信息:

 另请注意,您可以在没有任何自变量的情况下运行脚本程序。如果您拥有脏的git系统树,则该脚本程序会比较HEAD和脏git系统树。如果你拥有清洁的git系统树,则该脚本程序会比较HEAD和HEAD-1。

示例2 – 更改类型

以下为将某一结构体的最后一个成员从有符号的32位整数更改为无符号的32位整数的示例:

结构体的规模不会改变,因此您的输入/输出控制代码不会受到影响。但是,如果出现不兼容的情况,例如用户空间程序传入一个负数,则脚本程序会报告该不兼容情况:

另一方面,如果您知道用户空间无法传递负数,那么您可以将其忽略。您可以传递-i参数,以便脚本程序忽略不明确的更改。

另请注意,该脚本程序为您提供了有关diff的统计数据(“1/912用户空间应用程序编程接口头文件…”)。

示例3 – 对某一成员重新排序

如要将目标寄存器移动到源寄存器下方,只需要简单地移动结构体的某一成员:

即使采用了-i标记,脚本程序也会发现破坏性更改,因为使用其中一种寄存器的任何用户空间程序均将失效:

https://developer.qualcomm.com/sites/default/files/attachments/uapi-009.png

示例4 – 针对具体架构的头文件

假设您对某一arm64头文件进行了更改,例如在结构体的末尾处添加了一个新变量:

https://developer.qualcomm.com/sites/default/files/attachments/uapi-010.png

在x86机器上运行时,脚本程序不会报告用户空间应用程序编程接口头文件的更改情况:

https://developer.qualcomm.com/sites/default/files/attachments/uapi-011.png

这是因为当您运行make headers_install程序时,并行树并不包括arm64头文件。但是,如果您传入一个交叉编译器并设置拱形变量,则会正确安装make headers_install程序,并检查该等头文件:

 

请注意,arm64有884个头文件,而x86则有912个头文件。

您也可以在此处传送-i标记,而脚本程序不会将其报告为破坏性更改。为什么不会报告?因为在结构体的末尾添加一个新变量确实会改变结构体的规模,但是在技术方面则可以在内核空间中处理这种扩展。您可以正确地映射输入/输出控制,以转至相同的命令。而且,如果您采用copy_struct_from_user()和_IOC_SIZE宏,则内核会自动进行零扩展,或截断您的结构体,以处理规模差异情况。

因此,如果内核驱动程序可以看到这一新变量并知道其是否设置为零 – 这意味着用户空间没有这一新变量的概念 – 这样操作就没有问题。在这种情况下,我们将其视为不明确的破坏性更改。

示例5 – 交叉依赖性

如果将types.h中的poll_t更改为无符号短整型,会发生什么情况?

https://developer.qualcomm.com/sites/default/files/attachments/uapi-013.png

该脚本程序可以检测出eventpoll.h中的差异:

 

 这一点值得注意,因为eventpoll.h不是您所修改的头文件。但是,该代码强调了为什么我们希望脚本程序检查树中的每一个头文件:以捕捉头文件之间的交叉依赖性。

该脚本程序还确定eventpoll.h代码在两个版本之间没有发生更改。但是,该代码可以指出受到破坏的内容,并表明其中包含的某一个头文件可能发生了更改。

示例6 – 用户空间应用程序编程接口头文件删除

如果您将term.ios从Kbuild中注释掉:

则make headers_install会将其忽略,而脚本程序则会报告:

https://developer.qualcomm.com/sites/default/files/attachments/uapi-016.png

您已经将用户空间应用程序编程接口头文件删除,因此脚本程序无法确定您是否破坏了用户空间,并为此项更改添加标记。

示例7 – 检查大量历史记录

您可以传入参数-b和-p,以利用该脚本程序检查大量历史记录。下面显示了如何比较标签6.1和6.0:

在这种情况下,脚本程序为这两种标签之间37个头文件中出现的用户空间应用程序编程接口破坏情况添加标记:

https://developer.qualcomm.com/sites/default/files/attachments/uapi-018.png

补丁是否会破坏与模块参数的兼容性?

除了检查用户空间应用程序编程接口头文件,我们还在同一个补丁集中上传了check-module-params.sh。这是一个脚本程序,可以帮助确定补丁是否会破坏与模块参数的兼容性。该脚本程序的逻辑是对所有module_param.*()调用进行文本搜索,然后在应用更改之前和之后比较其自变量。与check-uapi.sh一样,该脚本程序可以检查与HEAD^1相比,最近的提交(或当前的脏更改)是否引入了任何应用系统二进制接口更改。

在该示例中,某一自变量更改了:

https://developer.qualcomm.com/sites/default/files/attachments/uapi-019.png

 该脚本程序为其添加标记:

如要发现对模块参数所进行的更改,设置一个自动化处理流程非常有用 – 例如,没有经验的开发人员在无意中修改了某些内容。

有关脚本程序的社区输入非常有用。现在,为我们带来更多益处!

在上传至Linux内核时,并没有任何快捷方式。您必须确保内核维护人员相信您的代码不会破坏向后兼容性,并且构建强大的自动化流程检测破坏情况非常重要。Linux内核社区提供了大量有用、详细的输入,从而在很大程度上改进了该工具。

以下是我们希望进行讨论的其他领域:

为了确保稳定的进行内核升级,对sysfs和procfs文件的更改也必须向后兼容。与用户空间应用程序编程接口头文件类似,该类文件属于用户空间和内核空间之间的边界,有很多方法可以通过更改文件的方式破坏用户空间。但是,由于该类文件没有提供干净的信应用程序接口,因此abidiff无法帮助对其进行分析。

请注意,利用脚本程序/get_abi.pl程序,某些自动化流程已经就位。许多sysfs和procfs接口均处于文档/应用系统二进制接口中,而get_abi.pl程序可以解析文档。该程序还可以在文档目录中查找正在运行的sysfs文件节点,并为任何未记录的节点提供标记。

我们有一些初步想法:

  • 扩展文档接口(例如:类似于设备树绑定的接口)。
  • 如果文档成为sysfs和procfs事实上的应用程序接口,则应编写自动化工具,以生成测试桩。
  • 对文件进行静态分析。

我们对其他想法持开放态度。

边缘情况

如涉及到向后兼容性,则存在许多边缘情况(或极端情况)。如要查看哪些情况可行,哪些情况不可行,则要利用大量的开发历史记录,特别是在Linux内核上工作时。本公司的check-uapi.sh脚本程序可以全面查看内核历史记录,并根据时间查找到破坏情况。有了这些数据,我们可以分析某些破坏模式,并改进本公司脚本程序中的检测算法。

您可以使用该工具,并在内核历史记录和新补丁进入邮件列表时检查其结果,从而帮助我们发现更多的边缘情况。请告知您所发现的内容

作者意图

本公司的check- api.sh脚本程序并不了解作者的意图,也没有作者的意图做出任何假设。对于该脚本程序而言,破坏就是破坏,并且需要标记出是故意破坏还是偶然破坏。但是,变量是内核本身在某些情况下需要重构的内容 – 例如,通过删除未使用的接口。该类操作完全有效的,但属于该脚本程序并不了解的意图组成部分,因此该脚本程序可能会对其进行标记,即使其并不影响用户空间。针对这一问题,如果文件被移动,则该脚本程序将其视为删除。

Linux内核社区的目标之一是将Linux设置在其尚未存在的地方;例如,在A级层面上,即高度危险的情况下。但是,这需要开发人员和架构师编写Linux内核所没有的设计和架构文档。该文档通常会充分涵盖开发人员/作者的意图。

我们听说,对于在安全至关重要的环境中工作的社区成员而言,使用与内核更改相关的元信息来表达作者的意图非常重要。如果能够在不增加维护人员负担的情况下实现这一目标,我们会提供大力支持。

轮到你了:试试上传的脚本程序

目前我们已经上传了脚本程序,欢迎您在自己的环境中使用脚本程序。如果您能点击链接并与社区其他成员一起参与补丁内容,我们将不胜感激。

我们的目标是与其他希望将其集成到构建管道和流程中的Linux内核开发人员共享此脚本程序的实施。为了结束几个月来为上传该脚本程序所进行的工作,我们在Linux管道开发人员大会上提出了“使用自动化工具改进用户空间应用程序编程接口兼容性审核”。请浏览幻灯片和视频,以便在用户接触之前了解有关用户空间应用程序编程接口破坏检测的更多详情。

骁龙与高通品牌产品均属于高通科技公司和/或其子公司产品。

相关博客:

有关Vulkan的高阶滤波和区块匹配新图像处理扩展提高了性能并降低了功耗

Gunyah管理程序软件 – 支持在安卓虚拟化框架下所保护的虚拟机

利用Bazel、Kleaf和设备驱动程序开发包构建外部安卓内核模块 – 你准备好了吗?

小型转储:为进行崩溃后分析选择随机存取存储器区域的新工具

回顾:2023年纽约droidcon活动

相关标签:

在所发布内容中表达的观点仅为原作者的个人观点,并不代表高通公司或其子公司(以下简称为“高通公司”)的观点。所提供的内容仅供参考之用,而并不意味着高通公司或任何其他方的赞同或表述。本网站同样可以提供非高通公司网站和资源的链接或参考。高通公司对于可能通过本网站引用、访问、或链接的任何非高通公司网站或第三方资源并没有做出任何类型的任何声明、保证、或其他承诺。 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
微博关注
【免责声明:CSDN本栏目发布信息,目的在于传播更多信息,丰富网络文化,稿件仅代表作者个人观点,与CSDN无关。其原创性以及文中陈述文字和文字内容未经本网证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本网不做任何保证或者承诺,请读者仅作参考,并请自行核实相关内容。您若对该稿件有任何怀疑或质疑,请立即与CSDN联系,我们将迅速给您回应并做处理。】