简体   繁体   English

带有 -D_FORTIFY_SOURCE=2 的 swprintf 会引发缓冲区溢出

[英]swprintf with -D_FORTIFY_SOURCE=2 throws a buffer overflow

This is the first time I work with wchar and I found something surprising about it.这是我第一次使用 wchar 并且我发现了一些令人惊讶的地方。 I can't find the answer so I will share my experiences.我找不到答案,所以我将分享我的经验。

I have a simple test program (based on a swprintf example)我有一个简单的测试程序(基于 swprintf 示例)

#include <stdio.h>
#include <wchar.h>

int main()
{
    wchar_t str_unicode[100] = {0};

    swprintf(str_unicode, sizeof(str_unicode), L"%3d\n", 120);

    fputws(str_unicode, stdout);

    return 0;
}

Compiling it with or without optimization works fine:无论有没有优化,编译它都可以正常工作:

gcc -O2 test.c -o test

Running it also works fine:运行它也可以正常工作:

./test
120

But in my current project, I use -D_FORTIFY_SOURCE=2 , and it makes this simple program crash:但是在我当前的项目中,我使用-D_FORTIFY_SOURCE=2 ,它使这个简单的程序崩溃:

gcc -O2 -D_FORTIFY_SOURCE=2 test.c -o test
./test 
*** buffer overflow detected ***: terminated
[1]    28569 IOT instruction (core dumped)  ./test

I have more details with valgrind, and it seems that __swprintf_chk fails.我有更多关于 valgrind 的细节,似乎__swprintf_chk失败了。

valgrind ./test    
==30068== Memcheck, a memory error detector
==30068== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==30068== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==30068== Command: ./test
==30068== 
*** buffer overflow detected ***: terminated
==30068== 
==30068== Process terminating with default action of signal 6 (SIGABRT): dumping core
==30068==    at 0x48A29E5: raise (in /usr/lib64/libc-2.32.so)
==30068==    by 0x488B8A3: abort (in /usr/lib64/libc-2.32.so)
==30068==    by 0x48E5006: __libc_message (in /usr/lib64/libc-2.32.so)
==30068==    by 0x4975DF9: __fortify_fail (in /usr/lib64/libc-2.32.so)
==30068==    by 0x4974695: __chk_fail (in /usr/lib64/libc-2.32.so)
==30068==    by 0x49752C4: __swprintf_chk (in /usr/lib64/libc-2.32.so)
==30068==    by 0x401086: main (in /home/pierre/workdir/test)
==30068== 
==30068== HEAP SUMMARY:
==30068==     in use at exit: 0 bytes in 0 blocks
==30068==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==30068== 
==30068== All heap blocks were freed -- no leaks are possible
==30068== 
==30068== For lists of detected and suppressed errors, rerun with: -s
==30068== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
[1]    30068 IOT instruction (core dumped)  valgrind ./test

I don't understand why this check fails, the buffer size is more than enough (100) for a single integer.我不明白为什么这个检查失败,缓冲区大小对于单个整数来说已经足够了(100)。 Is it a bug in libc?这是libc中的错误吗? Or did I do something wrong?还是我做错了什么?

My GCC version is 10.3.1我的 GCC 版本是 10.3.1

gcc --version
gcc (GCC) 10.3.1 20210422 (Red Hat 10.3.1-1)

Your problem is the second parameter of the function call -你的问题是函数调用的第二个参数 -

swprintf(str_unicode, sizeof(str_unicode), L"%3d\\n", 120);

You passed in the size in bytes of the entire array - ie 400 bytes if sizeof(wchar_t) == 4 .您传入了整个数组的字节sizeof(wchar_t) == 4 - 即如果sizeof(wchar_t) == 4则为 400 字节。

But swprintf 's second parameter is the number of cells in the wchar_t array - ie 100 cells in your example.但是swprintf的第二个参数是wchar_t 数组中的单元格数- 即在您的示例中为 100 个单元格。

Change your function call to:将您的函数调用更改为:

swprintf(str_unicode, sizeof(str_unicode) / sizeof(wchar_t), L"%3d\\n", 120);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM