Мне нужна помощь реального C-гуру для анализа сбоя в моем коде. Не для фиксации аварии; Я могу легко это исправить, но прежде чем делать это, я хотел бы понять, насколько возможен этот крах, поскольку мне кажется совершенно невозможным.
Этот сбой происходит только на машине клиента, и я не могу воспроизвести его локально (поэтому я не могу выполнить код с помощью отладчика), так как я не могу получить копию этой пользовательской базы данных. Моя компания также не позволит мне просто изменить несколько строк в коде и создать пользовательскую сборку для этого клиента (поэтому я не могу добавить некоторые строки printf и снова запустить код), и, конечно, клиент имеет сборку без отладочные символы. Другими словами, мои способности к дебоксированию очень ограничены. Тем не менее я мог бы свернуть аварийную ситуацию и получить некоторую отладочную информацию. Однако, когда я смотрю на эту информацию, а затем на код, я не могу понять, как поток программы может когда-либо достичь этой строки. Код должен был разбиться задолго до того, как попасть в эту линию. Я полностью потерялся здесь.
Начните с соответствующего кода. Это очень маленький код:
// ... code above skipped, not relevant ...
if (data == NULL) return -1;
information = parseData(data);
if (information == NULL) return -1;
/* Check if name has been correctly \0 terminated */
if (information->kind.name->data[information->kind.name->length] != '\0') {
freeParsedData(information);
return -1;
}
/* Copy the name */
realLength = information->kind.name->length + 1;
*result = malloc(realLength);
if (*result == NULL) {
freeParsedData(information);
return -1;
}
strlcpy(*result, (char *)information->kind.name->data, realLength);
// ... code below skipped, not relevant ...
Это уже оно. Он сбой в strlcpy. Я могу сказать вам, как strlcpy действительно вызывается во время выполнения. strlcpy фактически вызывается со следующими параметрами:
strlcpy ( 0x341000, 0x0, 0x1 );
Зная это, довольно очевидно, почему strlcpy падает. Он пытается прочитать один символ из указателя NULL, и это, конечно же, потерпит крах. И поскольку последний параметр имеет значение 1, исходная длина должна быть равна 0. У моего кода явно есть ошибка здесь, он не может проверить, что данные имени имеют значение NULL. Я могу исправить это, никаких проблем.
Мой вопрос:
Как этот код когда-либо попадает в strlcpy?
Почему этот код не сбой в if-statement?
Я попробовал его локально на своей машине:
int main (
int argc,
char ** argv
) {
char * nullString = malloc(10);
free(nullString);
nullString = NULL;
if (nullString[0] != '\0') {
printf("Not terminated\n");
exit(1);
}
printf("Can get past the if-clause\n");
char xxx[10];
strlcpy(xxx, nullString, 1);
return 0;
}
Этот код никогда не передается инструкцией if. Он сбой в выражении if, и это определенно ожидается.
Так может кто-нибудь подумать о любой причине, почему первый код может быть передан, что if-statement без сбоев, если name- > data действительно NULL? Это совершенно таинственно для меня. Это не кажется детерминированным.
Важная дополнительная информация:
Код между двумя комментариями действительно завершен, ничего не осталось. Далее приложение однопоточное, поэтому нет другого потока, который мог бы неожиданно изменить любую память в фоновом режиме. Платформа, где это происходит, - это процессор PPC (G4, в случае, если он может играть какую-либо роль). И в случае, если кто-то задается вопросом о "добром". Это связано с тем, что "информация" содержит "союз" с именем "вид", а имя снова является структурой (вид - это объединение, каждое возможное значение объединения - это другой тип структуры); но здесь все это не имеет никакого значения.
Я благодарен за любую идею здесь. Я еще более благодарен, если это не просто теория, но если есть способ, я могу проверить, что эта теория действительно справедлива для клиента.
Решение
Я уже принял правильный ответ, но на всякий случай кто-то найдет этот вопрос в Google, вот что на самом деле произошло:
Указатели указывали на память, которая уже была освобождена. Освобождение памяти не приведет к нулю или приведет к возврату системы в систему сразу. Поэтому, хотя память была ошибочно освобождена, она содержала правильные значения. Указанный указатель не является NULL во время выполнения "if check".
После этой проверки я выделяю некоторую новую память, вызывая malloc. Не уверен, что именно malloc делает здесь, но каждый вызов malloc или free может иметь далеко идущие последствия для всей динамической памяти виртуального адресного пространства процесса. После вызова malloc указатель фактически равен NULL. Как-то malloc (или какой-то системный вызов malloc использует) нули уже освобожденную память, где находится сам указатель (а не данные, на которые он указывает, сам указатель находится в динамической памяти). Обнуляя эту память, указатель теперь имеет значение 0x0, которое равно NULL в моей системе, и когда вызывается strlcpy, это, конечно, сбой.
Таким образом, реальная ошибка, вызывающая это странное поведение, была в другом месте моего кода. Никогда не забывайте: Освобожденная память сохраняет это значение, но на какое-то время вам не под силу. Чтобы проверить, есть ли у вашего приложения ошибка в доступе к уже освобожденной памяти, просто убедитесь, что освобожденная память всегда обнуляется перед ее освобождением. В OS X вы можете сделать это, установив переменную среды во время выполнения (нет необходимости перекомпилировать что-либо). Конечно, это замедляет программу совсем немного, но вы поймаете эти ошибки намного раньше.