Hello maintainers!

Using tar and building it with the UBSAN sanitizer I have found a
runtime error in the short_read() function. Running tar with the -xjSv
and --to-stdout arguments and passing a file with specific data
to stdin, an out-of-bounds error occurs.

The problem is that inside the short_read() function we offset the
buffer[] field of the block union by a status value. However since the
buffer field in this union is 512 bytes long, offsetting it by a larger
number of bytes (> 512) will result in an out-of-bounds error.


Here is the stacktrace:

buffer.c:965:31: runtime error: index 4096 out of bounds for type 'char[512]'
    #0 0x5a34251b9b02 in short_read /tar\/src/buffer.c:965:31
    #1 0x5a34251b35bf in _gnu_flush_read /tar\/src/buffer.c:1884:2
    #2 0x5a3425183802 in gnu_flush_read /tar\/src/buffer.c:1892:3
    #3 0x5a3425177953 in flush_read /tar\/src/buffer.c:1990:3
    #4 0x5a3425176781 in flush_archive /tar\/src/buffer.c:1043:7
    #5 0x5a34251745e8 in find_next_block /tar\/src/buffer.c:619:7
    #6 0x5a34251863a7 in _open_archive /tar\/src/buffer.c:859:7
    #7 0x5a3425183556 in open_archive /tar\/src/buffer.c:2005:3
    #8 0x5a34254415f9 in read_and /tar\/src/list.c:180:3
    #9 0x5a34256e4cfa in main /tar\/src/tar.c:2894:7
#10 0x71ccc825f09a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a) (BuildId: 79cd7beb3903a9b34e306f52a988d970e13524a6) #11 0x5a342512a599 in _start (/tar\/src/tar+0x12a599) (BuildId: f24c97bc1198bcffea19f758ad64f65536cfb420)

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior buffer.c:965:31


Since the buffer[] in the block union is the first field, we can offset
the pointer to the union rather than the members.

This diff fixes the error for me:

diff --git a/src/buffer.c b/src/buffer.c
index 1cdeffc6..e2106159 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -962,7 +962,7 @@ short_read (idx_t status)
   idx_t left;                  /* bytes left */
   char *more;                   /* pointer to next byte to read */

-  more = record_start->buffer + status;
+  more = ((char *) record_start) + status;
   left = record_size - status;

   if (left && left % BLOCKSIZE == 0


Steps to reproduce:

1. Build project with UBSAN sanitizer

2. Create a file, that contains information, like this:

echo -e s"\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\xf8\xc4\x0b\x97\x00\x00\x74\xfb\x84\xca\x10\x00\x30\x40\x00\x7f\x80\x00\x04\x66\x00\x9e\x00\x00\x00\x80\x08\x20\x00\x75\x0d\x53\xf4\x8d\xaa\x00\x1a\x64\xd3\x20\x92\xa7\x94\x0c\x87\xa8\x34\x34\x15\xfa\x93\x42\x0c\x30\x01\x23\x48\xc4\xda\x00\x00\x00\x00\x81\x94\x30\x30\x30\x30\x0a\x8e\x02\x91\x7f\x00\xb9\xd1\xd9\xe0\x21\x30\x1f\x12\x23\x61\x49\xb0\xe0\x54\x4d\x52\xb6\x2e\x30\x8e\x57\x6b\xc9\x24\x1f\x8b\x62\x05\x80" > crash

3. Run tar as follows:

cat crash | ./src/tar -xjSv --to-stdout

4. Get an ubsan error

Reply via email to