-D_FORTIFY_SOURCE=2 的 swprintf 抛出缓冲区溢出
swprintf with -D_FORTIFY_SOURCE=2 throws a buffer overflow
这是我第一次使用 wchar,我发现了一些令人惊讶的事情。我找不到答案所以我会分享我的经验。
我有一个简单的测试程序(基于 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;
}
无论有无优化都可以正常编译:
gcc -O2 test.c -o test
运行 它也可以正常工作:
./test
120
但是在我当前的项目中,我使用 -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
我有更多关于 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
我不明白为什么此检查失败,缓冲区大小对于单个整数来说绰绰有余 (100)。它是 libc 中的错误吗?还是我做错了什么?
我的 GCC 版本是 10.3.1
gcc --version
gcc (GCC) 10.3.1 20210422 (Red Hat 10.3.1-1)
你的问题是函数调用的第二个参数-
swprintf(str_unicode, sizeof(str_unicode), L"%3d\n", 120);
您传入了整个数组的 大小(以字节为单位) - 即如果 sizeof(wchar_t) == 4
.
则为 400 字节
但是 swprintf
的第二个参数是 wchar_t 数组中的单元格数量 - 即您的示例中的 100 个单元格。
将您的函数调用更改为:
swprintf(str_unicode, sizeof(str_unicode) / sizeof(wchar_t), L"%3d\n", 120);
这是我第一次使用 wchar,我发现了一些令人惊讶的事情。我找不到答案所以我会分享我的经验。
我有一个简单的测试程序(基于 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;
}
无论有无优化都可以正常编译:
gcc -O2 test.c -o test
运行 它也可以正常工作:
./test
120
但是在我当前的项目中,我使用 -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
我有更多关于 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
我不明白为什么此检查失败,缓冲区大小对于单个整数来说绰绰有余 (100)。它是 libc 中的错误吗?还是我做错了什么?
我的 GCC 版本是 10.3.1
gcc --version
gcc (GCC) 10.3.1 20210422 (Red Hat 10.3.1-1)
你的问题是函数调用的第二个参数-
swprintf(str_unicode, sizeof(str_unicode), L"%3d\n", 120);
您传入了整个数组的 大小(以字节为单位) - 即如果 sizeof(wchar_t) == 4
.
但是 swprintf
的第二个参数是 wchar_t 数组中的单元格数量 - 即您的示例中的 100 个单元格。
将您的函数调用更改为:
swprintf(str_unicode, sizeof(str_unicode) / sizeof(wchar_t), L"%3d\n", 120);