Как углубиться в очевидную коррупцию - PullRequest
0 голосов
/ 11 июля 2020

Я достаточно долго работаю с C и C ++. У меня есть специальность по информатике. Я знаком с внутренними подводными камнями c низкоуровневого доступа к памяти процессов, которые предоставляют эти языки. Я провел в них дни и недели.

Учился использовать valgrind около десяти лет go был спасителем с точки зрения обнаружения мелких ошибок доступа и тому подобного. В настоящее время я также использую ASAN с clion, и ошибки такого рода обычно выявляются и быстро устраняются. 1007 * У меня есть объект, который включает не публикуемое c sockaddr_storage поле с именем from. Доступ к нему можно получить через:

const sockaddr_storage* getSockAddr () {
    return &from;
}

Но возвращенный адрес неверен. Начиная с точки останова в строке return в gdb:

Breakpoint 3, socketeering::Socket::getSockAddr (this=0x617000000400) at Socket.hpp:81
81          return &from;
(gdb) p this
$1 = (socketeering::UDPsocket * const) 0x617000000400
(gdb) p &from
$2 = (sockaddr_storage *) 0x617000000600
(gdb) p (const sockaddr_storage*)&from
$3 = (const sockaddr_storage *) 0x617000000600

Кажется довольно очевидным, что возвращаемое значение должно быть 0x6170000006 00 . Но нет:

(gdb) fin
Run till exit from #0  socketeering::Socket::getSockAddr (this=0x617000000400) at Socket.hpp:81
0x00000000004290ab in udpHandler::dataReady (this=0x631000014810, iod=0x617000000400, con=0x60e0000249b0) at /opt/cogware/C++/Socketeering2/demo/echo_server.cpp:66
66              auto sa = sock->getSockAddr();
Value returned is $4 = (const sockaddr_storage *) 0x617000000618
                                                              ^^
(gdb) p sock
$5 = (socketeering::UDPsocket *) 0x617000000400

Это нехорошо - внутри структуры 18 байт. Хуже того, я НЕ МОГУ воспроизвести его с помощью простого SSCCE:

class foo {
    sockaddr_storage ss;
    public:
        foo () { cout << &ss << "\n"; }
        const sockaddr_storage* getSockAddr () { return &ss; }
}; 

Это означает, что это не какое-то непонимание правил и т. Д. c. Очевидно, это не ошибка logi c.

Это должно быть повреждение, верно?

Это однопоточный процесс, и если вместо fin я просто продолжаю переходить к посмотреть, что происходит, смотреть буквально не на что. Один шаг до закрытия функции, а следующий при присваивании с неправильным значением. Ни valgrind, ни ASAN не указывают на хиджинкс.

На что я могу посмотреть, чтобы узнать, что происходит? Очевидно, что что-то идет не так, между:

 return &from;

И фактическим возвратом значения. Разве поиск в дампах сборок ключей - единственный оставшийся мне путь (предполагая, что это вообще поможет, я не парень из ASM)? а ASAN не уловила. Отправной точкой для этого является выяснение того, при каких обстоятельствах они не поймают коррупцию.

  1. Который я поднимал ранее в теперь удаленном вопросе. Все, что можно было сказать, было именно то, что я сказал бы, если бы прочитал такой вопрос: нам нужен SSCCE, и повреждение может быть в других частях кода. Дело в том, что в информации, которую я должен показать, нет ничего, что объясняет проблему, но без приглашения всех участвовать в проекте 10-20K LO C, это все, что я могу сделать. Итак, я спрашиваю сейчас не о том, что не так, а «Как я могу определить, что не так?»

1 Ответ

0 голосов
/ 21 июля 2020

Ищет в дампах сборки подсказки, единственный оставшийся мне путь

Да, здесь подходящим подходом является использование команды disas.

(предполагая, что это вообще поможет, я не парень ASM)?

Даже если вы не можете записать сборку, часто довольно легко читать сборка. Особенно, если это что-то вроде x86_64 и не связано со сложным вращением битов или с плавающей запятой. И это навык, который вам пригодится.

Обычно проблема такого рода является результатом нарушения ODR: где-то в вашей программе у вас есть другое определение socketeering::Socket, в котором смещение между this и from - это 24 (это не 18 байт, это 0x18 байт!) Вместо 0.

Часто такое нарушение ODR происходит из-за использования разных #defines в разных части кода, например,

class Socket {
#if defined(TRACING_ON)
  char trace_buf[24];
#endif
  sockaddr_storage from;
};

Скомпилируйте приведенную выше структуру в один .cc файл с -DTRACING_ON, скомпилируйте другой .cc без него, свяжите их вместе в один двоичный файл и БУМ: вы можете увидеть именно ошибка, которую вы описали.

Иногда проблема возникает из-за того, что не перекомпилирован весь код (например, у вас может быть старый объект или общая библиотека, лежащая вокруг ).

Это также может быть связано с объединением кода, созданного разными компиляторами, хотя это случается редко (обычно, если компиляторы несовместимы с ABI, они используют другое искажение имен, чтобы предотвратить компоновку программы).

Примечание: если Socket наследуется от какого-то другого класса, разница может исходить от суперкласса, а не от самого Socket.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...