BrotliDecoderDecompress() crashed if inbuf is part of mmaped file #231

Closed
opened 2026-01-29 20:40:19 +00:00 by claunia · 20 comments
Owner

Originally created by @torappinfo on GitHub (May 30, 2018).

If data points to mmaped file, the following crashed:
BrotliDecoderDecompress(len,data+offset,&decoded_size,inBuffer);

Adding extra copy fixed the issue:
memcpy(testbuf,data+offset,len);
BrotliDecoderDecompress(len,testbuf,&decoded_size,inBuffer);

Note: in the first case, data+offset is not aligned. Is alignment needed for BrotliDecoderDecompress()?

Originally created by @torappinfo on GitHub (May 30, 2018). If data points to mmaped file, the following crashed: BrotliDecoderDecompress(len,data+offset,&decoded_size,inBuffer); Adding extra copy fixed the issue: memcpy(testbuf,data+offset,len); BrotliDecoderDecompress(len,testbuf,&decoded_size,inBuffer); Note: in the first case, data+offset is not aligned. Is alignment needed for BrotliDecoderDecompress()?
Author
Owner

@eustas commented on GitHub (May 30, 2018):

What is the OS / CPU?
Actually, decoder takes care of alignment: f9b8c02673/c/dec/bit_reader.c (L37)

Going to investigate the problem soon.

@eustas commented on GitHub (May 30, 2018): What is the OS / CPU? Actually, decoder takes care of alignment: https://github.com/google/brotli/blob/f9b8c02673c576a3e807edbf3a9328e9e7af6d7c/c/dec/bit_reader.c#L37 Going to investigate the problem soon.
Author
Owner

@torappinfo commented on GitHub (Jun 2, 2018):

Linux 4.16.6-1-ARCH #1 SMP PREEMPT Mon Apr 30 12:30:03 UTC 2018 x86_64 GNU/Linux

Name : brotli
Version : 1.0.4-1
Description : Brotli compression library
Architecture : x86_64

@torappinfo commented on GitHub (Jun 2, 2018): Linux 4.16.6-1-ARCH #1 SMP PREEMPT Mon Apr 30 12:30:03 UTC 2018 x86_64 GNU/Linux Name : brotli Version : 1.0.4-1 Description : Brotli compression library Architecture : x86_64
Author
Owner

@torappinfo commented on GitHub (Jun 2, 2018):

If alignment is helpful for performance, this bug needs not to be fixed. We just need to document it.

@torappinfo commented on GitHub (Jun 2, 2018): If alignment is helpful for performance, this bug needs not to be fixed. We just need to document it.
Author
Owner

@eustas commented on GitHub (Jun 5, 2018):

Can't reproduce.
If you like, I can share modified research/brotli_decoder.c which reads from mmapped file from given offset, so you could check if it crashes as well.

@eustas commented on GitHub (Jun 5, 2018): Can't reproduce. If you like, I can share modified `research/brotli_decoder.c` which reads from mmapped file from given offset, so you could check if it crashes as well.
Author
Owner

@jhudsoncedaron commented on GitHub (Jun 15, 2018):

I can give a clue how to try to reproduce this. I think the code tries to read past the end of the buffer and ignores the result. This will crash if the buffer is butt-up against the end of a page and the next page isn't allocated. This is easy to set up with a mmapped file and hard otherwise.

@jhudsoncedaron commented on GitHub (Jun 15, 2018): I can give a clue how to try to reproduce this. I think the code tries to read past the end of the buffer and ignores the result. This will crash if the buffer is butt-up against the end of a page and the next page isn't allocated. This is easy to set up with a mmapped file and hard otherwise.
Author
Owner

@eustas commented on GitHub (Jun 19, 2018):

I've published branch with demo mmaped file decoder: https://github.com/google/brotli/tree/mmap-read
Could you provide file and offeset that would crash it, please.

@eustas commented on GitHub (Jun 19, 2018): I've published branch with demo mmaped file decoder: https://github.com/google/brotli/tree/mmap-read Could you provide file and offeset that would crash it, please.
Author
Owner

@torappinfo commented on GitHub (Jul 27, 2018):

I'v compressed every 64k block of a large file with brotli and then concatenate them together. The compressed file has a header to provide offsets for each blocks.
The header format is as follows:

struct CONTENT_LOC {
  byte offset[5]; //Big endian/network order 5-byte integer 
};

struct HEADER {
  byte flags;
  CONTENT_LOC len_decompressed; //length of original uncompressed file
  CONTENT_LOC blocks[]; //offsets for each blocks, CONTENT_LOC[nBlocks+1] 
};

Here nBlocks=(len_decompressed-1)/64k + 1.
and the offset for the first block is zero, and second offset is the length of compressed first block etc.

In this format, BrotliDecoderDecompress() crashed for the very first block for my testing data.

The first 1MB of compressed file is attached here.
test.log

@torappinfo commented on GitHub (Jul 27, 2018): I'v compressed every 64k block of a large file with brotli and then concatenate them together. The compressed file has a header to provide offsets for each blocks. The header format is as follows: ``` struct CONTENT_LOC { byte offset[5]; //Big endian/network order 5-byte integer }; struct HEADER { byte flags; CONTENT_LOC len_decompressed; //length of original uncompressed file CONTENT_LOC blocks[]; //offsets for each blocks, CONTENT_LOC[nBlocks+1] }; ``` Here nBlocks=(len_decompressed-1)/64k + 1. and the offset for the first block is zero, and second offset is the length of compressed first block etc. In this format, BrotliDecoderDecompress() crashed for the very first block for my testing data. The first 1MB of compressed file is attached here. [test.log](https://github.com/google/brotli/files/2236017/test.log)
Author
Owner

@torappinfo commented on GitHub (Jul 27, 2018):

The full compressed file (15MB) is here: (ahd.dict.tbr)
https://github.com/torappinfo/century-dictionary/releases/tag/v1

@torappinfo commented on GitHub (Jul 27, 2018): The full compressed file (15MB) is here: (ahd.dict.tbr) https://github.com/torappinfo/century-dictionary/releases/tag/v1
Author
Owner

@mraszyk commented on GitHub (Jul 30, 2018):

I managed to successfully decompress the test file, i.e., I was not able to reproduce the crash.
Moreover, the uncompressed file (ahd.dict) has SHA256 hash
2f4197eab677a9344f3b4879fc5a65dcabb4b0b3df1497d50580936efde9a24b
which is different from century.md with SHA256 hash
aac17eb82496ba106cb2161da8016f3580ba23593e224b71fadb2a99d9005416

@mraszyk commented on GitHub (Jul 30, 2018): I managed to successfully decompress the test file, i.e., I was not able to reproduce the crash. Moreover, the uncompressed file (ahd.dict) has SHA256 hash 2f4197eab677a9344f3b4879fc5a65dcabb4b0b3df1497d50580936efde9a24b which is different from century.md with SHA256 hash aac17eb82496ba106cb2161da8016f3580ba23593e224b71fadb2a99d9005416
Author
Owner

@torappinfo commented on GitHub (Jul 31, 2018):

The file is not century.md. I just put there as a test file, so the SHA256 is different.
When you decompressed the test file, did you mmap the whole test file (at offset 0)
and use BrotliDecoderDecompress(len,data+offset,&decoded_size,inBuffer) directly on it?

If you read the test file to a buffer, the data is probably properly aligned and nothing will be wrong.
But if you mmap the whole test file (including header) and directly use BrotliDecoderDecompress() on the address space. It crashed me every time.

My latest test is using :
Linux 4.17.2-1-ARCH #1 SMP PREEMPT UTC 2018 x86_64 GNU/Linux
Name : brotli
Version : 1.0.5-1
Description : Brotli compression library
Architecture : x86_64

@torappinfo commented on GitHub (Jul 31, 2018): The file is not century.md. I just put there as a test file, so the SHA256 is different. When you decompressed the test file, did you mmap the whole test file (at offset 0) and use BrotliDecoderDecompress(len,data+offset,&decoded_size,inBuffer) directly on it? If you read the test file to a buffer, the data is probably properly aligned and nothing will be wrong. But if you mmap the whole test file (including header) and directly use BrotliDecoderDecompress() on the address space. It crashed me every time. My latest test is using : Linux 4.17.2-1-ARCH #1 SMP PREEMPT UTC 2018 x86_64 GNU/Linux Name : brotli Version : 1.0.5-1 Description : Brotli compression library Architecture : x86_64
Author
Owner

@jhudsoncedaron commented on GitHub (Aug 3, 2018):

@torappinfo : I'd recommending extracting out and uploading the two dozen lines required to set this up. I only ever got this with corrupted streams...

@jhudsoncedaron commented on GitHub (Aug 3, 2018): @torappinfo : I'd recommending extracting out and uploading the two dozen lines required to set this up. I only ever got this with corrupted streams...
Author
Owner

@torappinfo commented on GitHub (Aug 6, 2018):

@mraszyk, Could you give your code to jhudsoncedaron, as you have already successfully extracted streams. The only thing to make sure segment fault is not to read compressed stream to buffer.

@torappinfo commented on GitHub (Aug 6, 2018): @mraszyk, Could you give your code to jhudsoncedaron, as you have already successfully extracted streams. The only thing to make sure segment fault is not to read compressed stream to buffer.
Author
Owner

@mraszyk commented on GitHub (Aug 6, 2018):

I used the following code to decompress the test file ahd.dict.tbr and successfully obtained the uncompressed file ahd.dict also on Arch Linux:
https://drive.google.com/file/d/0ByXI7DhQRNE-a2t4bUJKTE54emR6V0pZMFV2eklpLTdlS0JF/view?usp=sharing

Files (with SHA256 hashes):
adbe6fa730efddbcf62a75134ece9ab8a50abf4465ed38af6de7898b11f07061 ahd.dict.tbr
2f4197eab677a9344f3b4879fc5a65dcabb4b0b3df1497d50580936efde9a24b ahd.dict

System: Linux arch 4.17.11-arch1 #1 SMP PREEMPT x86_64 GNU/Linux
Brotli version: 1.0.5

Please check the attached code if it crashes, and if it doesn't, please provide your code which does crash on decompressing ahd.dict.tbr.

@mraszyk commented on GitHub (Aug 6, 2018): I used the following code to decompress the test file ahd.dict.tbr and successfully obtained the uncompressed file ahd.dict also on Arch Linux: https://drive.google.com/file/d/0ByXI7DhQRNE-a2t4bUJKTE54emR6V0pZMFV2eklpLTdlS0JF/view?usp=sharing Files (with SHA256 hashes): adbe6fa730efddbcf62a75134ece9ab8a50abf4465ed38af6de7898b11f07061 ahd.dict.tbr 2f4197eab677a9344f3b4879fc5a65dcabb4b0b3df1497d50580936efde9a24b ahd.dict System: Linux arch 4.17.11-arch1 #1 SMP PREEMPT x86_64 GNU/Linux Brotli version: 1.0.5 Please check the attached code if it crashes, and if it doesn't, please provide your code which does crash on decompressing ahd.dict.tbr.
Author
Owner

@jhudsoncedaron commented on GitHub (Aug 6, 2018):

@torappinfo : What are your FS and OS page sizes? (The FS page size is the size of an empty directory assuming you aren't using an exotic filesystem.) Your processor supports large pages but I can't tell if you're using them.

@jhudsoncedaron commented on GitHub (Aug 6, 2018): @torappinfo : What are your FS and OS page sizes? (The FS page size is the size of an empty directory assuming you aren't using an exotic filesystem.) Your processor supports large pages but I can't tell if you're using them.
Author
Owner

@torappinfo commented on GitHub (Aug 6, 2018):

My FS and OS page sizes are both 4KB.

@torappinfo commented on GitHub (Aug 6, 2018): My FS and OS page sizes are both 4KB.
Author
Owner

@torappinfo commented on GitHub (Aug 6, 2018):

mraszyk's code runs successfully on my machine.

@torappinfo commented on GitHub (Aug 6, 2018): mraszyk's code runs successfully on my machine.
Author
Owner

@jhudsoncedaron commented on GitHub (Aug 6, 2018):

I was expecting that. Your code please.

@jhudsoncedaron commented on GitHub (Aug 6, 2018): I was expecting that. Your code please.
Author
Owner

@torappinfo commented on GitHub (Aug 8, 2018):

The segment fault may be inside my code, since simply declaring a global variable fixed the crash:
unsigned char ub1 testbuf[64*1024];

Even though the coredump stack is as follows:
#0 0x00007ffff782bb5f in raise () from /usr/lib/libc.so.6
#1 0x00007ffff7816452 in abort () from /usr/lib/libc.so.6
#2 0x00007ffff786e658 in __libc_message () from /usr/lib/libc.so.6
#3 0x00007ffff7874f6a in malloc_printerr () from /usr/lib/libc.so.6
#4 0x00007ffff78754c4 in munmap_chunk () from /usr/lib/libc.so.6
#5 0x00007ffff7b44a42 in ?? () from /usr/lib/libbrotlidec.so.1
#6 0x00007ffff7b43548 in BrotliDecoderDecompressStream () from /usr/lib/libbrotlidec.so.1
#7 0x00007ffff7b43bac in BrotliDecoderDecompress () from /usr/lib/libbrotlidec.so.1

@torappinfo commented on GitHub (Aug 8, 2018): The segment fault may be inside my code, since simply declaring a global variable fixed the crash: unsigned char ub1 testbuf[64*1024]; Even though the coredump stack is as follows: #0 0x00007ffff782bb5f in raise () from /usr/lib/libc.so.6 #1 0x00007ffff7816452 in abort () from /usr/lib/libc.so.6 #2 0x00007ffff786e658 in __libc_message () from /usr/lib/libc.so.6 #3 0x00007ffff7874f6a in malloc_printerr () from /usr/lib/libc.so.6 #4 0x00007ffff78754c4 in munmap_chunk () from /usr/lib/libc.so.6 #5 0x00007ffff7b44a42 in ?? () from /usr/lib/libbrotlidec.so.1 #6 0x00007ffff7b43548 in BrotliDecoderDecompressStream () from /usr/lib/libbrotlidec.so.1 #7 0x00007ffff7b43bac in BrotliDecoderDecompress () from /usr/lib/libbrotlidec.so.1
Author
Owner

@jhudsoncedaron commented on GitHub (Aug 8, 2018):

You have heap corruption. It could be anywhere in your code.

@jhudsoncedaron commented on GitHub (Aug 8, 2018): You have heap corruption. It could be anywhere in your code.
Author
Owner

@torappinfo commented on GitHub (Aug 28, 2018):

Very weird bug. I decompressed the first block (BrotliDecoderDecompress) to the first cache of the following cache array:
struct dictCACHE {
ub1 cache[65536][DICT_CACHE_SIZE];
//ub1 testbuf[65536]; //this line fixed segment fault
ub4 chunk[DICT_CACHE_SIZE];//index for 64k chunks, (ub4)-1 means invalid
ub4 stamp[DICT_CACHE_SIZE];
};
Whatever DICT_CACHE_SIZE is, without testbuf defined, BrotliDecoderDecompress segment faulted for sure. And even if the testbuf is defined, it is never write-accessed in run (I made the testbuf page read-only, and it never crashed.)
The Valgrind error is as follows:
==593== Memcheck, a memory error detector
==593== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==593== Using Valgrind-3.14.0.GIT and LibVEX; rerun with -h for copyright info
==593== Command: ./tdict_tbr /home/fengcao/dict/ahd hello
==593==
==593== Invalid write of size 8
==593== at 0x483C02B: memcpy@GLIBC_2.2.5 (vg_replace_strmem.c:1033)
==593== by 0x4ACB489: ??? (in /usr/lib/libbrotlidec.so.1.0.5)
==593== by 0x4ACFFBA: BrotliDecoderDecompressStream (in /usr/lib/libbrotlidec.so.1.0.5)
==593== by 0x4AD0BAB: BrotliDecoderDecompress (in /usr/lib/libbrotlidec.so.1.0.5)
==593== by 0x10A98C: content (getcontent_tbr.c:122)
==593== by 0x10B6D7: GetContent (tdict_le.c:187)
==593== by 0x10D3FE: tdict_main (tdictmain.c:261)
==593== by 0x10AB35: main (getcontent_tbr.c:156)
==593== Address 0x121000 is not stack'd, malloc'd or (recently) free'd
==593==
==593==
==593== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==593== Access not within mapped region at address 0x121000
==593== at 0x483C02B: memcpy@GLIBC_2.2.5 (vg_replace_strmem.c:1033)
==593== by 0x4ACB489: ??? (in /usr/lib/libbrotlidec.so.1.0.5)
==593== by 0x4ACFFBA: BrotliDecoderDecompressStream (in /usr/lib/libbrotlidec.so.1.0.5)
==593== by 0x4AD0BAB: BrotliDecoderDecompress (in /usr/lib/libbrotlidec.so.1.0.5)
==593== by 0x10A98C: content (getcontent_tbr.c:122)
==593== by 0x10B6D7: GetContent (tdict_le.c:187)
==593== by 0x10D3FE: tdict_main (tdictmain.c:261)
==593== by 0x10AB35: main (getcontent_tbr.c:156)
==593== If you believe this happened as a result of a stack
==593== overflow in your program's main thread (unlikely but
==593== possible), you can try to increase the size of the
==593== main thread stack using the --main-stacksize= flag.
==593== The main thread stack size used in this run was 8388608.
==593==
==593== HEAP SUMMARY:
==593== in use at exit: 110,457 bytes in 8 blocks
==593== total heap usage: 8 allocs, 0 frees, 110,457 bytes allocated
==593==
==593== LEAK SUMMARY:
==593== definitely lost: 0 bytes in 0 blocks
==593== indirectly lost: 0 bytes in 0 blocks
==593== possibly lost: 0 bytes in 0 blocks
==593== still reachable: 110,457 bytes in 8 blocks
==593== suppressed: 0 bytes in 0 blocks
==593== Rerun with --leak-check=full to see details of leaked memory
==593==
==593== For counts of detected and suppressed errors, rerun with: -v
==593== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

@torappinfo commented on GitHub (Aug 28, 2018): Very weird bug. I decompressed the first block (BrotliDecoderDecompress) to the first cache of the following cache array: struct dictCACHE { ub1 cache[65536][DICT_CACHE_SIZE]; //ub1 testbuf[65536]; //this line fixed segment fault ub4 chunk[DICT_CACHE_SIZE];//index for 64k chunks, (ub4)-1 means invalid ub4 stamp[DICT_CACHE_SIZE]; }; Whatever DICT_CACHE_SIZE is, without testbuf defined, BrotliDecoderDecompress segment faulted for sure. And even if the testbuf is defined, it is never write-accessed in run (I made the testbuf page read-only, and it never crashed.) The Valgrind error is as follows: ==593== Memcheck, a memory error detector ==593== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==593== Using Valgrind-3.14.0.GIT and LibVEX; rerun with -h for copyright info ==593== Command: ./tdict_tbr /home/fengcao/dict/ahd hello ==593== ==593== Invalid write of size 8 ==593== at 0x483C02B: memcpy@GLIBC_2.2.5 (vg_replace_strmem.c:1033) ==593== by 0x4ACB489: ??? (in /usr/lib/libbrotlidec.so.1.0.5) ==593== by 0x4ACFFBA: BrotliDecoderDecompressStream (in /usr/lib/libbrotlidec.so.1.0.5) ==593== by 0x4AD0BAB: BrotliDecoderDecompress (in /usr/lib/libbrotlidec.so.1.0.5) ==593== by 0x10A98C: content (getcontent_tbr.c:122) ==593== by 0x10B6D7: GetContent (tdict_le.c:187) ==593== by 0x10D3FE: tdict_main (tdictmain.c:261) ==593== by 0x10AB35: main (getcontent_tbr.c:156) ==593== Address 0x121000 is not stack'd, malloc'd or (recently) free'd ==593== ==593== ==593== Process terminating with default action of signal 11 (SIGSEGV): dumping core ==593== Access not within mapped region at address 0x121000 ==593== at 0x483C02B: memcpy@GLIBC_2.2.5 (vg_replace_strmem.c:1033) ==593== by 0x4ACB489: ??? (in /usr/lib/libbrotlidec.so.1.0.5) ==593== by 0x4ACFFBA: BrotliDecoderDecompressStream (in /usr/lib/libbrotlidec.so.1.0.5) ==593== by 0x4AD0BAB: BrotliDecoderDecompress (in /usr/lib/libbrotlidec.so.1.0.5) ==593== by 0x10A98C: content (getcontent_tbr.c:122) ==593== by 0x10B6D7: GetContent (tdict_le.c:187) ==593== by 0x10D3FE: tdict_main (tdictmain.c:261) ==593== by 0x10AB35: main (getcontent_tbr.c:156) ==593== If you believe this happened as a result of a stack ==593== overflow in your program's main thread (unlikely but ==593== possible), you can try to increase the size of the ==593== main thread stack using the --main-stacksize= flag. ==593== The main thread stack size used in this run was 8388608. ==593== ==593== HEAP SUMMARY: ==593== in use at exit: 110,457 bytes in 8 blocks ==593== total heap usage: 8 allocs, 0 frees, 110,457 bytes allocated ==593== ==593== LEAK SUMMARY: ==593== definitely lost: 0 bytes in 0 blocks ==593== indirectly lost: 0 bytes in 0 blocks ==593== possibly lost: 0 bytes in 0 blocks ==593== still reachable: 110,457 bytes in 8 blocks ==593== suppressed: 0 bytes in 0 blocks ==593== Rerun with --leak-check=full to see details of leaked memory ==593== ==593== For counts of detected and suppressed errors, rerun with: -v ==593== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/brotli#231