一种基于对齐空洞的内核数据类型动态扩展热补方法

文档序号:31195798发布日期:2022-08-20 00:33阅读:54来源:国知局
一种基于对齐空洞的内核数据类型动态扩展热补方法

1.本发明涉及操作系统内核的动态更新技术领域,具体而言涉及一种基于对齐空洞的内核数据类型扩展热补方法。


背景技术:

2.操作系统内核需要通过补丁来修复漏洞、优化性能和添加新功能。传统的补丁更新方式需要重启设备更换内核,这导致系统服务的中断,造成诸多不便甚至巨大损失。操作系统内核的动态更新、热补技术可以在避免机器停机、重启场景下完成补丁任务。
3.内核补丁更新任务包括对内核函数的替换以及数据类型的改变,利用内核在函数头预留的插桩点,内核可以完成在更新点对函数执行流的改变。然而在涉及数据类型变化时,目前主流的热补系统仍然缺少一种高效的解决方法来解决类型扩展上的空间问题。这是由于现有技术需要管理对象类型扩展的外部空间,而维护扩展带来的依赖关联关系造成扩展成员的访问和管理上额外的运行开销。


技术实现要素:

4.本发明针对内核补丁中常见的对数据类型的扩展需求场景,通过分析、寻找热补对象布局中的空洞来解决成员扩展的空间问题,并根据扩展模式来修改相应的内核函数完成将补丁转化为热补模块的工作,提供一种低开销的内核数据类型动态扩展热补方法。通过利用空洞与热补数据对象之间的相关性,采取类型强制转换的方式实现更新后数据类型的使用降低运行开销。利用空洞与数据对象的相关性,设计了利用强制类型转换来更新数据视图,保证了对扩充成员高效访问的同时保留了原数据使其他函数不受更新替换影响,这极大地减少了数据热补后的额外执行开销。一方面保留了通过成员访问的数据获取方式,另一方面借助继承内核对父对象的分配释放以及访问控制而无需额外的管理操作。通过借助数据对象的分配释放来管理扩展成员避免修改负责初始化和释放的内核函数,帮助开发者减轻额外的人工负担。
5.为实现上述目的,本发明采用以下技术方案:
6.一种基于对齐空洞的内核数据类型扩展热补方法,所述热补方法包括以下步骤:
7.s1,对补丁文件进行预处理,提取得到热补对象有效信息:
8.从补丁文件中过滤掉与热补无关和对热补机制有影响的内容,提取补丁的更新信息,将补丁的更新信息分为数据类型更新操作以及相应的函数替换操作,形成相应的描述文件;
9.s2,根据步骤s1提取得到的输入数据类型更新操作的描述文件,分析热补目标数据类型的内部空洞布局,输出类型布局定义:
10.根据描述文件枚举待更新的结构名称,通过内核映像中提供的符号表信息查询和对调试信息解析计算热补对象内部的空间布局,转换为空洞的描述和定位;分析扩展成员的空间大小与空洞空间进行匹配,再通过对空洞进行版本的标记和访问模式位的设置,形
成更新后的数据类型视图;
11.s3,结合步骤s1输出的数据类型更新操作的描述文件和步骤s2输出的空洞布局分析输出的新类型定义,编写热补模块程序生成热补模块;编写过程包括:针对涉及类型更新后使用新成员的函数修改其访问的方式,通过指针类型转换的方式重新翻译原数据对象的布局信息,通过基地址偏移的方式完成对新成员的初始化及访问操作;结合对象内部在空洞分析后形成metadata标记来选取不同的访问模式;为热补模块的初始化处增加语义判断来确保初始化操作的执行顺序先于访问,同时提供开发人员访问的公共接口和为系统用户交互开放文件系统接口;
12.s4,热补模块的安装和生效:将步骤s3中生成的热补模块安装至内核并等待更新点使得补丁内容生效,完成热补更新任务。
13.为优化上述技术方案,采取的具体措施还包括:
14.进一步地,步骤s1中,对补丁文件进行预处理,提取得到热补对象有效信息的过程包括以下滋补粥:
15.步骤21,输入补丁文件,对单个文件的差异化进行比较,将补丁文件分解为多个目标文件并记录每个目标文件的路径;
16.步骤22,逐个判断目标文件是否为与当前热补无关的代码内容,如果是无关内容则转入步骤2b,否则转入步骤23;
17.步骤23,通过规则库illegal_rules进行匹配,判断匹配路径是否涉及包括与插桩有关以及对常驻函数的判断的更新操作;如果匹配成功则转入步骤2b,否则进入步骤24;
18.步骤24,判断修改对象的类型,如果是类型定义则转入步骤25,如果是函数修改则转入步骤27;
19.步骤25,按照对类型定义中的新增、替换和删除成员操作对类型定义的步骤进行分解。记录形式为(op,main_type,sub_type,sub_name),其中op代表对数据类型的新增还是删除还是更新,main_type代表修改的父数据类型体,sub_type代表修改的成员类型,sub_name为成员名称;
20.步骤26,将分解后的操作并入类型操作集合patch_report.json的type_ops对象的数组内;转入步骤2a;
21.步骤27,判断是否为宏和静态函数的修改,如果是则进入步骤28,否则转入步骤29;
22.步骤28,通过人工调整宏在相应函数内使用的展开以及静态函数在调用函数内的重新定义,形成语义等价的调整;
23.步骤29,将补丁中非类型的修改并入patch_report.json中的func_ops对象的数组内;
24.步骤2a,会补丁内容进行优化合并形成完整的patch_report.json报告;
25.步骤2b,判断是否处理到补丁文件结尾,如果是则转入步骤2c,否则转到步骤22;
26.步骤2c,输出类型更新操作描述集合和函数更新操作集合,同时记录存在差异化的文件名路径。
27.进一步地,步骤s2中,根据步骤s1提取得到的输入数据类型更新操作的描述文件,分析热补目标数据类型的内部空洞布局,输出类型布局定义包括以下步骤:
28.步骤31,在补丁预处理的输出patch_report.json数据类型操作集合type_ops提取类型名 main_type赋值给type_name;
29.步骤32,读取开启了config_debug_info编译后的内核映像vmlinux中的.debuginfo区的信息,其格式为dwarf;
30.步骤33,匹配dwarf格式信息中一级类型名name等于type_name;
31.步骤34,依次遍历该一级类型下的所有成员,确定其data_member_location和size,通过计算hole_size=next_location-(size+cur_loaction)来确定空洞大小,空洞位置为hole_location =size+cur_location;
32.步骤35,判断子成员是否完全遍历,如果是则进入步骤36,否则回到步骤34;
33.步骤36,记录步骤34中的计算结果hole_size和hole_location到holes_map,以main_type 为键名,以字典组holes为值;
34.步骤37,取类型操作type_ops中对象当前条目的子成员sub_type,确定其类型名和大小;
35.步骤38,结合空洞布局描述holes_map对扩展成员进行空间的分配,形成新的数据类型定义输出;
36.步骤39,判断类型操作集合type_ops对象中的条目是否完全遍历,如果是进入步骤3a,否则回到步骤31;
37.步骤3a,结束流程。
38.进一步地,步骤38中,结合空洞布局描述holes_map对扩展成员进行空间的分配,形成新的数据类型定义输出的过程包括以下步骤:
39.步骤41,输入原对象类型名main_name和扩展成员名member_name和类型member_type;
40.步骤42,查询原类型名空洞布局描述哈希表holes_map,以原对象类型main_type为键来查询哈希表获得holes布局描述;
41.步骤43,遍历holes,判断连续空洞是否满足类型分配的大小要求,如果sizeof(sub_type) 《=hole_size则判定为满足类型分配的大小要求,进入步骤44,否则进入步骤46;
42.步骤44,设置空洞中metadata中的模式标记位mode为0,该metadata利用原对象的空洞布局中第一个holes中的1b空间并被排除在空洞空间,用作模式标注和版本记录,mode=0 表示使用in-site模式访问,即直接访问模式;
43.步骤45,分配该连续空洞为新成员初始偏移地址,并从holes_map中更新已使用的空洞信息;
44.步骤46,设置mode位为1,mode=1表示使用out-site访问,即间接访问模式;
45.步骤47,判断是否存在满足指针空间8b大小的单个空洞,如果是则进入步骤48,否则转入步骤49;
46.步骤48,对当前符合的空洞偏移位置offset记录为member_location,同时生成对main_type 描述中新增成员记录(add,main_type,member_type,member_name,member_location);转入步骤4b;
47.步骤49,判断是否存在两个的4b大小空洞,若存在转入步骤4a,否则转入步骤4b;
48.步骤4a,通过在main_type内设置两个4b空洞,设置其为u32类型的p_low和p_high 作为拼接指针的成员,记录该操作到main_type的描述中;
49.步骤4b,依据新增成员记录标记偏移位置形成新的数据类型定义,命名为main_type_v1,输出此类型定义用作后续的强制类型转换的目标类型;
50.步骤4c,结束流程。
51.进一步地,步骤s3中,结合步骤s1输出的数据类型更新操作的描述文件和步骤s2输出的空洞布局分析输出的新类型定义,编写热补模块程序生成热补模块的过程包括以下步骤:
52.步骤51,输入包含新的类型定义的头文件和函数更新操作集合func_ops;
53.步骤52,判断当前函数操作是否涉及新成员使用,如果涉及则进入步骤53,否则转入步骤5a;
54.步骤53,对数据对象进行类型强制转换,转换目标是头文件定义中的新数据类型main
‑ꢀ
_type_v1;
55.步骤54,判断metadata中mode位是否为0,如果是则进入步骤55,否则转入步骤56;
56.步骤55,直接使用成员访问符号来进行函数更新内容的修改;转入步骤59;
57.步骤56,设置该成员的初始化和空间分配释放函数,记录其分配后的地址为sub_addr;
58.步骤57,在初始化函数内新增w_combineaddr宏对p_low设置为sub_addr的低32 位,p_high为sub_addr的高32位,并将在父对象的分配函数中调用该初始化和分配释放函数;
59.步骤58,在当前函数中涉及新成员的访问操作中新增r_combineaddr对成员进行组合指针的解引用访问;
60.步骤59,形成更行后的函数定义并重新命名为[func_name]_dlp,为之后的插桩替换操作提供输入;
[0061]
步骤5a,判断函数集合遍历是否完成,如果是则进入步骤5b,否则转入步骤52;
[0062]
步骤5b,针对函数设置插桩以及随之带来的符号问题,通过函数替换动态更改执行逻辑;
[0063]
步骤5c,通过ftrace设置链表中的顺序来定义更新函数的插桩顺序以及对通过编程接口 dlp_register注册热补版本信息,设置热补的初始化状态;
[0064]
步骤5d,编写makefile和kconfig文件,形成模块编译条件,在命令行终端输入make-c /lib/modules/`uname-r`/build m=`pwd`生成目标模块文件hotpatch.ko;
[0065]
步骤5e,结束流程。
[0066]
进一步地,步骤5b中,针对函数设置插桩以及随之带来的符号问题,通过函数替换动态更改执行逻辑的过程包括以下步骤:
[0067]
步骤61,输入函数操作集合func_ops;
[0068]
步骤62,通过判断函数参数中是否包含父对象类型main_type,以判断类型是否导致函数原型的修改,如果是则进入步骤65,否则进入步骤63;
[0069]
步骤63,判断函数使用过程中所遇内核符号集合是否为内核导出符号,如果是则进入步骤64,否则转入步骤66;
[0070]
步骤64,通过调用kallsyms_lookup_name()来确定内核中为导出符号的地址;
[0071]
步骤65,通过函数指针的方式进行函数使用,通过对函数指针赋予目标函数原型进行函数指针间接调用目标函数;
[0072]
步骤66,将调用逻辑修改形成更新后的函数定义[func_name]_dlp;
[0073]
步骤67,通过vmlinux中的.debuginfo信息查询符号信息来确认函数是否被优化为内联函数,如果是则进入步骤68,否则转入步骤69;
[0074]
步骤68,在内核源码中查找所有调用该内联函数的调用方,将内联的函数替换为更新后的[func_name]_dlp的调用;
[0075]
步骤69,利用ftrace插桩机制对原函数[func_name]进行替换,替换目标是[func_name]_dlp,所使用的是内核livepatch机制所提供的klp_register_patch函数,通过设置原函数和目标函数完成对原函数的替换;
[0076]
步骤6a,判断函数更新操作集合是否遍历完成,如何是则进入步骤6b,否则回到步骤 62;
[0077]
步骤6b,结束流程。
[0078]
进一步地,步骤s4中,热补模块的安装和生效的过程包括以下步骤:
[0079]
步骤81,输入步骤5d所输出的热补模块文件hotpatch.ko;
[0080]
步骤82,在命令行终端下输入sudo insmod hotpatch.ko在内核中安装该热补模块;
[0081]
步骤83,执行步骤5c中设置的初始化动作,负责对静态全局变量的替换、对间接访问模式所需的变量空间的预分配以及热补任务所需额外操作;
[0082]
步骤84,设置目标线程的热补状态,将patch_state设置为1开始热补;
[0083]
步骤85,基于unwind栈回溯来检查当前时刻内核栈中是否存在目标函数,如不存在则表明是安全更新点,进入步骤86,否则回到步骤84;
[0084]
步骤86,基于ftrace对进行函数的替换操作,将在原函数插桩点进行二进制替换,改变执行流到目标更新函数;
[0085]
步骤87,设置当前线程的patch_state为0,热补过程结束;
[0086]
步骤88,通过register接口注册当前的热补版本信息;
[0087]
步骤89,结束流程。
[0088]
本发明的有益效果是:
[0089]
第一,本发明的基于对齐空洞的内核数据类型扩展热补方法,有效支持了内核补丁中常见的对于数据类型扩展的需求,拓宽了热补技术的应用范围。
[0090]
第二,本发明的基于对齐空洞的内核数据类型扩展热补方法,利用数据对象成员内部的空洞空间,减少了负责对扩展空间的管理任务和访问方式的重写,减轻了热补开发的负担。
[0091]
第三,本发明的基于对齐空洞的内核数据类型扩展热补方法,基于对象内部空洞有效利用了已有缓存,相较于读取外部空间更为快速,高效的命中会降低访问延迟。
附图说明
[0092]
图1是本发明实施例的基于对齐空洞的内核数据类型扩展方法的工作流程图
[0093]
图2是本发明实施例的补丁预处理流程图
[0094]
图3是本发明实施例的空洞布局分析流程图
[0095]
图4是本发明实施例的扩展成员匹配流程图。
[0096]
图5是本发明实施例的热补模块生成的流程图。
[0097]
图6是本发明实施例的函数更新操作中符号解决和插桩流程图。
[0098]
图7是本发明实施例的为用户交互接口和编程接口定义图。
[0099]
图8是本发明实施例的热补模块安装和生效的操作流程图。
具体实施方式
[0100]
现在结合附图对本发明作进一步详细的说明。
[0101]
需要注意的是,发明中所引用的如“上”、“下”、“左”、“右”、“前”、“后”等的用语,亦仅为便于叙述的明了,而非用以限定本发明可实施的范围,其相对关系的改变或调整,在无实质变更技术内容下,当亦视为本发明可实施的范畴。
[0102]
本实施例基于对象在分配空间时会按照编译对齐要求产生内部空洞的特点,提供一种基于对齐空洞的内核数据类型扩展热补方法。该热补方法包括以下关键操作:
[0103]
s1,补丁预处理。对补丁文件进行预处理,从补丁文件中提取补丁的更新信息,并过滤掉其非热补的安全操作。输入补丁文件,输出过滤和优化后的补丁内容。此操作的输出将作为步骤s2空洞布局分析和步骤s3热补模块生成的输入部分。具体操作可以分为:首先过滤与热补无关内容包括注释和文档的内容,然后排除会影响到热补机制的更新安全的部分包括插桩依赖机制的修改,最后提取数据类型更新和函数变动。数据类型更新部分记录成针对类型更新操作的描述,然后输出该补丁的更新描述和函数变动。
[0104]
s2,空洞布局的分析。根据补丁信息提取操作提取得到的目标信息,分析热补目标数据类型的内部空洞布局。输入来自步骤s1补丁预处理操作后的类型操作的描述,输出类型布局定义,此输出将作为步骤s3热补模块的生成的输入。具体操作分为:根据描述文件来枚举待更新的结构名称,通过内核映像中提供的符号表信息查询和对调试信息解析来计算热补对象内部的空间布局,转换为空洞的描述和定位。分析扩展成员的空间大小与空洞空间进行匹配,再通过对空洞进行版本的标记和访问模式位的设置,形成更新后的数据类型视图。
[0105]
s3,热补模块的生成。结合步骤s1补丁预处理所产生的更新操作描述和s2空洞布局分析输出的新类型定义,编写热补模块程序生成热补模块。其中涉及对相关函数修改以及目标对象符号的定位,在形成新数据类型视图后需要对相关内核函数进行修改,改变其访问数据的方式,设计了直接访问和指针间接访问两种模式来扩大类型热补的场景覆盖率。
[0106]
s4,热补模块的安装和生效。用于将步骤s3生成的模块安装至内核并等待更新点使得补丁内容生效,完成热补更新任务。具体可以分为模块安装、模块初始化、热补初始状态设置更新点等待和热补生效,热补完成状态设置和版本注册。
[0107]
如图1基于对齐空洞的内核数据类型扩展方法的工作流程图所示,本实施例提出一种基于对齐空洞的内核数据类型扩展热补方法,用于内核补丁中涉及数据类型扩展的动态更新任务,依据x86_64架构下linux内核作实际操作称述。总体包括四类操作,补丁的预
处理、空洞布局分析、热补模块生成以及热补模块的安装和生效。
[0108]
补丁预处理操作是对热补对象有效信息的提取,将为后续的空洞布局分析和热补模块生成提供更新内容的输入。补丁的预处理包括无关内容的过滤、优化补丁实现和更新操作的提取。通过无关内容过滤来简化热补工作而避免对热补模块的生成造成干扰,同时排除会导致热补不安全的一些更新操作来确保热补方法的可靠性。优化补丁实现是过滤热补功能无关的优化操作,以及对更新中涉及内核宏的定义展开来针对热补有效性进行优化。更新内容的提取是在进行无关内容过滤和优化补丁后进行的,将更新内容分为数据类型的更新操作以及相应的函数替换操作,通过形成描述文件来指导空洞布局分析和热补模块的生成。
[0109]
空洞布局分析操作是解决类型更新中遇到的扩展空间的问题,在生成热补模块的时候考虑到扩展成员需要额外的内存空间放置,具体分为空洞布局生成以及扩展成员匹配来负责更新内容与空洞匹配。其中空洞的布局信息生成是通过内核映像的调试信息来分析热补数据类型的成员的内存布局,是为后面的扩展成员的匹配来提供空间信息。扩展成员的匹配是对数据类型的空洞的利用,通过生成兼容原数据布局视图的类型定义来完成对空洞的适用。同时标记所有修改的内核函数用于后续热补模块的生成。
[0110]
热补模块生成操作是结合当前内核版本进行模块的编写,完成热补模块的编译生成。其中主要负责更新中相关函数的替换,内核中符号的定位和初始化操作设置。相关函数是指涉及类型更新后使用新成员的函数需要修改其访问的方式,通过指针类型转换的方式重新翻译原数据对象的布局信息,通过基地址偏移的方式完成对新成员的初始化及访问操作。符号的定位是解决内核未向开发人员外部导出的符号,主要用于解决静态全局变量以及函数更新中涉及内部静态函数的使用。解决了函数更新和符号定位的问题后,为了保证数据不会发生未初始化导致访问异常,需要在模块生成阶段为模块的初始化处增加语义判断来确保初始化操作的执行顺序先于访问。同时提供开发人员访问的公共接口和为系统用户交互开放文件系统接口。
[0111]
热补模块的安装和生效操作利用编译生成后热补模块,依赖模块的动态加载的特性作为内核的数据类型动态更新的实现载体。通过insmod安装热补模块,执行预先设置的初始化动作,以及调用内核现有机制livepatch进行更新点检查完成函数替换,最终通过注册接口记录热补版本完成热补更新任务。
[0112]
图2为补丁预处理流程图。热补的安全不单是热补过程的安全性,也包含热补模块生成时编译、运行的安全性。官方补丁的正确性将由提交者通过单元测试和审核者的检验保证,可以从词法、语法以及语义的检查工具和单元测试框架完成。但是由于补丁只能保证针对源码的安全性,并未考虑热补场景的应用,实际上补丁中会存在与热补机制相冲突的内容所以需要对其加以筛选和检验。而过滤器将对官方发布的补丁进行预处理,其目的在于保证热补将是符合内核的动态更新正确性。其中最为重要特点是热补的补丁不可对函数更新所基于的插桩机制ftrace自身有关函数和常驻内核线程涉及的函数进行修改。此外,我们通过预处理来提取补丁的基础内容,将函数和数据类型的更改通过json格式输出,以指导后续的热补模块生成。我们将通过脚本工具对其进行过滤,再手动来去除语义不变的优化代码,流程实现如图2所示,我们需要在过滤规则库中添加正则匹配项目来进行优化输出。经过过滤器的补丁将会生成报告,记录修改了的函数以及数据类型,并输出精简的代
码。具体流程如下:
[0113]
步骤20,开始流程。
[0114]
步骤21,输入补丁文件,通过补丁文件中的单条diff条目代表对单个文件的差异化比较,我们通过匹配关键字“diff
‑‑
git”来分解为处理单元并记录目标文件路径。
[0115]
步骤22,通过正则匹配来判断是否是代码注释、内核配置、是否包含关键字__init__内核初始化代码。通过路径文件名的后缀来判断是是否是汇编代码和内核文档。以及对包含arch 下的路径的判断是否是与当前平台无关的代码内容;如果为无关内容则转入步骤2b,否则转入步骤23。
[0116]
步骤23,匹配路径是否涉及kprobe、ftrace和alternative等与插桩有关以及对常驻函数的判断的更新操做,通过规则库illegal_rules进行匹配。如果匹配结果是则转入步骤2b,否则进入步骤24。
[0117]
步骤24,通过grep匹配每行第二个“@@”后的字符串来判断修改对象,以关键词struct 来确认为数据类型修改。如果是类型定义则转入步骤25,否则转入步骤27。
[0118]
步骤25,按照对类型定义中的新增、替换和删除成员操作对类型定义的步骤进行分解。记录形式为(op,main_type,sub_type,sub_name)。其中op代表对数据类型的新增还是删除还是更新,main_type代表修改的父数据类型体,sub_type代表修改的成员类型,sub_name为成员名称。
[0119]
注意到这里并未对成员的位置作出规定,因为成员在结构体定义阶段内部的位置调整并不会影响到数据的读取。
[0120]
步骤26,将分解后的操作并入类型操作集合patch_report.json的type_ops对象的数组内。转入步骤2a。
[0121]
步骤27,判断是否为宏和静态函数的修改,通过匹配关键字define和static来区分。如果是则进入步骤28,否则转入步骤29。
[0122]
步骤28,通过人工调整宏在相应函数内使用的展开以及静态函数在调用函数内的重新定义,形成语义等价的调整。
[0123]
步骤29,将补丁中非类型的修改并入patch_report.json中的func_ops对象的数组内。
[0124]
步骤2a,通过调整步骤26和步骤29中的实现,排除一些与热补目标无关的优化操作,以及可以合并的函数替换行为,进行内容的优化合并形成完整的patch_report.json报告。
[0125]
步骤2b,判断是否处理到补丁文件结尾,如果是则转入步骤2c,否则还有其他差异需要处理转到步骤22。
[0126]
步骤2c,输出记录了类型更新操作type_ops和函数更新操作func_ops的patch_report.json,并同时记录存在差异化的文件名路径,为后续的空洞布局分析和热补模块生成提供输入。
[0127]
步骤2d,结束流程。
[0128]
图3为空洞布局分析流程图。热补需要解决拓展成员与父对象之间的关联,而结构类型的存在是描述需要同类型或不同类型数据的集合,通过连续的物理存储结构建立之间的联系。而对于现有对象内存固定的情形,通过观察到编译时的对齐技术会在数据对象内
部产生空洞现象,利用空洞来满足对数据类型的扩展需求。这里将解释如何计算编译的空洞形成热补对象的空洞布局信息以及扩展成员和空洞布局的匹配。这是为热补模块的生成的准备工作,将形成新的数据类型布局定义头文件new_type_vx.h。具体流程如下:
[0129]
步骤30,开始流程;
[0130]
步骤31,在补丁预处理的输出patch_report.json数据类型操作集合type_ops提取类型名 main_type赋值给type_name。
[0131]
步骤32,读取开启了config_debug_info编译后的内核映像vmlinux中的.debuginfo区的信息,其格式为dwarf。
[0132]
步骤33,匹配dwarf格式信息中一级类型名name等于type_name。
[0133]
步骤34,分析成员之间的空洞,依次遍历该一级类型下的所有成员,确定其 data_member_location和size,通过计算hole_size=next_location-(size+cur_loaction)来确定空洞大小,空洞位置为hole_location=size+cur_location;
[0134]
步骤35,判断子成员是否完全遍历,如果是则进入步骤36,否则回到步骤34。
[0135]
步骤36,记录步骤34中的计算结果hole_size和hole_location到holes_map。以main_type 为键名,以字典组holes为值。
[0136]
步骤37,取类型操作type_ops中对象当前条目的子成员sub_type,确定其类型名和大小。
[0137]
步骤38,扩展空间的匹配,用于结合空洞布局描述holes_map对扩展成员进行空间的分配,然后形成新的数据类型定义输出,详细的步骤分解见图4。
[0138]
步骤39,判断类型操作集合type_ops对象中的条目是否完全遍历,如果是进入步骤3a,否则回到步骤31。
[0139]
步骤3a,结束流程。
[0140]
图4为扩展成员匹配流程图,将会结合布局信息和更新操作对数据对象进行空洞分配,用于形成新的数据布局定义,本发明为了便于使用将通过类型转换的方式强制翻译内部成员偏移量信息。为后续热补模块的生成中对相关调用函数的修改提供输入。具体流程如下:
[0141]
步骤40,开始流程。
[0142]
步骤41,输入原对象类型名main_name和扩展成员名member_name和类型member_type。
[0143]
分别为main_type、sub_name和sub_type赋值而来。
[0144]
步骤42,查询原类型名空洞布局描述哈希表holes_map,以原对象类型main_type为键来查询哈希表获得holes布局描述。
[0145]
步骤43,遍历holes,判断连续空洞是否满足类型分配的大小要求,如果sizeof(sub_type) 《=hole_size则满足进入步骤44,否则进入步骤46。
[0146]
步骤44,设置空洞中metadata中的模式标记位mode为0,该metadata利用原对象的空洞布局中第一个holes中的1b空间并被排除在空洞空间,用作模式标注和版本记录,mode=0 表示使用in-site模式访问,即直接访问模式。
[0147]
步骤45,分配该连续空洞为新成员初始偏移地址,并从holes_map中更新已使用的空洞信息。转入步骤4b。
[0148]
步骤46,设置mode位为1,mode=1表示使用out-site访问,即间接访问模式。
[0149]
步骤47,判断是否存在满足指针空间8b大小的单个空洞,如果是则进入步骤48,否则转入步骤49。
[0150]
步骤48,对当前符合的空洞偏移位置offset记录为member_location,同时生成对main_type 描述中新增成员记录(add,main_type,member_type,member_name,member_location);转入步骤4b。
[0151]
步骤49,判断是否存在两个的4b大小空洞,若存在转入步骤4a,否则转入步骤4b。
[0152]
步骤4a,通过在main_type内设置两个4b空洞,设置其为u32类型的p_low和p_high 作为拼接指针的成员,这为之后函数中访问修改为combineaddr宏的调用。然后记录该操作到main_type的描述中,与步骤48一致。
[0153]
步骤4b,依据上述步骤中新增成员记录标记偏移位置形成新的数据类型定义,命名为 main_type_v1,输出此类型定义用作后续的强制类型转换的目标类型。
[0154]
步骤4c,结束流程。
[0155]
图5为热补模块生成的流程图。在获取到结合空洞进行分配之后的新类型定义之后,我们需要修改原补丁中针对该类型的使用函数。需要结合对象内部在空洞分析后形成metadata 标记来选取不同的访问模式,之后设置初始化的操作步骤然后结合内核源码进行热补模块的编译输出,最终形成完成动态更新的内核模块。具体流程如下:
[0156]
步骤50,开始流程。
[0157]
步骤51,输入包含新的类型定义的头文件和函数更新操作集合func_ops。
[0158]
步骤52,对当前函数更新操作利用字符串匹配sub_name,如果匹配成功则进入步骤53,否则转入步骤5a。
[0159]
步骤53,对数据对象进行类型强制转换,转换目标是头文件定义中的新数据类型main
‑ꢀ
_type_v1。
[0160]
步骤54,判断metadata中mode位是否为0,如果是则进入步骤55,否则转入步骤56。
[0161]
步骤55,直接使用成员访问符号来进行函数更新内容的修改。转入步骤59。
[0162]
步骤56,设置该成员的初始化和空间分配释放函数,记录其分配后的地址为sub_addr。
[0163]
步骤57,在初始化函数内新增w_combineaddr宏对p_low设置为sub_addr的低32 位,p_high为sub_addr的高32位。并将在父对象的分配函数中调用该初始化和分配释放函数。
[0164]
步骤58,在当前函数中涉及新成员的访问操作中新增r_combineaddr对成员进行组合指针的解引用访问。
[0165]
步骤59,形成更行后的函数定义并重新命名为[func_name]_dlp为之后的插桩替换操作提供输入。
[0166]
步骤5a,判断函数集合遍历是否完成,如果是则进入步骤5b,否则转入步骤52。
[0167]
步骤5b,对函数设置插桩以及随之带来的符号问题的解决,通过函数替换动态更改执行逻辑。这部分在图6中将详细解释。
[0168]
步骤5c,进行模块的初始化设置,通过ftrace设置链表中的顺序来定义更新函数的插桩顺序以及对通过编程接口dlp_register注册热补版本信息,设置热补的初始化状
态。
[0169]
步骤5d,编写makefile和kconfig文件,形成模块编译条件,然后在命令行终端输入 make-c/lib/modules/`uname-r`/build m=`pwd`生成目标模块文件hotpatch.ko。
[0170]
步骤5e,结束流程。
[0171]
图6为函数更新操作中符号解决和插桩流程图。用于解决在类型更新后会导致函数原型的修改以及未导出符号的解决。具体流程如下:
[0172]
步骤60,开始流程。
[0173]
步骤61,输入函数操作集合func_ops。
[0174]
步骤62,判断类型是否导致函数原型的修改,判断方式是判断函数参数中是否包含父对象类型main_type。如果是则进入步骤65,否则进入步骤63。
[0175]
步骤63,判断函数使用过程中所遇内核符号集合是否为内核导出符号,即在内核源码中被export系列宏导出。如果是则进入步骤64,否则转入步骤66。
[0176]
步骤64,通过调用kallsyms_lookup_name()来确定内核中为导出符号的地址。
[0177]
步骤65,通过函数指针的方式进行函数使用,通过对函数指针赋予目标函数原型进行函数指针间接调用目标函数。
[0178]
步骤66,将调用逻辑修改形成更新后的函数定义[func_name]_dlp。
[0179]
步骤67,判断该函数是否被优化为内联函数,即通过vmlinux中的.debuginfo信息查询符号信息来确认是否被内联。如果是则进入步骤68,否则转入步骤69。
[0180]
步骤68,在内核源码中查找所有调用该内联函数的调用方,在该集合内进行替换,对内联的函数替换为更新后的[func_name]_dlp的调用。
[0181]
步骤69,利用ftrace插桩机制进行对原函数[func_name]的替换,替换目标是 [func_name]_dlp,所使用的是内核livepatch机制所提供的klp_register_patch函数,通过设置原函数和目标函数完成对原函数的替换。
[0182]
步骤6a,判断函数更新操作集合是否遍历完成,如何使则进入步骤6b,否则回到步骤 62。
[0183]
步骤6b,结束流程。
[0184]
图7为用户交互接口和编程接口定义图。用于定义编程接口和用户交互接口。um(updatemanager)为本方法为编程实现提供的内核模块用于提供版本控制接口,支持累积更新操作的编程接口,register用于在热补完成时对此次热补的版本注册,query用于在初始化阶段查询当前热补最新版本。update用于更新当前版本的注册信息。cancellation用于回滚更新版本时对当前版本的注销。此外为了方便用户进行实时控制,基于内核livepatch机制在sysfs下开放路径/sysfs/dlp/type/version/inspection用于查看当前类型热补的状态和版本信。在命令行终端下对echo 0》/sysfs/dlp/type/version/on来进行回滚操作。
[0185]
图8为热补模块的安装和生效流程图。用于向内核安装热补模块,标记热补状态完成热补更新任务。具体步骤如下:
[0186]
步骤80,开始流程。
[0187]
步骤81,输入步骤5d所输出的热补模块文件hotpatch.ko。
[0188]
步骤82,在命令行终端下输入sudo insmod hotpatch.ko在内核中安装该热补模
块。
[0189]
步骤83,模块初始化,会执行步骤5c中设置的初始化动作,负责对静态全局变量的替换、对间接访问模式所需的变量空间的预分配以及热补任务所需额外操作。
[0190]
步骤84,设置目标线程的热补状态,将patch_state设置为1表示开始热补。
[0191]
步骤85,基于unwind栈回溯来检查当前时刻内核栈中是否存在目标函数,如不存在则表明是安全更新点进入步骤86,否则回到步骤84。
[0192]
步骤86,基于ftrace对进行函数的替换操作,将在原函数插桩点进行二进制替换,改变执行流到目标更新函数。
[0193]
步骤87,设置当前线程的patch_state为0,表示本热补过程结束。
[0194]
步骤88,通过register接口注册当前的热补版本信息。
[0195]
步骤89,结束流程。
[0196]
以上仅是本发明的优选实施方式,本发明的保护范围并不仅局限于上述实施例,凡属于本发明思路下的技术方案均属于本发明的保护范围。应当指出,对于本技术领域的普通技术人员来说,在不脱离本发明原理前提下的若干改进和润饰,应视为本发明的保护范围。
当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1