-
Notifications
You must be signed in to change notification settings - Fork 725
Open
Milestone
Description
Hi, there's a minor issue with how chunk IDs are logged in ps_config_wavfile
.
Specifically, it uses a %s
format specifier for a non-null-terminated char[4]
containing the ID.
With a specially crafted WAV file, it's possible to hit this path.
A safer alternative would be %c%c%c%c
, passing in each of the 4 chars individually.
(found via automated fuzzing)
See the following testcase which reproduces the bug (and associated ASAN report of the overflow):
testcase.cpp
#include <cstdio>
#include <cstdint>
#include <cstring>
extern "C" {
#include "/fuzz/install/include/pocketsphinx.h"
}
// Helper to write a 32-bit little-endian value
static void w32le(FILE* f, uint32_t v){
unsigned char b[4];
b[0]=v&0xff;
b[1]=(v>>8)&0xff;
b[2]=(v>>16)&0xff;
b[3]=(v>>24)&0xff;
fwrite(b,1,4,f);
}
int main(){
// Initialize config
ps_config_t *cfg = ps_config_init(NULL);
if (!cfg) return 0;
// Create a deliberately malformed WAV-like file that will
// cause ps_config_wavfile() to log a 4-byte chunk ID.
const char *path = "/tmp/repro.wav";
FILE *f = fopen(path, "wb");
// RIFF header
fwrite("RIFF",1,4,f);
w32le(f, 2000); // arbitrary size
fwrite("WAVE",1,4,f);
// fmt chunk (minimal plausible PCM fmt)
fwrite("fmt ",1,4,f);
w32le(f, 16);
unsigned char fmt[16] = {
1,0, // PCM
1,0, // mono
0x80,0x3E,0,0, // sample rate 16000
0,0x7D,0,0, // byte rate (deliberately inconsistent values are okay)
2,0, // block align
16,0 // bits per sample
};
fwrite(fmt,1,16,f);
// An unknown chunk with an 8-byte size but no payload to force an error
// in which the 4-byte ID is logged as a string without NUL termination.
// This triggers the stack-buffer-overflow in the logging path.
fwrite("JUNK",1,4,f); // 4-byte ID with no terminating NUL
w32le(f, 8); // claims 8 bytes follow, but we don't write them
// EOF here
fclose(f);
// Now invoke ps_config_wavfile
FILE *infh = fopen(path, "rb");
if (!infh) return 0;
ps_config_wavfile(cfg, infh, path);
fclose(infh);
return 0;
}
crash report
==12==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fd7bef010a4 at pc 0x55715d3165e2 bp 0x7ffdb7784340 sp 0x7ffdb7783ac8
READ of size 5 at 0x7fd7bef010a4 thread T0
#0 0x55715d3165e1 in printf_common(void*, char const*, __va_list_tag*) asan_interceptors.cpp.o
#1 0x55715d316979 in vsnprintf (/fuzz/workspace/test+0x59979) (BuildId: 79b35302799e787e859ed5ba0c888fa363c5eeb8)
#2 0x55715d3eee47 in err_msg_system /fuzz/src/src/util/err.c:196:5
#3 0x55715d3f2c7f in ps_config_wavfile /fuzz/src/src/util/soundfiles.c:209:17
#4 0x55715d3cd695 in main /fuzz/workspace/test.cpp:56:5
#5 0x7fd7c0c83d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#6 0x7fd7c0c83e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#7 0x55715d2f2314 in _start (/fuzz/workspace/test+0x35314) (BuildId: 79b35302799e787e859ed5ba0c888fa363c5eeb8)
Address 0x7fd7bef010a4 is located in stack of thread T0 at offset 36 in frame
#0 0x55715d3f21ff in ps_config_wavfile /fuzz/src/src/util/soundfiles.c:101
This frame has 3 object(s):
[32, 36) 'id' (line 102) <== Memory access at offset 36 overflows this variable
[48, 52) 'intval' (line 103)
[64, 66) 'shortval' (line 104)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow asan_interceptors.cpp.o in printf_common(void*, char const*, __va_list_tag*)
Shadow bytes around the buggy address:
0x7fd7bef00e00: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7fd7bef00e80: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7fd7bef00f00: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7fd7bef00f80: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
0x7fd7bef01000: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
=>0x7fd7bef01080: f1 f1 f1 f1[04]f2 04 f2 02 f3 f3 f3 00 00 00 00
0x7fd7bef01100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7fd7bef01180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7fd7bef01200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7fd7bef01280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7fd7bef01300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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
Metadata
Metadata
Assignees
Labels
No labels