当我在 clang 中使用 Fuzzer 和 Adresssanitizer 并针对
memcpy
进行测试时,两者都报告堆缓冲区溢出,但我无法解释自己实际上“为什么”。在名为 foo
的虚拟函数中,我创建了一个名为 buf
的缓冲区,大小为 4 个字节。函数 foo 接受一个名为 str
的参数,顾名思义,它应该包含一个字符串。在此示例中,我将字符串“hello”传递给 foo。由于 buf 中只有 4 个字节,我还指示 memcpy 始终只从参数 str 中复制 4 个字节,否则我们会溢出。
但是,为什么 Clang fuzzer/AdressSanitizer 会报告堆溢出?对我来说一切似乎都很好...我从一个较大的字符串(“hello”)仅复制 4 个字节到一个缓冲区(buf),无论如何它只能容纳 4 个字节..
要创建错误,这是源文件
fuzz_string.cpp
:
#include <string>
#include <cstdint>
#include <cstring>
int foo(const char *str)
{
char buf[4];
memcpy(buf, str, sizeof(buf));
return 0;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
foo(reinterpret_cast<const char*>(data));
return 0;
}
编译:
clang++ -g -fsanitize=address,fuzzer fuzz_string.cpp -o fuzz_string
还创建一个名为
corpus/
的子目录,并在其中放置一个名为 input1
的文件,其中包含:
hello
之后我通过运行以下命令来执行程序:
./fuzz_string corpus -max_len=1000
输出:
./fuzz_string corpus -max_len=1000
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 3852592942
INFO: Loaded 1 modules (2 inline 8-bit counters): 2 [0x558e0d7deed0, 0x558e0d7deed2),
INFO: Loaded 1 PC tables (2 PCs): 2 [0x558e0d7deed8,0x558e0d7deef8),
INFO: 1 files found in corpus
=================================================================
==3433==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000011 at pc 0x558e0d79bf20 bp 0x7ffeb3a6b750 sp 0x7ffeb3a6b748
READ of size 1 at 0x602000000011 thread T0
#0 0x558e0d79bf1f in foo(char const*) /mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string.cpp:19:14
#1 0x558e0d79c160 in LLVMFuzzerTestOneInput /mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string.cpp:30:5
#2 0x558e0d6c2323 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string+0x3e323) (BuildId: b5f5fad80b257a1e799c54575adef93091b807c4)
#3 0x558e0d6c3580 in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile> >&) (/mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string+0x3f580) (BuildId: b5f5fad80b257a1e799c54575adef93091b807c4)
#4 0x558e0d6c3bd2 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile> >&) (/mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string+0x3fbd2) (BuildId: b5f5fad80b257a1e799c54575adef93091b807c4)
#5 0x558e0d6b1f22 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string+0x2df22) (BuildId: b5f5fad80b257a1e799c54575adef93091b807c4)
#6 0x558e0d6dbc12 in main (/mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string+0x57c12) (BuildId: b5f5fad80b257a1e799c54575adef93091b807c4)
#7 0x7ff7481b6d8f (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 203de0ae33b53fee1578b117cb4123e85d0534f0)
#8 0x7ff7481b6e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: 203de0ae33b53fee1578b117cb4123e85d0534f0)
#9 0x558e0d6a6964 in _start (/mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string+0x22964) (BuildId: b5f5fad80b257a1e799c54575adef93091b807c4)
0x602000000011 is located 0 bytes to the right of 1-byte region [0x602000000010,0x602000000011)
allocated by thread T0 here:
#0 0x558e0d79985d in operator new[](unsigned long) (/mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string+0x11585d) (BuildId: b5f5fad80b257a1e799c54575adef93091b807c4)
#1 0x558e0d6c2232 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string+0x3e232) (BuildId: b5f5fad80b257a1e799c54575adef93091b807c4)
#2 0x558e0d6c3580 in fuzzer::Fuzzer::ReadAndExecuteSeedCorpora(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile> >&) (/mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string+0x3f580) (BuildId: b5f5fad80b257a1e799c54575adef93091b807c4)
#3 0x558e0d6c3bd2 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile> >&) (/mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string+0x3fbd2) (BuildId: b5f5fad80b257a1e799c54575adef93091b807c4)
#4 0x558e0d6b1f22 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string+0x2df22) (BuildId: b5f5fad80b257a1e799c54575adef93091b807c4)
#5 0x558e0d6dbc12 in main (/mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string+0x57c12) (BuildId: b5f5fad80b257a1e799c54575adef93091b807c4)
#6 0x7ff7481b6d8f (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 203de0ae33b53fee1578b117cb4123e85d0534f0)
SUMMARY: AddressSanitizer: heap-buffer-overflow /mnt/c/Users/masch/Documents/Workbench/C/nexus-src/fuzz_string.cpp:19:14 in foo(char const*)
Shadow bytes around the buggy address:
0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa[01]fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==3433==ABORTING
MS: 0 ; base unit: 0000000000000000000000000000000000000000
artifact_prefix='./'; Test unit written to ./crash-da39a3ee5e6b4b0d3255bfef95601890afd80709
Base64:
我尝试在谷歌上搜索示例或解释,但不幸的是,没有人能帮助我。这个链接让我对这个主题感到好奇,但无法正确解释为什么当我们仅将 4 字节复制到 4 字节目标时会导致堆溢出。
如果 strlen(str) < 3. I suggest you use
strncpy()
则代码将会溢出,并确保 buf
如果您需要它是一个字符串,则以“ ”结尾。
if(str)
memcpy(buf, str, strlen(str) < 3 ? strtlen(str) + 1 : sizeof(buf));
如果
str
不是字符串(给定调用者传入 size_t
),则无法在 foo()
中修复此问题。