malloc: ошибка двойного освобождения с printf и NULL wchar_t * - PullRequest
1 голос
/ 18 января 2011

Я пытаюсь перенести приложение с Linux на Mac Os X (leopard), но когда я его запускаю, у меня появляется это сообщение об ошибке: malloc: *** error for object 0x100160 : double free.

Я воспроизвел эту проблему скод ниже:

//main.cpp 
#include <stdio.h>
#include <wchar.h>

int main(int argc, char*argv[])
{
    wchar_t *b=NULL;
    printf("a=%ls, b=%ls \n", L"a", b);
}

Скомпилировано с gcc:

gcc main.cpp -o test

Результат выполнения:

a=a, b=(null)
test (5337) malloc: *** error for object 0x100160 : double free
*** set a breakpoint in malloc_error_break to debug

Странно, потому что, если я использую эту строку:printf("a=%ls, b=%ls", b, b), ошибки не печатаются.Кроме того, я не могу использовать wprintf(L"a=%ls, b=%ls", a, b).На Fedora 13 эта программа не печатает никаких ошибок.

Это ошибка printf?Как я могу удалить эту ошибку?

Ответы [ 4 ]

6 голосов
/ 18 января 2011

Вы не можете печатать NULL-указатели в виде строк, это неопределенное поведение.Из стандарта C99, §7.19.6.1 / 8

Спецификаторы преобразования и их значения:
...
s Если нет l длинамодификатор присутствует, аргумент должен быть указателем на начальный элемент массива символьного типа....

Если присутствует модификатор длины l, аргумент должен быть указателем на начальный элемент массива типа wchar_t.

Поскольку указатели NULLявно не разрешены, они запрещены.Вы должны изменить свой код на что-то вроде:

printf("a=%ls, b=%ls \n", L"a", b ? b : L"(null)");
5 голосов
/ 18 января 2011

Это просто ошибка в vprintf_l, которая обрабатывает printf (и, вероятно, всех его друзей).

Строго говоря, библиотека имеет право справиться с этой ситуацией, делая все, что угодно, включая повреждение кучи, но, судя по коду, намерение - как и должно быть для любой не-мусорной реализации printf - - это разумно обращаться с NULL строками. В коде просто есть ошибка, которая делает это. Эти вещи случаются.

Если предпринята попытка напечатать указатель на символ шириной NULL после того, как был напечатан предыдущий указатель на символ шириной не NULL, vprintf_l подхватит следующий строковый аргумент или при выходе из vprintf_l. (Возможно, есть и другие способы, чтобы это произошло - я не проверял.)

Код ошибки здесь:

    case 's':
        if (flags & LONGINT) {
            wchar_t *wcp;

            if (convbuf != NULL)
                free(convbuf);
            if ((wcp = GETARG(wchar_t *)) == NULL)
                cp = "(null)";
            else {
                convbuf = __wcsconv(wcp, prec, loc);
                if (convbuf == NULL) {
                    fp->_flags |= __SERR;
                    goto error;
                }
                cp = convbuf;
            }
        } else if ((cp = GETARG(char *)) == NULL)

Если GETARG(wchar_t *) возвращает NULL, convbuf будет указывать на старый (и теперь освобожденный) буфер. Затем при выходе из функции происходит двойное освобождение:

if (convbuf != NULL)
    free(convbuf);

То же самое применимо, если бы был другой строковый аргумент, хотя в этом случае двойное освобождение происходит в приведенном выше коде case 's':

printf("a=%ls, b=%ls c=%ls\n", L"a", b, L"c");

Конечно, решением было бы установить convbuf на NULL после освобождения.

Код printf здесь:

http://www.opensource.apple.com/source/Libc/Libc-594.1.4/stdio/vfprintf-fbsd.c

Судя по разборке, это код, который используется для среды выполнения по умолчанию в Snow Leopard.

1 голос
/ 18 января 2011

В Ubuntu он печатает a=a, b=(null), но в общем случае не стоит пытаться печатать строки NULL.

0 голосов
/ 18 января 2011

Хотя не рекомендуется передавать указатель NULL в printf, с общей точки зрения я не могу принять ситуацию, когда какая-либо функция (включая printf) реагирует на недопустимый ввод, уничтожая кучу памяти.Поэтому, хотя вы не должны передавать NULL, я считаю это поведение ошибкой в ​​библиотеке.Если функция получает недопустимые аргументы, она может кричать много, но не должна разрушать кучу памяти.

Конечно, иногда невозможно узнать, является ли параметр допустимым, но здесь у нас есть константа NULL и онапросто знать, что на него нельзя ссылаться или что-то в этом роде.

...