开发者

malloc : error double free with printf and a NULL wchar_t*

开发者 https://www.devze.com 2023-02-05 03:09 出处:网络
I\'m trying to port an application from Linux to Mac Os X (leopard), but when I execute it, I have this error message : malloc: *** error for object 0x100160 : double free.

I'm trying to port an application from Linux to Mac Os X (leopard), but when I execute it, I have this error message : malloc: *** error for object 0x100160 : double free.

I've reproduced this issue with the code below :

//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);
}

Compiled with gcc:

gcc main.cpp -o test

The out开发者_Go百科put of the execution:

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

It's strange because if I use this line : printf("a=%ls, b=%ls", b, b), no error are printed. Furthermore, I can't use wprintf(L"a=%ls, b=%ls", a, b). On Fedora 13, this program don't print any error.

Is it a printf bug? How can I remove this error?


You cannot print NULL pointers as strings, that's undefined behavior. From the C99 standard, §7.19.6.1/8

The conversion specifiers and their meanings are:
...
s     If no l length modifier is present, the argument shall be a pointer to the initial element of an array of character type. ...

If an l length modifier is present, the argument shall be a pointer to the initial element of an array of wchar_t type.

Since NULL pointers are not explicitly allowed, they are disallowed. You should change your code to something like:

printf("a=%ls, b=%ls \n", L"a", b ? b : L"(null)");


This is simply a bug in vprintf_l, which handles printf (and presumably all its friends).

Strictly speaking, the library is within its rights to handle this situation by doing whatever it likes, heap corruption included, but judging by the code the intent -- as should be the case for any non-rubbish printf implementation -- is to handle NULL strings sensibly. There is simply a bug in the code that does this. These things happen.

If an attempt is made to print a NULL wide char pointer after a previous non-NULL wide char pointer was printed, vprintf_l will cock up on the next string argument, or when vprintf_l exits. (There may be other ways to get this to happen, too, I suppose - I didn't check.)

The offending code is here:

    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)

If GETARG(wchar_t *) returns NULL, convbuf will point to the old (and now freed) buffer. Then, when the function exits, there is a double free:

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

The same would apply were there another string argument, though in this case the double free happens in the case 's' code above:

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

The solution of course would be to set convbuf to NULL after it is freed.

The printf code is here:

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

Judging by the disassembly, it is the code that is used for the default runtime in Snow Leopard.


On Ubuntu it prints a=a, b=(null), but in general it is not a good idea to try to print NULL strings.


Although it is not a good practice to pass a NULL pointer to printf, from generic point of view I cannot accept a situation when any function (including printf) reacts to an invalid input by destroying memory heap. So although you should not pass a NULL there I consider this behavior as a bug in library. If a function gets invalid arguments it can scream a lot but is not supposed to destroy memory heap.

Of course sometimes it is not possible to know if a parameter is valid, but here we have a NULL constant and it is simple to know that it cannot be dereferenced or something.

0

精彩评论

暂无评论...
验证码 换一张
取 消