当前位置:首页 期刊杂志

计算机Linux 操作系统调用测试的自动化设计

时间:2024-07-28

李 菲

(沈阳市公用事业技工学校 辽宁 沈阳 110011)

0 引言

在计算机中,操作系统作为基础组成部分,是保障计算机系统正常、稳定运行的关键。 随着计算机生态体系的不断完善,各类操作系统崭露头角,Linux 操作系统凭借自身开源免费、软件环境良好、服务器发展势头强等优势,在嵌入式系统和个人电脑等领域具有广泛的应用[1]。 但与之相伴的是在操作系统的快速发展以及用户需求的不断增多下,Linux 操作系统内核的更新速度更快,内核复杂性也在持续提升,随着内核接受代码量的增加,Linux 内核遭受攻击的范围相继提升,导致计算机系统面对的安全隐患加剧,此时就需要对操作系统进行自主可控测试。 而系统调用作为操作系统内核测试的关键切入点,在发展进程中规模越来越大,无论是数量、种类还是复杂程度均发生了较大的改变,致使操作系统调用测试工作的难度增大,且易出现遗漏情况。 由此,基于Linux 操作系统内核,设计一种面向系统调用测试的自动化程序框架,以此实现对系统调用的全面、高效测试,为国内操作系统开发工作的进一步发展提供支持。

1 Linux 操作系统调用机制

1.1 传统系统调用机制

针对Linux 操作系统而言,传统系统调用机制原理如下:借助软中断将用户态的代码请求转移至内核态之中,当前执行代码段所处位置可用代码段寄存器(code segment,CS)中最低两位予以表述。 在某个系统调用执行时,需将调用号、参数和程序的上下文环境存储至寄存器之中,再进行软中断[2]。 之后由中央处理器从中断描述符表中提取匹配门描述符,并对门描述符的结构化查询语言(data processing language,DPL)和调用者的计算机程序库(computer program library,CPL)进行检查,当DPL 大于CPL 时,调用者才可以访问此代码段。 此时,int 指令会更改寄存器中的CPL 为0,从而进入Linux 系统内核。 门描述符是system_call()函数,入口地址储存在中断描述符之中,在Linux 初始化时预先设置好此函数,当执行软中断指令时,则会自动执行system_call()。 同时,system_call()作为全部系统调用的公用入口,能够根据调用号对相应的内核函数予以处理。 最后,执行iret 指令即可切回用户态。

1.2 快速系统调用机制

自Linux 内核2.6 之后,出现快速系统调用机制。 因为在执行系统调用时调用者的CPL 必然是3,对应的门描述符DPL 也为3。 由此,采用SYSENTER/SYSCALL 指令将处在3 的用户程序可以直接调用处在0 的内核程序,并利用SYSENTER/SYSRET 指令将处在0 的内核程序切回到用户程序。 此过程中,因为无需进行CPL 和DPL 检查,系统调用指令的执行速度明显增快。 以Intel 的快速系统调用指令为例,其实现主要涉及如下三个寄存器:

(1)SYSENTER_CS_MSR,负责存储待执行的内核态代码段,还能得到内核堆栈段基址和用户态代码段与堆栈段基址;

(2)SYSENTER_EIP_MSR 负责存储下一次将要执行指令的偏移地址;

(3)SYSENTER_ESP_MSR:负责存储内核态代码堆栈的栈顶指针。

对于上述寄存器内容的写入采用WRMSR 指令,而读取则可采用RDMSR 指令。 当用户程序调用SYSENTER指令时,即可在CS 寄存器中存储内核态代码段描述符,将值增加8 后于SS 寄存器中进行存储,将待执行的指令地址存入企业信息门户寄存器,并将栈顶指针存入扩展栈指针寄存器,从而实现3 向0 的切换,对相应的内核程序予以执行。

2 Linux 操作系统调用测试自动化程序设计与实现

2.1 系统调用分类

针对Linux5.6.14 内核版本而言,系统调用表是Linux内核的大数据结构,负责对所有系统调用内核函数指针进行记录,借助此表即可明确哪个系统调用得到实现。 在x86 架构下,使用syscall_64. c 文件实现初始化系统调用表[3]。 通过对系统调用表的分析,能够明确Linux5.6.14内核版本在x86 架构下实现的系统调用共有342 个,依照功能划分,共分为以下6 类:

(1)文件操作:共具有127 个文件操作类系统调用,涉及文件的创建、读写、删除、复制、重命名、属性设置等功能。

(2)系统管理:共具有70 个系统管理类系统调用,涉及系统日期、时间的设置和获取,系统数据的设置和查看等功能。

(3)进程控制:共具有53 个进程控制类系统调用,涉及进程创建、执行、加载、终止以及进程属性设置等功能。

(4)通信:共具有48 个通信类系统调用,涉及通信连接的创建、删除,状态信息传递,消息接发等功能。

(5)内存管理:共具有26 个内存管理类系统调用,涉及内存页面的加解锁、映射以及内存缓冲区数据写回等功能。

(6)用户管理:共具有18 个用户管理类系统调用,涉及用户组的设置、修改以及用户权限等功能。

2.2 系统调用测试自动化程序设计及实现

现阶段系统调用测试方法以黑盒测试为主,测试过程缺少对内核函数的分析,忽略了测试程序的内部实现,导致系统调用测试易出现用例冗余或覆盖范围不全的情况。因此,在上述分析的基础上提出一种基于灰色测试的系统调用测试自动化程序。

2.2.1 用例设计和优化

系统调用由用户程序发起,以“文件打开”系统调用为例。 用户程序共传入4 个参数,在第3 个参数中文件打开标志位存有14 个选项,各选项还可以与其他标志项进行多种组合,倘若未进行内核源码分析就对参数进行盲目性的组合,一方面可能出现只能对错误代码进行调用的情况,导致核心功能代码未执行,从而造成用例冗余,增加系统调用测试难度,另一方面部分系统调用的内核函数完全相同,例如fstate 与state 等。 针对此类系统调用测试而言,可以选择传递参数最全的系统调用进行测试,尽可能减少测试数量。

在系统调用测试程序的执行过程中,具体的执行路径取决于if 条件判断,由此在用例优化过程中,可从if 条件判断的优化入手。 考虑部分if 条件判断的标准为不可控因素,所以可以将此类if 条件判断忽略,例如__d_lookup_rcu()函数是对缓存中某个目录项进行查找,并非用户程序可控,故此可以忽略。 倘若if 条件判断与用户传入参数相关,在测试过程中无论真假分支均需进行测试。

此外,通过对系统调用内核源码的静态分析,能够发现内核函数在系统调用的实现过程中还与内核安全自主访问控制机制的权限检测有关。 在测试前,需要提前赋予用户针对文件拥有的访问控制列表(access control list,ACL)权限或进程具备的能力。 在此过程中,若只采用黑盒测试对系统调用功能的实现进行验证,难免全面涉及所有与权限检查相关的代码,导致执行测试的过程中内核代码的覆盖率较低[4]。

2.2.2 测试程序自动化设计和实现

基于上述对系统调用分类和内核函数的分析,对系统调用测试程序自动化的设计流程如图1 所示。

图1 系统调用测试自动化流程图

在系统调用测试自动化流程中,测试环境配置、测试用例执行、测试结果记录与分析均实现自动化,显著提升系统调用测试的效率。

在自动化设计过程中,将自动化测试程序的目录进行四层结构设计,如图2 所示。

图2 系统调用测试自动化程序目录结构

第一层目录结构是系统调用的六大分类。

第二层目录结构包括部分以系统调用名称进行命名的子目录,一个Makefile 文件以及auto. sh 脚本,Makefile文件可递归至第三层目录结构中的Makefile 文件之中,从而实现对同一系统调用中全部测试程序的共同编译。 而auto.sh 脚本则可以对已经完成编译的测试程序进行执行。

第三层目录结构中Makefile 文件负责对某系统调用测试程序进行编译,因为在一两层目录结构中也对Makefile 文件及对应的脚本进行设计,所以测试程序自动化粒度既要求能够对同类系统调用进行统一测试,也能够针对单独的一个系统调用进行测试。 result 中存储的是测试分析结果,采用系统调用名称+测试时间的方式对存储文件进行命名。 TEST 中存储的是执行测试时创建的目录与文件。 inelud 存储的是测试程序中具有的头文件,其中包括存放测试用例和结果的结构体。 通过对测试用例数量TOTALLINE 宏的初始化,当出现用例数量变化时,仅需对宏进行修改或调整即可。 main. c 则是主要测试程序,通过对可执行文件的运行实现对系统调用的测试,负责初始化数据结构体和全局变量、逐条获取测试用例参数、执行系统调用测试、记录结果、编写日志等工作[5]。 setup.sh脚本负责测试前的环境配置和检查工作,例如权限检查,用户及用户组创建并赋予相应权限等。 testcases 存储的是为单个系统调用设计的测试用例,测试程序可将从此文件中提取出的用例存入test_cases 结构体中。

当完成系统调用测试之后,即可对设置的测试环境进行清除,考虑不同系统调用测试需要的测试环境也有所差异,所以应依照具体测试情况对测试环境清除程序进行设计。 大体上,测试环境清除的类别划分如下:

(1)对测试前预先创建的目录或文件进行清除;

(2)对测试前预先创建的用户及用户组以及赋予的权限进行清除;

(3)对测试过程中创建的目录和文件进行清除;

(4)对执行测试时打开的文件予以关闭。

3 面向SylixOS 的测试实施和结果分析

SylixOS 是一款类Linux 的国产计算机操作系统,将上述设计的测试用例移植到SylixOS 上,既是对系统调用测试程序自动化设计成果的检验,也是对Linux 系统调用测试用例在国产操作系统中的适用性展开探究。

3.1 测试用例执行

在面向SylixOS 的测试过程中,需采用RealEvo-IDE工具对测试环境进行部署,并新建Base 工程。 实验采用兆芯,编译工具链为x86-sylixos-toolchain,编译代码级别是Debug。 当完成Base 工程创建后,编译SylixOS 内核,完成编译后即可将其上传至目标板上并启动。 之后对测试工程进行建设,须注意在Base 工程中对创建好的x86 Base文件夹进行选择,并对测试工程予以编译,编译完成的工程上传至SylixOS 系统目标文件之中。 最后将编译好的测试程序在目标板上进行执行。

3.2 测试结果分析

系统调用测试实验结果显示,在75 个测试用例中存在8 个用例的执行结果和预期不符,如表1 所示,其余测试结果均与预期结果和Linux 测试结果保持一致。

表1 测试结果对照表

以其中问题级别为“严重”的15、22、23、25 为例,主要测试的是O_TRUNC 标志同写权限之间的关系。 O_TRUNC 是当文件存在清空文件内容,在打开时需赋予用户相应的写权限,文件自身也需要提供权限。 但在SylixOS 测试中并未检查对写权限的赋予,与操作系统的安全性问题有关,所以标注问题级别为“严重”。

同时,将本文提出的系统调用测试程序与Linux 测试计划(Linux test project,LTP)程序进行对比,发现本文提出的系统调用测试自动化程序更为灵活,既可支持单一或全部系统调用的检测,也支持某一类的系统调用检测。 同时,由于本文提出的测试自动化程序建立在灰盒测试方法基础上,在测试用例设计上基于系统调用内核源码分析,且尽可能全面覆盖代码执行路径,在用例设计方面具有更强的完备性,冗余情况得到有效减少。 以openat 系统调用为例,LTP 测试仅对其基本功能进行了验证,测试用例仅在3 个测试程序中有所分布;而本文提出的测试自动化程序,针对openat 系统调用共设计了75 个用例,除基本功能测试外还包含面向ACL 机制的权限测试、内核机制的权限测试等,在测试用例的完备性方面明显更强,可选性更为灵活。

4 结语

综上所述,本文基于对Linux 操作系统调用测试机制的介绍以及系统调用类型划分,对Linux 操作系统调用测试自动化程序展开设计,总结规律优化系统调用测试用例。 并将测试用例移植到SylixOS 操作系统之中进行检验,分析测试结果,在验证系统调用程序自动化设计有效的同时,对更适合国产计算机操作系统的自动化系统调用测试方法进行探究。

免责声明

我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自各大过期杂志,内容仅供学习参考,不准确地方联系删除处理!