-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Open
Description
Hello,
I encountered a crash while performing my fuzzing tasks, which prompted me to leave this message.
have a good day :)
📌 Main Information
Project | hiredis |
---|---|
Fuzzer | libfuzzer |
Fuzz binary | format_command_fuzzer |
Sanitizer | asan |
Crash Type | Heap-buffer-overflow |
Crash State | sdscatlen in /src/hiredis/sds.c:386:5 |
=================================================================
==643==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000000145 at pc 0x00000052e371 bp 0x7fffffffd7d0 sp 0x7fffffffcfa0
READ of size 260 at 0x603000000145 thread T0
SCARINESS: 26 (multi-byte-read-heap-buffer-overflow)
[Detaching after fork from child process 645]
#0 0x52e370 in __asan_memcpy /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3
#1 0x57ab16 in sdscatlen /src/hiredis/sds.c:386:5
#2 0x56d2de in redisvFormatCommand /src/hiredis/hiredis.c
#3 0x56f2cd in redisFormatCommand /src/hiredis/hiredis.c:569:11
#4 0x56c53e in LLVMFuzzerTestOneInput /src/hiredis/format_command_fuzzer.c:51:9
#5 0x43de53 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:611:15
#6 0x4295b2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
#7 0x42ee5c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:860:9
#8 0x458392 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
#9 0x7ffff7c58082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
#10 0x41f77d in _start (/out/hiredis/format_command_fuzzer+0x41f77d)
DEDUP_TOKEN: __asan_memcpy--sdscatlen--redisvFormatCommand
0x603000000145 is located 0 bytes to the right of 21-byte region [0x603000000130,0x603000000145)
allocated by thread T0 here:
#0 0x52efe6 in __interceptor_malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:69:3
#1 0x56c4f0 in LLVMFuzzerTestOneInput /src/hiredis/format_command_fuzzer.c:44:15
#2 0x43de53 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:611:15
#3 0x4295b2 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:324:6
#4 0x42ee5c in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:860:9
#5 0x458392 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
#6 0x7ffff7c58082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
DEDUP_TOKEN: __interceptor_malloc--LLVMFuzzerTestOneInput--fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long)
SUMMARY: AddressSanitizer: heap-buffer-overflow /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3 in __asan_memcpy
Shadow bytes around the buggy address:
0x0c067fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff8000: fa fa 00 00 00 fa fa fa 00 00 00 fa fa fa 00 00
0x0c067fff8010: 00 fa fa fa 00 00 00 00 fa fa 00 00 04 fa fa fa
=>0x0c067fff8020: 00 00 04 fa fa fa 00 00[05]fa fa fa fa fa fa fa
0x0c067fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8070: 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
==643==ABORTING
[Thread 0x7ffff40f9700 (LWP 644) exited]
[Inferior 1 (process 643) exited with code 01]
🎯 Fuzz target Information
format_command_fuzzer.c
#include <stdlib.h> #include <string.h> #include "hiredis.h"
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
char *new_str, *cmd;if (size < 3) return 0; new_str = malloc(size+1); if (new_str == NULL) return 0; memcpy(new_str, data, size); new_str[size] = '\\0'; if (redisFormatCommand(&cmd, new_str) != -1) hi_free(cmd); free(new_str); return 0;
}
- Crash data
24 68 78 25 25 25 62 22 20 22 20 25 08 01 20 01 20 81 68 78
🧐 Analysis
- The function is invoked in the following order with the input data
$hx%%%b…
:$
→newarg = sdscatlen(curarg, c, 1);
h
→newarg = sdscatlen(curarg, c, 1);
x
→newarg = sdscatlen(curarg, c, 1);
%%
→newarg = sdscat(curarg, "%");
%b
→newarg = sdscatlen(curarg, arg, size);
- The crash occurs in the
memcpy
inside the lastsdscatlen
call, and the cause is that thesize
value is larger than the length of the input data. - The
size
value is initialized during thesize = va_arg(ap, size_t);
step before thesdscatlen
function is called.
sds sdscatlen(sds s, const void *t, size_t len) { size_t curlen = sdslen(s);
s = sdsMakeRoomFor(s,len); if (s == NULL) return NULL; memcpy(s+curlen, t, len); // [*] -- crash state sdssetlen(s, curlen+len); s[curlen+len] = '\\0'; return s;
}
int redisvFormatCommand(char **target, const char *format, va_list ap) {
/* skip */
while(*c != '\\0') {
if (*c != '%' || c[1] == '\\0') {
if (*c == ' ') {
if (touched) {
newargv = hi_realloc(curargv,sizeof(char*)*(argc+1));
if (newargv == NULL) goto memory_err;
curargv = newargv;
curargv[argc++] = curarg;
totlen += bulklen(sdslen(curarg));
/* curarg is put in argv so it can be overwritten. */
curarg = sdsempty();
if (curarg == NULL) goto memory_err;
touched = 0;
}
} else {
newarg = sdscatlen(curarg,c,1);
if (newarg == NULL) goto memory_err;
curarg = newarg;
touched = 1;
}
} else {
char *arg;
size_t size;
/* Set newarg so it can be checked even if it is not touched. */
newarg = curarg;
switch(c[1]) {
case 's':
arg = va_arg(ap,char*);
size = strlen(arg);
if (size > 0)
newarg = sdscatlen(curarg,arg,size);
break;
case 'b':
arg = va_arg(ap,char*);
size = va_arg(ap,size_t);
if (size > 0)
newarg = sdscatlen(curarg,arg,size); // [*] -- crash func call
break;
case '%':
newarg = sdscat(curarg,"%");
break;
/* skip */
}
- If a larger
memcpy
occurs than the size of the input data, it is expected that arbitrary data will be copied into the buffer.
Metadata
Metadata
Assignees
Labels
No labels