Greetings,
passing certain combinations of strings through TAR_OPTIONS (reproducer
below) causes tar 1.35 to write outside allocated memory regions, before
processing archives.
Cursory testing will not show this bug, but memory error detection will
reliably trap the bug IME. All my testing took place on amd64/x86_64
with two different builds and OSes.
The same options through argc/**argv seem to be split properly (which
happens inside the shell) and
- in my case passing extra quotes to --mtime was the culprit -
cause complaints that the time format starting with "@ were unrecognized.
All details in the Fedora bug report I've filed (link below), I'll put
the relevant info here.
<https://bugzilla.redhat.com/show_bug.cgi?id=2389217>
This shows as "buffer overflow detected" + abort() in fortified builds,
or as Invalid write complaint under valgrind, and I surmise the address
sanitizer of GCC's or clang's would also complain.
-----
Minimal reproducer with address sanitizer, or fortified builds:
env 'TAR_OPTIONS=--mtime="@1234567890"' \
tar --format=ustar -chf - /tmp >/dev/null
Results in this, on Fedora 42:
*** buffer overflow detected ***: terminated
Aborted (core dumped)
-----
Minimal reproducer with valgrind:
env 'TAR_OPTIONS=--mtime="@1234567890"' \
valgrind --track-origins=yes \
tar --format=ustar -chf - /tmp >/dev/null
-----
Experimenting where the bug is:
BUG: 'TAR_OPTIONS=--mtime="@1234567890"' tar --format=ustar -chf -
SEMI (doesn't seem to scribble memory it doesn't own):
tar --format=ustar '--mtime="@1234567890"' tar --format=ustar -chf -
GOOD:
'TAR_OPTIONS=--mtime=@1234567890' tar --format=ustar -chf -
tar --format=ustar '--mtime=@1234567890' tar --format=ustar -chf -
-----
valgrind trace obtained on FreeBSD, apparently non-fortified build.
It's not too useful by itself, but the "Invalid write" in memcpy are
confirmed on a different build with backtrace below.
==48178== Memcheck, a memory error detector
==48178== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==48178== Using Valgrind-3.25.1 and LibVEX; rerun with -h for copyright info
==48178== Command: gtar --format=ustar -chf - /tmp
==48178==
==48178== Invalid write of size 8
==48178== at 0x48569E4: memcpy (vg_replace_strmem.c:1168)
==48178== by 0x24A7CD: ??? (in /usr/local/bin/gtar)
==48178== by 0x24A68A: ??? (in /usr/local/bin/gtar)
==48178== by 0x2498C1: ??? (in /usr/local/bin/gtar)
==48178== by 0x240633: ??? (in /usr/local/bin/gtar)
==48178== by 0x4903E69: __libc_start1 (in /lib/libc.so.7)
==48178== by 0x22041F: ??? (in /usr/local/bin/gtar)
==48178== by 0x4824007: ???
==48178== Address 0x5623c48 is 8 bytes inside a block of size 9 alloc'd
==48178== at 0x484E2E4: malloc (vg_replace_malloc.c:450)
==48178== by 0x24A72E: ??? (in /usr/local/bin/gtar)
==48178== by 0x24A68A: ??? (in /usr/local/bin/gtar)
==48178== by 0x2498C1: ??? (in /usr/local/bin/gtar)
==48178== by 0x240633: ??? (in /usr/local/bin/gtar)
==48178== by 0x4903E69: __libc_start1 (in /lib/libc.so.7)
==48178== by 0x22041F: ??? (in /usr/local/bin/gtar)
==48178== by 0x4824007: ???
==48178==
==48178== Invalid write of size 2
==48178== at 0x4856A85: memcpy (vg_replace_strmem.c:1168)
==48178== by 0x24A7CD: ??? (in /usr/local/bin/gtar)
==48178== by 0x24A68A: ??? (in /usr/local/bin/gtar)
==48178== by 0x2498C1: ??? (in /usr/local/bin/gtar)
==48178== by 0x240633: ??? (in /usr/local/bin/gtar)
==48178== by 0x4903E69: __libc_start1 (in /lib/libc.so.7)
==48178== by 0x22041F: ??? (in /usr/local/bin/gtar)
==48178== by 0x4824007: ???
==48178== Address 0x5623c50 is 7 bytes after a block of size 9 alloc'd
[... more out of bounds reads and writes omitted ...]
-----
The Fedora 42 Linux build aborts due to the fortification which find out
that the canaries have been harmed and then abort. Fedora bug report
with details filed here for reference:
Backtrace with Fedora's fortified build, which I find usable (indented
so Thunderbird won't word-wrap it):
#0 __pthread_kill_implementation (threadid=<optimized out>,
signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
tid = <optimized out>
ret = 0
pd = <optimized out>
old_mask = {__val = {140543246097462}}
ret = <optimized out>
#1 0x00007fd2c641e163 in __pthread_kill_internal (threadid=<optimized out>,
signo=6) at pthread_kill.c:89
No locals.
#2 0x00007fd2c63c4a7e in __GI_raise (sig=sig@entry=6) at
../sysdeps/posix/raise.c:26
ret = <optimized out>
#3 0x00007fd2c63ac6d0 in __GI_abort () at abort.c:77
act = {__sigaction_handler = {sa_handler = 0x559a00000000, sa_sigaction = 0x559a00000000}, sa_mask = {__val = {0, 94121436579136, 8, 140734169646992, 779909072403883264, 94121436579136, 0, 0, 335544320, 140543245700016, 65536, 94119913324560, 140543246595678,
4096, 140543247367818, 976259312}}, sa_flags = 1371333888, sa_restorer = 0x7fd2c655e2da}
#4 0x00007fd2c63ad6f3 in __libc_message_impl (fmt=fmt@entry=0x7fd2c655e2c3 "*** %s
***: terminated\n") at ../sysdeps/posix/libc_fatal.c:134
ap = {{gp_offset = 16, fp_offset = 32767, overflow_arg_area =
0x7fff3a3087f0, reg_save_area = 0x7fff3a308780}}
fd = 2
iov = {{iov_base = 0x7fd2c655e2c3, iov_len = 4}, {iov_base = 0x7fd2c655e2aa, iov_len = 24}, {iov_base = 0x7fd2c655e2c9, iov_len = 17}, {iov_base = 0x0, iov_len = 94121436574688}, {iov_base = 0x559a5acaf680, iov_len = 0}, {iov_base = 0x2475acaf3e0,
iov_len = 40}, {iov_base = 0x559a5acb1550, iov_len = 0}}
iovcnt = <optimized out>
total = <optimized out>
cp = <optimized out>
#5 0x00007fd2c64aa549 in __GI___fortify_fail (msg=msg@entry=0x7fd2c655e2aa "buffer
overflow detected") at fortify_fail.c:24
No locals.
#6 0x00007fd2c64a9ea4 in __GI___chk_fail () at chk_fail.c:28
No locals.
#7 0x0000559a384b0c6e in memcpy (__dest=<optimized out>, __src=<optimized
out>, __len=11) at /usr/include/bits/string_fortified.h:29
No locals.
#8 coalesce_segment (wsp=wsp@entry=0x7fff3a3089b0,
node=node@entry=0x559a5acb1580) at ../lib/wordsplit.c:598
next = 0x0
str = <optimized out>
slen = 11
p = 0x559a5acb15b0
end = 0x559a5acb15b0
len = 8
buf = 0x559a5acb05a0 "--mtime="
cur = <optimized out>
#9 0x0000559a384b574a in wsnode_coalesce (wsp=0x7fff3a3089b0) at
../lib/wordsplit.c:675
p = 0x559a5acb1580
#10 wordsplit_process_list (wsp=0x7fff3a3089b0, start=<optimized out>) at
../lib/wordsplit.c:2379
p = 0x559a384e5d10 <exptab+48>
#11 0x0000559a384bc15b in wordsplit_run (lvl=0, command=0x7fff3a30a4cf "--owner=0 --group=0
--sort=name --mtime=\"@1752234285\"", length=<optimized out>, wsp=0x7fff3a3089b0,
flags=33558086) at ../lib/wordsplit.c:2434
rc = <optimized out>
start = 0
#12 wordsplit_len (command=0x7fff3a30a4cf "--owner=0 --group=0 --sort=name
--mtime=\"@1752234285\"", length=<optimized out>, wsp=0x7fff3a3089b0,
flags=33558086) at ../lib/wordsplit.c:2444
No locals.
#13 wordsplit (command=0x7fff3a30a4cf "--owner=0 --group=0 --sort=name
--mtime=\"@1752234285\"", ws=0x7fff3a3089b0, flags=33558086) at
../lib/wordsplit.c:2450
No locals.
#14 0x0000559a3847f47e in parse_default_options (args=0x7fff3a308980) at
/usr/src/debug/tar-1.35-5.fc42.x86_64/src/tar.c:2256
opts = <optimized out>
ws = {ws_wordc = 0, ws_wordv = 0x0, ws_offs = 1, ws_wordn = 0, ws_flags =
33558086, ws_options = 1632, ws_maxwords = 140543247636624, ws_wordi = 0, ws_delim =
0x559a384d0cd1 " \t\n", ws_comment = 0x0, ws_escape = {
0x559a384e5c90 <wordsplit_c_escape_tab> "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v", 0x559a384e5c90 <wordsplit_c_escape_tab> "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v"}, ws_alloc_die = 0x559a384af9c0 <_wsplt_alloc_die>, ws_error = 0x559a384b09f0 <_wsplt_error>, ws_debug = 0x0,
ws_env = 0x15, ws_envbuf = 0x100a, ws_envidx = 255, ws_envsiz = 4185718668, ws_getvar = 0x0, ws_closure = 0x0, ws_command = 0xf97cff8c, ws_input = 0x7fff3a30a4cf "--owner=0 --group=0 --sort=name --mtime=\"@1752234285\"", ws_len = 53, ws_endp = 53,
ws_errno = 0, ws_usererr = 0x0, ws_head = 0x559a5acb14f0, ws_tail = 0x559a5acb15b0, ws_lvl = 0}
loc = {source = OPTS_ENVIRON, name = 0x559a384d0d6f "TAR_OPTIONS", line
= 0, prev = 0x0}
save_loc_ptr = <optimized out>
#15 decode_options (argc=5, argv=0x7fff3a308bf8) at
/usr/src/debug/tar-1.35-5.fc42.x86_64/src/tar.c:2378
idx = 32767
loc = {source = OPTS_COMMAND_LINE, name = 0x0, line = 0, prev = 0x0}
args = {loc = 0x7fff3a308940, textual_date = 0x0, o_option = false,
pax_option = false, compress_autodetect = false, backup_suffix_string = 0x0,
version_control_string = 0x0}
#16 main (argc=<optimized out>, argv=<optimized out>) at
/usr/src/debug/tar-1.35-5.fc42.x86_64/src/tar.c:2792
No locals.
Let me know if you need anything else.
Best regards,
Matthias Andree