malloc :错误双重释放 printf 和 NULL wchar_t*

发布于 2024-10-12 14:44:49 字数 761 浏览 2 评论 0原文

我正在尝试将应用程序从 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 错误吗? 我怎样才能消除这个错误?

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 output 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

咽泪装欢 2024-10-19 14:44:49

您不能将 NULL 指针打印为字符串,这是未定义的行为。来自 C99 标准,§7.19.6.1/8

转换说明符及其含义是:
...
s    如果不存在 l 长度修饰符,则参数应为指向字符类型数组的初始元素的指针。 ...

如果存在 l 长度修饰符,则参数应为指向初始值的指针
wchar_t 类型数组的元素。

由于未明确允许 NULL 指针,因此不允许使用 NULL 指针。您应该将代码更改为:

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

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)");
紫竹語嫣☆ 2024-10-19 14:44:49

这只是 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)

If GETARG(wchar_t *) returns NULL code>, 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 中的默认运行时。

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.

烟织青萝梦 2024-10-19 14:44:49

在 Ubuntu 上,它打印 a=a, b=(null),但一般来说,尝试打印 NULL 字符串并不是一个好主意。

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

此岸叶落 2024-10-19 14:44:49

尽管将 NULL 指针传递给 printf 不是一个好习惯,但从一般角度来看,我无法接受任何函数(包括 printf)通过破坏内存堆对无效输入做出反应的情况。因此,尽管您不应该在那里传递 NULL,但我认为这种行为是库中的错误。如果一个函数获得无效参数,它可能会尖叫很多,但不应该破坏内存堆。

当然,有时不可能知道参数是否有效,但这里我们有一个 NULL 常量,很容易知道它不能被取消引用或其他什么。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文