同步操作将从 openEuler/libxml2-rust 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
#项目名称:libxml2-rust 使用Rust语言重写libxml2高危模块
libxml2历史安全漏洞较多,其中内存漏洞在两个项目漏洞中分别占比74%,都比较高,产生这些安全漏洞或与这些安全漏洞相关的模块称为高危模块。本项目拟通过Rust语言重写libxml2库的这些高危模块,利用Rust语言自身的内存安全机制修复这些安全漏洞,增加两个库的安全性和可靠性。
整体方案: 按照以下步骤,对libxml2部分文件进行改写并通过测试:
(1)Step1:混合编译 使用CMake工具,通过编写CMakeLists.txt文件,将Rust项目编译过程插入到C项目的编译过程和链接过程中间。理论过程如下:
具体文件改写方式如下:
改写上述文件后,可通过如下顺序完成整个项目的混合编译过程:
(2)Step2:自动改写C代码 项目中使用成熟的C转Rust工具C2Rust进行自动改写,该工具可以自动转换得到与C代码功能相同的Rust代码。
改写过程遵循以下规范:
对每个函数改写完成后,使用ctest工具执行执行系统测试,并针对出现的问题进行代码手动修复,保证每次改写都可通过所有测试用例。
(3)Step3:改写宏定义 宏定义在C工程中主要有宏作为值进行访问和宏作为编译条件进行访问两种情况。针对这两种情况,分别制定了不同的改写方案。
宏作为值时,在C工程中,将所有方法中使用的宏在方法之前进行定义,并对每个方法使用的宏通过一个结构体来保存。在C工程中编写函数,负责返回该结构体中各个属性的值,以供Rust工程调用。在Rust工程中定义结构相同的结构体,并构造单例全局变量,调用C工程中传递结构体值的函数,赋值给单例全局变量,以供Rust工程使用。Rust工程调用C工程中传递结构体值函数的过程符合上文所述Rust改写规范。
宏作为编译条件时,在C工程中,与宏作为值类似,建立方法返回条件宏的值。在Rust工程中,建立专门用于处理条件宏值的rust工具包,即rust_ffi,在工具包中编写cfg设置方法,该方法作用是根据C工程提供的条件宏值打印对应宏命令行,以建立Rust的条件宏。在存放改写代码的rust工程包,即rust_project,通过调用rust_ffi工具包的cfg设置方法添加对应cfg属性。此时,Rust工程即可根据cfg属性情况进行条件编译。
在该宏作为编译条件的解决方案下,Rust工程中rust_ffi工具包需要依赖可执行的c库,因此需要在CMakeLists.txt添加全局宏定义,再拟造出一个无需链接Rust工程即可执行的C库,添加方式如下。同时C工程中,当进行编译时,需要提供可直接运行的空壳方法,以生成可执行C库。
if(STEP STREQUAL "link")
ADD_DEFINITIONS(-DCOMPILE_WITH_RUST) #当链接rust时,添加全局宏定义COMPILE_WITH_RUST
endif()
值得一提的是,在自动改写过程中,如果环境不满足条件宏,则对应代码无法自动翻译。因此需要首先将条件宏删除,获得完整代码,再根据条件宏分别添加对应代码。
(4)Step4:缩小unsafe范围
在进行自动代码改写后,Rust版本函数都使用了unsafe进行包裹,需要进行范围缩小。针对以下情况,代码进行了unsafe范围调整:
按照上述方式进行unsafe调整后,即可去除Rust版本函数的整体unsafe标识符,缩小其unsafe范围。
准备make,cmake,rust,python-devel等环境。版本推荐:
安装步骤如下:
通过gitee 获取代码 git clone git@gitee.com:openeuler/libxml2-rust.git
运行build.sh编译代码
运行make install 将代码安装到系统中
使用时,引入libxml包下的头文件parser.h和xmlmemory.h进行xml文档解析。例如,针对一个文档名为story.xml的文档,其解析代码如下:
/* 解析storyinfo节点,打印keyword节点的内容 */
void parseStory(xmlDocPtr doc, xmlNodePtr cur){
xmlChar* key;
cur=cur->xmlChildrenNode;
while(cur != NULL){
/* 找到keyword子节点 */
if(!xmlStrcmp(cur->name, (const xmlChar *)"keyword")){
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("keyword: %s\n", key);
xmlFree(key);
}
cur=cur->next; /* 下一个子节点 */
}
return;
}
/* 解析文档 */
static void parseDoc(char *docname){
/* 定义文档和节点指针 */
xmlDocPtr doc;
xmlNodePtr cur;
/* 进行解析,如果没成功,显示一个错误并停止 */
doc = xmlParseFile(docname);
if(doc == NULL){
fprintf(stderr, "Document not parse successfully. \n");
return;
}
/* 获取文档根节点,若无内容则释放文档树并返回 */
cur = xmlDocGetRootElement(doc);
if(cur == NULL){
fprintf(stderr, "empty document\n");
xmlFreeDoc(doc);
return;
}
/* 确定根节点名是否为story,不是则返回 */
if(xmlStrcmp(cur->name, (const xmlChar *)"story")){
fprintf(stderr, "document of the wrong type, root node != story");
xmlFreeDoc(doc);
return;
}
/* 遍历文档树 */
cur = cur->xmlChildrenNode;
while(cur != NULL){
/* 找到storyinfo子节点 */
if(!xmlStrcmp(cur->name, (const xmlChar *)"storyinfo")){
parseStory(doc, cur); /* 解析storyinfo子节点 */
}
cur = cur->next; /* 下一个子节点 */
}
xmlFreeDoc(doc); /* 释放文档树 */
return;
}
rust库作为libxml2的依赖不单独对外提供服务。libxml2-rust的使用方式与libxml2完全相同,更多使用方式可以参考gitlab上的wiki:https://gitlab.gnome.org/GNOME/libxml2/-/wikis/home。
libxml2-2.9.12_openEuler_version目录下的是原始代码,但在openEuler操作系统下有部分ctest测试用例无法通过,故我们将libxml2官方库的代码放在libxml2-2.9.12_github_version目录下一并提交。所有对libxml2-2.9.12_openEuler_version的改写操作也会同步到libxml2-2.9.12_github_version,通过后者能否通过所有ctest测试用例来判断改写内容的正确性。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。