读取xml时出现越界错误

Address Out of bounds error when reading xml

本文关键字:越界 错误 xml 读取      更新时间:2023-10-16

当使用libxml解析文件时,我得到一个奇怪的段错误。这段代码以前在我将其编译为32位应用程序时可以工作。我把它改成64位的应用程序,它停止工作了。

seg错误出现在"if (xmlStrcmp(cur->name, (const xmlChar *)"服务器"))"

cur->name是一个const xmlChar *,它指向一个地址,表明它超出了边界。但是当我调试并进入内存位置时,数据是正确的。

int XmlGetServers()
{
xmlDocPtr doc;
xmlNodePtr cur;
doc = xmlParseFile("Pin.xml");
if (doc == NULL)
{
    std::cout << "n Pin.xml not parsed successfully." << std::endl;
    return -1;
}
cur = xmlDocGetRootElement(doc);
if (cur == NULL)
{
    std::cout << "n Pin.xml is empty document." << std::endl;
    xmlFreeDoc(doc);
    return -1;
}
if (xmlStrcmp(cur->name, (const xmlChar *) "servers"))
{
    std::cout << "n ERROR: Pin.xml of the wrong type, root node != servers." << std::endl;
    xmlFreeDoc(doc);
    return -1;
}
}

在初始化cur之前,name参数是

Name : name
    Details:0xed11f72000007fff <Address 0xed11f72000007fff out of bounds>

初始化cur后,name参数为

Name : name
    Details:0x64c43000000000 <Address 0x64c43000000000 out of bounds> 

引用的XML文件

<?xml version="1.0"?>
<servers>
<server_info>
    <server_name>Server1</server_name>
    <server_ip>127.0.0.1</server_ip> 
    <server_data_port>9000</server_data_port> 
</server_info>
<server_info>
    <server_name>Server2</server_name> 
    <server_ip>127.0.0.1</server_ip> 
    <server_data_port>9001</server_data_port> 
</server_info>
</servers>

系统:

OS: Redhat Enterprise Linux 6.4 64位

GCC: 4.4.7-3

包:libxml2 2.7.6 - 8. el6_3.4.x86_64

我把你的代码,原样,并添加:

#include <libxml/parser.h>
#include <iostream>

然后将函数重命名为main(),并在x86-64 Fedora 22上编译它,该版本有libxml2 2.9.2

结果代码使用示例文件成功运行,没有段错误。甚至valgrind也没有发现内存访问冲突。作为证明,生成的简化的空间日志如下:

stat("Pin.xml", {st_mode=S_IFREG|0644, st_size=362, ...}) = 0
stat("Pin.xml", {st_mode=S_IFREG|0644, st_size=362, ...}) = 0
stat("Pin.xml", {st_mode=S_IFREG|0644, st_size=362, ...}) = 0
open("Pin.xml", O_RDONLY)               = 3
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "<?xml version="1.0"?>nn<servers>nn<server_info>nn    <server_name>Server1</server_name>nn    <server_ip>127.0.0.1</server_ip> nn    <server_data_port>9000</server_data_port> nn</server_info>nn<server_info>nn    <server_name>Server2</server_name> nn    <ser"..., 8192) = 362
read(3, "", 7830)                       = 0
getcwd("/tmp", 1024)                    = 5
close(3)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

虽然这是Fedora的libxml2和gcc稍微新了一点,但是这个区别并不重要。这里的答案是,这里显示的代码没有任何问题。我看不出有什么问题。

但它显然是一个更大的应用程序的一部分,你的内存损坏发生在你的应用程序的其他部分,只有当你的应用程序执行到这部分时,它才会表现出来。

c++的问题是,仅仅因为代码在某个特定点崩溃,并不意味着这一行代码就是问题所在。想出一个简单的例子应该不难:

#include <iostream>
#include <cstring>
int main()
{
    char foo[3];
    strcpy(foo, "FoobarbazXXXXXXXXXXXXXXXXXXXXXX");
    for (int i=0; i<100; i++)
        std::cout << i << std::endl;
    return 0;
}

这里的错误显然发生在strcpy行。但是代码将正常运行,并打印从0到99的100个数字,并在main()返回时崩溃。但是,很明显,"return 0"并不是错误所在。

这与您的应用程序中发生的情况类似。在某些时候会发生某种内存损坏,在您的代码试图解析XML文件之前,这不会对代码执行产生实质性影响。

欢迎来到c++。

问题是我们在代码中使用了#pragma pack(1),这意味着DOMParser中的bool被压缩到1字节,而Xerces没有#pragma pack,而是获得4字节的默认打包。