Ошибка адреса вне границ при чтении xml

Я получаю странное segfault при использовании libxml для анализа файла. Этот код работал раньше, когда я скомпилировал его как 32-битное приложение. Я изменил его на 64-битное приложение, и оно перестало работать.

Ошибка seg возникает по адресу «if (xmlStrcmp (cur-> name, (const xmlChar *) «servers»))»

cur->name является константой 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
    Details:0xed11f72000007fff <Address 0xed11f72000007fff out of bounds>

После инициализации cur параметр имени

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>

Система:

ОС: Redhat Enterprise Linux 6.4 64-битная

ССЗ: 4.4.7-3

пакеты: libxml2-2.7.6-8.el6_3.4.x86_64


person user758114    schedule 20.10.2015    source источник
comment
У вас есть 64-битная версия libxml?   -  person vmg    schedule 22.10.2015
comment
да libxml2-2.7.6-8.el6_3.4.x86_64   -  person user758114    schedule 22.10.2015
comment
Вы пробовали запускать приложение с помощью valgrind, как это делал Сэм Варшавчик?   -  person Jørgen Fogh    schedule 29.10.2015


Ответы (2)


Я взял ваш код как есть и добавил:

#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\"?>\n\n<servers>\n\n<server_info>\n\n    <server_name>Server1</server_name>\n\n    <server_ip>127.0.0.1</server_ip> \n\n    <server_data_port>9000</server_data_port> \n\n</server_info>\n\n<server_info>\n\n    <server_name>Server2</server_name> \n\n    <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. Но код будет работать нормально, выводить 100 чисел от 0 до 99 и давать сбой при возврате функции main(). Но, очевидно, ошибка не в «возврате 0».

Это аналогично тому, что происходит с вашим приложением. В какой-то момент происходит некоторое повреждение памяти, которое существенно не влияет на выполнение кода, пока ваш код не попытается проанализировать ваш XML-файл.

Добро пожаловать в С++.

person Sam Varshavchik    schedule 25.10.2015
comment
Спасибо, мне и в голову не пришло, что проблема может быть в другом месте. Просто быстрое продолжение. Есть ли способ перекомпилировать код, чтобы изменить точку, в которой происходит нарушение памяти? - person user758114; 30.10.2015
comment
Суть порчи памяти в том, что результаты непредсказуемы, и нет никаких правил или руководств, которым нужно следовать. На самом деле не существует простого, раскрашиваемого по номерам рецепта или процесса для отслеживания и обнаружения настоящей ошибки. Типичные вещи, которые я пробую в таких ситуациях, — это использование инструмента статического анализа, такого как valgrind, или временное изменение логики кода, чтобы пропустить большие участки кода, которые обычно выполняются, чтобы увидеть, исчезнет ли повреждение памяти. Все сводится к опыту, знаниям и пониманию того, как процессор выполняет код и как работает ваше приложение. - person Sam Varshavchik; 31.10.2015

Проблема заключалась в том, что мы использовали #pragma pack(1) в нашем коде, что означает, что bool в DOMParser упакованы до 1 байта, тогда как Xerces не использует #pragma pack и получает упаковку по умолчанию из 4 байтов.

person user758114    schedule 24.03.2016