The seek implementations for files return ARCHIVE_FATAL if seek fails to
adjust the position. ARCHIVE_FAILED is used in case of pipes etc.
Also, if a file seek fails, the position is not adjusted.
Adjust the memory implementations to do the same.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Actually follow the bidder comment and return amount of bits. Has the
nice advantage of removing the int/ssize_t handling, even though it was
safe anyway.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If open functions fail to store client data, free internally allocated
memory before returning an error code.
Writer open functions must check magic themselves.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The newc format stores only 32-bit inode values. Assign unique
synthetic values from each full device and inode identity. This avoids
collisions after truncation while preserving hardlink groups.
Fixes#3314.
Exercise large inode values that collide after 32-bit truncation,
hardlink identity, and distinct device IDs. Update bsdcpio expectations
so successful synthesis produces no truncation warnings.
PPMd pulls input through its byte callback, so the regular input buffer does not track bytes consumed after crossing a read-block boundary. Advance the packed stream using the callback count and reject reads beyond the remaining packed data.
Resolves#3337
There are other such generators in this code, but this one was the
subject of a recent bug. It was unclear how it worked, since it broke
writing the header, continuation and final entries into three different
places.
I feel this version is more intuitive.
If upstream filter provides huge amount of bytes, e.g. if archive is
fully stored in memory, bidding accesses too many bytes. This could in
turn overflow int return value, possibly leading to signed integer
overflows.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If registering a format parser bidder fails or triggers a warning,
forward the information instead of silently turning it into ARCHIVE_OK.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Several archive writers duplicate identical hdrcharset option handling.
Share the validation and conversion logic across the affected cpio, tar,
and zip writers.
This reduces duplication without changing behavior.
When creating an archive from an @archive argument, configure the
input reader rather than passing the output writer to
archive_read_add_passphrase(). Use the reader for error reporting as
well.
Exercise --passphrase while copying entries from an encrypted
@archive into a new archive, then extract the result and verify the
copied file contents.
If a signature is too large, the bid value could overflow INT_MAX. With
its current implementation, it could even lead to a signed integer
overflow, which is undefined behavior.
Disallow huge signatures, which most likely won't impact anyone.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
append_archive_filename() returns after reporting an input archive open
failure so archive creation can continue. Release the input archive
reader before returning to avoid accumulating one reader for every
failed @archive argument.
The name was only needed to find the appropriate bidder for a filter.
Since this has been resolved by accessing the last bidder, since in these
situations the bidder is added first, then the filter, the name is not
needed anymore.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Appending a filter implies appending a bidder. Always use the last added
bidder, since it was just added. This avoids desync situations where
previously added bidders could erroneously match.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use difftime() instead of changing time_t values by the half-year
range. This avoids signed overflow near the time_t limits without
changing normal output.
Use difftime() instead of subtracting time_t values when choosing the
date format. This avoids signed overflow for far past or future
timestamps without changing normal output.
test_read_format_zip_ppmd8_aes256_streaming fails with 6 assertion
failures on systems without a crypto library (openssl/nettle/mbedtls)
because it tries to decrypt a pre-built encrypted zipx file without
checking crypto availability first.
Add the same guard used by the other encrypted zip tests in this file.
Signed-off-by: Jaipaul Cheernam <jaipaul.cheernam@est.tech>
* In _checksum_init() and _checksum_final(), fail if the requested digest
is not supported (e.g. archive uses SHA256 but libarchive was compiled
without SHA256 support)
* In checksum_init(), fail with a meaningful error message if either
_checksum_init() call fails.
* In checksum_final(), simplify the logic and improve the error message.
* Make the various digest tests conditional on support for the digest
being tested.
Fixes: 6f10adcd59 ("xar: Add support for SHA256 and SHA512")
Extend the xar reader and writer to understand the SHA256 (header
cksum_alg 3, 32 bytes, style "sha256") and SHA512 (cksum_alg 4, 64
bytes, style "sha512") checksum algorithms in addition to the existing
SHA1 and MD5. These are the algorithm codes defined by the canonical
xar format, so archives written with them interoperate with Apple's xar,
pkgutil, and PackageKit.
Both the TOC ("toc-checksum") and per-file ("checksum") algorithms are
supported for writing. New algorithm handling is guarded by
ARCHIVE_HAS_SHA256 / ARCHIVE_HAS_SHA512 so builds without those digests
are unaffected. MAX_SUM_SIZE grows from 20 to 64 to hold a SHA512
digest.
3.8.8 ("make ARCHIVE_FATAL sticky in data-reading entry points",
commit e1f890dc) made archive_seek_data() set
a->archive.state = ARCHIVE_STATE_FATAL whenever the call returned
ARCHIVE_FATAL. That is correct for genuine I/O / parse failures
returned by a format's seek_data() implementation, but it also
poisoned the archive in two cases where seeking is simply
unsupported for the current format:
1. The seek_data == NULL branch in archive_seek_data() itself
(no format_seek_data_block registered: ustar/tar, the
streaming ZIP reader, ...).
2. rar5_seek_data() in archive_read_support_format_rar5.c, which
unconditionally returns ARCHIVE_FATAL because RAR5 is a
streaming unpacker. This was latent before 3.8.8 because
the read core did not make FATAL sticky; the stickiness
change exposed it.
Both are capability gaps, not stream corruption. ARCHIVE_FATAL
must always be sticky (it means the archive is irrecoverably
damaged), so both sites now return ARCHIVE_FAILED with an error
string instead. ARCHIVE_FAILED is recoverable: capability probes
such as
if (archive_seek_data(a, 0, SEEK_CUR) >= 0) ...
learn that seeking is unavailable while the archive remains
usable for subsequent reads. The sticky-on-genuine-FATAL part of
the original 3.8.8 change is preserved for actual ARCHIVE_FATAL
returns from a format's seek_data() implementation.
The seek_data == NULL branch also reports the condition better: a
format that never registers a seek hook is not illegal use of the
library, so the error is now ARCHIVE_ERRNO_MISC / "Cannot seek
data with this format" instead of ARCHIVE_ERRNO_PROGRAMMER /
"Internal error: No format_seek_data_block function registered".
The rar5 change also matches how the RAR4 reader already reports
the same situation (archive_read_support_format_rar.c:1339
returns ARCHIVE_FAILED for compressed RAR files).
This restores 3.8.7 behaviour for clients that probe seekability
before reading. VLC's skins2 stream extractor
(modules/stream_extractor/archive.c, archive_seek_subentry())
probes with archive_seek_data(a, 0, SEEK_CUR); on a non-seekable
format the poisoned state killed all later archive_read_data()
calls, breaking ZIP-packed .vlt skins on distributions shipping
libarchive 3.8.8.
Tests:
- test_archive_seek_data_unsupported: covers the seek_data==NULL
branch using a ustar archive.
- test_read_format_rar5_seek_data_unsupported: covers the
rar5_seek_data() branch using an existing RAR5 fixture, and
verifies the entry content with verify_data() after the probe.
Both fail on master and pass with this change.
Store the program filter description in the bidder state instead of the
active filter state.
The active state is freed by archive_read_close(), while the bidder state
remains valid until archive_read_free(). This keeps archive_filter_name()
from returning a dangling pointer.
Check reserved fields by comparing adjacent bytes after confirming the
first byte is zero. This preserves the memcmp fast path and removes
2 KiB from each ISO9660 reader state.
A varint field can actually have up to 10 bytes to resemble whole 64 bit
of a value. Use checked arithmetic to detect integer overflows.
Also, treat a varint which never has a byte without continuation bit as
faulty.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If there is no good reason to deviate from the rule, always use "f" for
filter just as we use "a" for archive.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
- If filter is meant, use "f" instead of "self" or "filter".
- If bidder is meant, use "b" instead of "self".
Matches writer filter style.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use "struct filter *filter" pattern, with filter being the name of the
filter. This matches the format parser pattern.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
- Declare access variable at the beginning
- Remove unneeded cast (see warc parser, which never did it)
- Remove inline cast function
- Avoid unneeded brackets to unify style
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Even though int64_t would be the correct data type because no more data
than INT64_MAX can be written, use the easier solution for format
writers for now.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Files which cannot fit into a stream should never return success, which
can happen due to truncation on 32 bit systems.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The isdigit function is locale-dependent, which means that characters
outside of 0-9 range can return true here, e.g. "power of 2" for a few
locales on Windows.
Test for ASCII character range from '0' to '9' instead.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use memcpy() for path components whose lengths are already known and
write the terminating null byte explicitly.
Cache the inserted component length instead of calculating it multiple
times while constructing the ustar pathname.
Replace the single-use basename helper with strrchr() and preserve the
existing rejection of pathnames ending in a slash.
Cache the member filename length to avoid repeated strlen() calls while
building and writing the archive header.
Continue searching after each replaced backslash instead of restarting
from the beginning of the filename.
This avoids repeatedly scanning the already converted prefix.
Check the allocation used to initialize cur_dirstr before accessing the
buffer.
Release the partially initialized strings, root entry, file registry,
and hardlink registry before returning ARCHIVE_FATAL.
Copy the old GNU tar header before reading sparse extension blocks.
Those reads may replace or free the callback buffer backing the original
header. Use the copy when parsing the common header fields.
Calculate the pathname length directly after validating the path.
The helper only wrapped strlen() and adjusted the length for directory
entries. Removing it keeps the calculation beside the validation and
reuses the existing file type value.
Add a test archive that reuses one read buffer for every 512-byte block.
Reading the sparse extension overwrites the main GNU header. Check that
mode, uid, gid, and mtime still come from the original header.
Format integer values directly at each call site and remove the custom
integer formatter.
This handles the full signed integer range and preserves unsigned byte
counters.
Clear format state and callbacks after running the previous format
destructor so a failed replacement setup cannot leave stale pointers.
Also unregister previous GNU tar state and require ARCHIVE_STATE_NEW
before switching to GNU tar.
If allocation of filter fails, gracefully handle it instead of
eventually triggering a NULL pointer dereference.
Allocate the filter in last step, because it's rather difficult to
cleanly revert in case of error.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Test that unregistering 7-Zip and XAR clears format state and is safe
to repeat.
Also test switching from 7-Zip to GNU tar and selecting GNU tar again.
Create free_data function for later reusing.
Also make sure that f->data is always set to NULL.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Writing an iso archive with a series of long names in it would eventually
cause an out-of-bounds write off the end of the rockridge extra data.
This happened when we allocated a net-new block for a file with a name
longer than would fit in two records spanning multiple blocks. We would:
- Fill up the extra_space in the last block
- Allocate a new block
- Write 250 + 5 (header) bytes of the name into it
- Try to copy 250 _more_ bytes out of the name, even if we did not have
space left.
AppleDouble detection only needs to inspect the final path component for
the "._" prefix.
Check the multibyte pathname first on all platforms and only fall back to
WCS when the multibyte pathname is unavailable. This avoids unnecessary
wide-character pathname conversion for ordinary tar entries when mac-ext
handling is enabled.
Entries without an available pathname now return no match instead of
ARCHIVE_FAILED from the boolean check.
Use a more specific error when resource header generation fails.
The old message said `cannot archive file`, but this path fails when
the generated WARC resource header exceeds the internal limit.
Check generated WARC header creation and writes.
Fail when the generated warcinfo header cannot be built. Treat a
resource header build failure as fatal because output may already exist.
Propagate write errors instead of marking the record as written.
Add memory-writer tests for generated WARC header write failures.
Cover both the warcinfo header and a resource header with warcinfo
disabled. These tests show that header write failures are hidden.
Add a per-format "align" option to the pax writer that pads each
regular file's pax extended header (creating one if necessary) so the
file data that follows begins on a multiple of the requested number of
bytes within the uncompressed archive stream. The value must be a
power of two and a multiple of the 512-byte tar block.
The alignment is applied to the stream feeding into any compression
filter, so the decompressed archive is aligned regardless of whether
the archive is compressed. This lets tools share file contents out of
an (uncompressed copy of an) archive using reflinks / copy_file_range(2)
instead of doing a full data copy on extraction.
The padding is carried in an ignorable "LIBARCHIVE.pad" pax record, so
existing readers extract the archive unchanged. Files shorter than the
alignment are left unpadded since they cannot share a whole block.
Add a test covering uncompressed and gzip-compressed archives and
document the option.
Signed-off-by: Daan De Meyer <daan@amutable.com>
A symlink entry named TRAILER!!! with a body large enough to grow the
read filter's copy buffer used to leave the cached name pointer stale
before the end-of-archive comparison. Read a static newc archive in
small blocks and assert the trailer is still detected (ARCHIVE_EOF).
The fix landed in 23b54da; this only adds coverage.
The uu filter can keep a partial encoded line in its internal input
buffer, then reach upstream EOF with no new bytes available. In that
case, d is NULL and avail_in is zero, but the buffered-input path still
passes d to memcpy() before processing the saved bytes.
Skip the append copy when avail_in is zero so the saved input is handled
without passing a null source pointer to memcpy().
Reject resource records when less data was written than Content-Length.
Do not append the WARC end marker after a short entry. Return fatal
because the record body has already been started.
This avoids writing a record whose length does not match its body.
Add a writer test for a resource entry shorter than Content-Length.
The test closes after a short write and checks for a fatal error. It
documents that the end marker must not hide a short body.
Rewrite WARC reader comments to use clearer wording and match the
style used by other libarchive format readers.
Keep the change comment-only while preserving existing behavior.
Cache the CPIO trailer-name check before consuming the pathname
buffer.
The reader kept a pointer returned by __archive_read_ahead() and used
it after __archive_read_consume(), and after possible symlink body
processing. Read-ahead pointers are views into libarchive input buffers
and should not be relied on after the associated bytes are consumed.
Store the TRAILER!!! decision while the pathname buffer is still valid,
then use the saved boolean after the remaining header handling.
Parse Content-Length only up to the located end-of-line instead of
passing a bounded header field to strtol(). Use int64_t for the parsed
entry size, ASCII digit checks, and checked arithmetic while accumulating
the value.
When a sparse map entry began before the current data block
(expected_offset < offset), verify_sparse_file() formed the intermediate
pointer "buff + (expected_offset - offset)" before adding the entry
size. That intermediate points before buff, which is undefined behavior;
with file4's large hole it trapped the test under UBSan
(-fsanitize=pointer-overflow). Fold the size into the displacement so the
whole byte offset is computed as one integer and added to buff in a
single, in-bounds step.
The standard has no fixed 10 bit limit. Instead, a pre-tree could
contain up to 16 bit codes, although this is very unlikely.
Grow the table if needed.
While at it, clarify now that max_bits are actually lookup_bits.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If a pre-tree is larger than 10 bit, libarchive fails to read the data.
According to spec, up to 16 bit are valid.
Example file provided by Duncan Horn. Thanks a lot!
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
- Huffman operations are up to 16 bit, thus uint16_t
- LZX itself can handle up to 32 bit, thus uint32_t
- Memory sizes are size_t
Otherwise:
- Use uint8_t to show where bit lengths are stored
- Use int to clarify that a status is stored
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The check against table size is redundant since Kraft's inequality
already verified that this operation cannot exceed table size anymore.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use 0 as marker for maximum symbol count to be able to eventually switch
to another data type.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
This step is not actually needed, but will prevent static analysis
warnings that values might exist which have never been initialized.
"Invalid" values only exist outside of the addressable area, which can
happen if maximum code bit length is smaller than table bit length.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
It's actually the amount of symbols/codes in Huffman tree. Clarify it by
using the field name symbol_count instead.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Some lzx functions reverse the generic logic of 0 being success and
anything else being an error. It's debatable from a boolean point of
view but use the regular ARCHIVE_OK in case of success, which is 0.
Unifies with other lzx functions and general idea of libarchive
functions.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
We really have to check if Kraft's inequality is 1 (in our case it's
0x10000) or 0 for an empty tree. Anything else is an error.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
As long as the given index is in range of the table, the previous
creation of the lookup table takes care that all elements are valid.
This is a left over of actual tree removal done in commit
c253f0aae9.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Most if not all compilers today understand inline, but it's also just a
hint. Static functions will be inlined if it's worth it from compiler
perspective and inline functions will be still functions, too.
Remove it and let compilers decide. Increases portability for very old
compilers which cannot handle it.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
remaining was set to length but never decremented, so the loop only stopped at a NUL and read past fields passed without a trailing NUL (a read_ahead window sized by mbsnbytes). Decrement per byte and check the count before dereferencing *itp.
Drives the best-effort converter (charset iconv cannot open) through
archive_strncpy_l with a length-delimited buffer: once with trailing
non-NUL bytes after the declared length and once with an exact-length
heap allocation with no trailing NUL. Fails under ASan before the fix.
iso9660_options() checks the second byte of the iso-level option value
before checking that the first byte contains a valid ISO level.
For an empty option value such as iso9660:iso-level=,
archive_write_set_options() passes a value pointer to the terminating NUL
byte of the duplicated options string. Reading value[1] then reads one byte
past that heap allocation.
Check value[0] before reading value[1]. This keeps the same accepted values
while rejecting the empty value without reading past the option buffer.
The GNU sparse name builder trims trailing slash and /. components before
building the synthetic ustar entry name. If the sparse pathname is fully
pruned, the effective source length becomes zero and build_ustar_entry_name()
reads one byte before the pathname buffer.
Handle the all-pruned pathname case before calling the ustar name builder.
The synthetic name then preserves the root-directory case.
Read a Solaris pax entry whose SUN.holesdata value ends right after its
final decimal offset (no trailing space), which is the shape of every
valid map. The 1536-byte file has data at [0,512) and [1024,1536) with a
hole between them, so archive_entry_sparse must report exactly those two
data blocks. This pins the parser's loop-termination behavior for the
common case where the run consumes all value_length bytes.
Make sure that multiplications do not overflow (or if they overflow,
handle them properly).
This also means that proper data type size_t instead of int is used.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The -s replacement scanner consumes a backslash and then reads the
escaped byte. If the replacement ends in one literal backslash, the loop
can advance past the terminating NUL and read one byte past the
allocated replacement buffer.
Stop scanning when a backslash is followed by the terminating NUL. The
final literal append then preserves the trailing backslash.
The seekable ZIP reader checks whether __MACOSX/ entries are resource
fork entries by testing the basename prefix.
The existing guard used r - name, which is the basename offset from the
start of the filename, not the basename length. For a short basename such
as "__MACOSX/.", this could read one byte past the filename.
Check the remaining basename length before reading the resource fork prefix.
When build_ustar_entry_name() trims trailing '/' characters and '/.'
path elements from a pathname made entirely of separators, filename_end
can be reduced all the way back to src.
Handle this root-like pathname case before attempting to locate the
filename component, and emit a root-like ustar name instead.
Add a regression test that writes and reads back a long all-slash pax
pathname.
Signed-off-by: R4mbb <yong010301@gmail.com>
Consume pending data before EOF and skip handling.
Then skip only the unread body bytes plus the WARC record separator.
This keeps the reader at the correct input position after an entry has
been partially or fully read.
read_children() checked the directory record length byte before ensuring
that the record pointer was still inside the current logical block.
If directory records exactly filled the final block, the next loop
condition could read one byte past the image buffer.
Check the remaining block length before reading the record length byte.
tar_atol_base_n() read the next byte before checking whether any bytes
remained. If a numeric field ended at the caller-provided boundary, this
caused a 1-byte OOB read while preparing the next digit range check.
Read each digit only while bytes remain.
header_pax_extension() passes PAX attribute values without the trailing
newline to pax_attribute(). The SUN.holesdata parser read one byte past
the supplied value when the last numeric field ended at the value
boundary.
Handle length == 0 before checking *e.
read_body_to_string() already consumes the extension body according to
the header size. Use the resulting string length instead of decoding the
size field again in header_Solaris_ACL().
Add a test that extracts data from the first WARC resource record, then
moves to the second header.
This checks the bug where already-read entry data was not handled correctly
before skip handling.
If strnlen is not available, custom code is used. Make sure that the
custom code keeps premise of strnlen API for maximum compatibility.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Explain the algorithm and data structure involved in Huffman tree
handling as array. Hopefully simplifies upcoming code change
verifications.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
While investigating improvements to Zip's header reading,
I found an overlooked case in the read core: When a format
returned FAILED for a header read (indicating that the entry
could not be read but that the archive as a whole is still valid),
the core silently converted that to FATAL, ending the archive
at that point.
The change to the read core adds a new internal state "DATA_RECOVERY"
indicating that we're post-header (like the existing DATA state) but
that client requests to actually obtain data are not valid.
While investigating improvements to Zip's header reading,
I found an overlooked case in the read core: When a format
returned FAILED for a header read (indicating that the entry
could not be read but that the archive as a whole is still valid),
the core silently converted that to FATAL, ending the archive
at that point.
Two formats already returned FAILED for certain header issues:
Zip and LHA both indicated unreadable symlink entries in this way,
which should allow the rest of the archive to continue being read.
New tests here verify that we can now continue reading past
such entries, and that clients who ignore the FAILED result
get the same FATAL handling as other API misuse.
The WARC writer capped each data write against the full declared entry
size, but never reduced that value after accepting data. As a result,
repeated write calls could emit more bytes than the WARC Content-Length.
Track the remaining byte count for the current regular-file entry and
decrement it after each successful write. Further writes are then
clamped to the remaining length and return zero once the entry is full.
archive_acl_from_text_nl() parses ACL text from a pointer and a length
and does not require a NUL terminator; the tar reader passes a
__archive_read_ahead() window of exactly value_length bytes for
SCHILY.acl.access/.default/.ace. When an entry is empty (a
whitespace-only value, or trailing whitespace after a valid entry)
next_field() consumes the rest of the buffer as whitespace and leaves
field[0].start one past the end, which the '#'-comment probe then
dereferences. Capture the buffer end and skip the entry when its first
field starts there. The wide archive_acl_from_text_w is NUL terminated
and unaffected.
Drives archive_acl_from_text_nl() with exact-length, non-NUL-terminated
buffers whose first field is only whitespace, covering the empty-entry
and trailing-whitespace cases. Allow the test to include
archive_acl_private.h via __LIBARCHIVE_TEST.
Read the fixed metadata prefix of the PKWARE Unix extra field 0x000d.
The fixed portion contains atime, mtime, uid, and gid values. Require
the full 12-byte prefix before reading it, and leave the variable-length
data field unsupported for now.
Add a test for the PKWARE Unix extra field 0x000d.
The fixture stores a regular file with the 12-byte fixed metadata
prefix in both the local header and central directory. The test checks
that the ZIP reader exposes mtime, atime, uid, and gid metadata for
seekable and streaming reads.
WARC records require a known Content-Length. Reject regular file
entries without a valid size instead of treating them as zero.
Avoid casting archive_entry_size() through size_t before storing it in
the uint64_t WARC Content-Length field, since that can truncate large
sizes on 32-bit platforms.
If enough bytes have to be skipped, a signed integer overflow could
occur (most realistically on 32 bit systems). Use an unsigned type,
which could still overflow but has only negative impact on diagnostic
warning message.
Follow up of 02fa05675f but this time for
function find_newc_header.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
archive_write_pax_header() set hardlink entries to size zero for the
restricted pax format, then immediately set all hardlink entries to size
zero again.
Remove the narrower check because it is fully subsumed by the following
hardlink check.
No functional change intended.
header_common() parsed the tar mode field once when setting the file
type and again when setting permissions. Cache the parsed value and reuse
it for both setters.
archive_entry_set_filetype() and archive_entry_set_perm() already mask
the relevant bits, so passing the full header mode preserves behavior.
The permission-set guard is unchanged.
No functional change intended.
This is the fix for....
Update the common archive_read_data_into_fd() to truncate
output to the actual entry size from the most recent
archive_read_header result.
This addresses a hole in handling of certain subtly-malformed archives
that can have more data than declared in the header. Before this,
bsdtar explicitly truncates the entry data to the actual declared size
when extracting to a file, but did not do so when extracting to
stdout.
This is the test for....
Update the common archive_read_data_into_fd() to truncate
output to the actual entry size from the most recent
archive_read_header result.
This addresses a hole in handling of certain subtly-malformed archives
that can have more data than declared in the header. Before this,
bsdtar explicitly truncates the entry data to the actual declared size
when extracting to a file, but did not do so when extracting to
stdout.
Reverse an old experiment: When the 32-bit size field is provided, it
now must match the actual size exactly. We no longer permit it to
only match the least-significant 32 bits of the actual size.
--begin-history--
When developing the earliest versions of libarchive's Zip support, I
experimented with the idea of supporting greater-than-4GiB entries in
32-bit Zip archives. This proved to be technically feasible as long
as the _compressed_ data was small enough to keep the resulting
archive under 4GiB. But this also required us to interpret
the 32-bit uncompressed size field as representing only the
least-significant 32 bits of the size.
--end-history--
Test: If the uncompressed size stored in the archive differs
from the actual uncompressed size by a multiple of 2^32,
we should report some kind of error.
This currently goes undiagnosed because of an old attempt
to support larger-than-4GiB entries in plain 32-bit Zip
archives without necessarily requiring Zip64 extensions.
While this is technically feasible, it requires treating
the 32-bit size field ambiguously, which can lead to size
confusion from readers.
ArchiveProperties entries have a property ID, a size, and payload.
The reader parsed the ID and size, but did not consume the payload.
Payload bytes could then be read as the next property ID, which
misaligned the rest of header parsing.
Skip the payload in chunks so parsing stays aligned with 7-Zip/7zz.
If the raw parser ever treats a basically endless stream which
eventually overflows INT64_MAX, fail instead of overflowing
offset + size calculation, leading to subsequent issues in callers.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If an entry declares an uncompressed size, we should not return any
data beyond that point. This doesn't impact bsdtar and friends since
they independently truncate to the declared entry size, but other
clients may not be so careful.
Apparently discovered (but not reported) by @bikini
Reported by @dkgkdfg65
Tests for...
If an entry declares an uncompressed size, we should not return any
data beyond that point. This doesn't impact bsdtar and friends since
they independently truncate to the declared entry size, but other
clients may not be so careful.
Apparently discovered (but not reported) by @bikini
Reported by @dkgkdfg65
Add a 7z test archive with a non-empty ArchiveProperties payload.
The archive has one empty file and an ArchiveProperties entry with a
1-byte payload. It is accepted by 7-Zip/7zz.
This covers parsing when ArchiveProperties has payload bytes.
Avoid adding the encryption overhead directly to entry sizes when deciding
whether Zip64 is needed or when updating the stored compressed size.
For the Zip64 decision, compare against ZIP_4GB_MAX - additional_size. For
stored encrypted entries, use archive_ckd_add_i64() so the size update and
overflow check happen together.
Covers basic E8 translations, yet it lacks the handling of negative
values because this would require large archives.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The cab specification states that E8 translation shall be performed only
within the first 1 GB range of an uncompressed file, i.e. for the first
0x8000 CFDATA entries in a folder.
Take this limit properly into account.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Avoid allocating and filling metadata bitmaps when the 7z header says
that every file has the corresponding value.
Sparse metadata still uses the existing bitmap path.
The Windows disk reader supports wildcards by converting a path like
"foo/*" into a single tree rooted at "foo/". This does not play nicely
with symlink mode `H`, which treats the first item in each tree as a
dereferenced link (L) and all subsequent items as physical links (P). On
Windows, we take the first _match_ in L mode and all subsequent
descendants and matches in P mode.
This creates a behavioral difference between `bsdtar -c -H *` and
`bsdtar -c -H one two three`.
This commit changes how we handle H->L/P transitions when doing wildcard
matches. The transition from H to P is now done when we descend into our
first directory, and we switch back to H mode when we ascend out of
it[^1].
Closes#3222
[^1]: This does not matter in practice because tree is breadth-first
rather than depth-first, but I've done it in the interest of
completeness and not leaving ourselves caltrops to step on later.
The memory allocation in archive_read_set_callback_data2, which is
called by archive_read_set_callback_data, could fail.
Check result and properly handle failure.
Resolves#3210.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Flatten nested allAreDefined checks for attribute and timestamp bools.
Also initialize empty-stream entries as directories by default before
applying the optional EmptyFile bitmap.
No archive behavior change is intended.
Both supported BCJ2 layout branches store the chosen unpack size in
remaining.
main_stream_bytes_remaining is set from remaining immediately after
the branch, so the earlier assignments are redundant.
On LoongArch64 with 16KB pages, Linux tmpfs often uses Transparent
Huge Pages (THP) sized at 32MiB. When a test writes data followed
by a 32MiB hole, the kernel may allocate a single huge page folio
to back the entire range in RAM. Because the range is physically
backed by memory, SEEK_HOLE correctly (from the kernel's perspective)
identifies the range as DATA, failing the test's assertion. Increasing
MIN_HOLE to 64MiB ensures the hole exceeds the huge page size on
loongarch64, preserving the sparse nature of the file for the test.
Signed-off-by: Pluto Yang <yangyj.ee@gmail.com>
Use correct data types to reduce amount of casts. This should further
improve readability.
While at it, also improve white space usage.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
It's quite rare that "p" is not the iteration pointer but the base
pointer, while "b" is not the base pointer but the iteration pointer.
Use more descriptive variable names to assist auditors to figure out how
E8 call translation reversal works. It's already hard by itself.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
It's a file size and used for signed integer comparison. Since it's
literally a file size, its maximum value should be properly checked as
well.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use more macro definitions instead of magic numbers to improve code
readability. Hopefully this explains a bit more where these values come
from.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Move the macro definitions up to the field where they are used, which is
done for other such fields as well.
This in turn allows to avoid the magic value 0 in initial assignment.
Use ST_RD_TRANSLATION instead for better documentation where the finite
state machine starts (and that this state cannot be reached again).
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The SFX scanner missed a valid 7-Zip header when exactly 32 bytes
remained in the lookahead buffer.
The ELF SFX path also missed ".data" when it was at the last valid
position in the section string table. It also allowed e_shstrndx to be
equal to e_shnum, even though that is one past the section table.
Tighten these checks so valid edge cases are handled correctly.
Add PE and ELF SFX fixtures for the SFX scanner boundary cases.
The PE fixture places an empty 7z archive at the final exact
32-byte scan window. The ELF fixture places ".data\0" at the final
valid section string-table position.
Register the fixtures for autotools distcheck.
Several ISO tests used a bz2 suffix even though they read .Z files
and verify ARCHIVE_FILTER_COMPRESS. Rename them to use a compress
suffix so the test names match the filter.
The write option setter checks used several tiny source files with the
same basic shape.
Keep the same DEFINE_TEST cases. Put the write-side option setter
checks in test_archive_write_set_options.c. Remove the extra source
files from the build lists.
The read option setter checks used several tiny source files with the
same basic shape.
Keep the same DEFINE_TEST cases. Put the read-side option setter
checks in test_archive_read_set_options.c. Remove the extra source
files from the build lists.
The close-twice read checks used three tiny source files.
Keep the same DEFINE_TEST cases. Put the fresh-object, fd-backed,
and filename-backed checks in one source file. Remove the two extra
files from the build lists.
This rather required porting them to assertIs{Reg,Dir,Symlink} and away
from lstat.
This is possible now that 9046e753ca fixed
a number of other symlink issues on Windows.
Commit e1f890dc62 fixed the underlying
root cause of these OOB checks: If a header is invalid and ARCHIVE_FATAL
is returned, do not allow data reads anymore.
The checks while reading CFDATA already looked rather off, since the
check exists in header parser and the value is never updated, but
prevent worst case scenario.
Keep the tests as regression tests if archive_read.c is modified again.
This makes the code easier to read and audit.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The comment states that a warning should be returned, but
97f964e3e0 explicitly changed that to
fatal. Adjust the comment and remove unneeded function call.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The function ar_parse_common_header can be called at exactly one
position in _ar_read_header, since it parses common information.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The __archive_read_consume function is binary: Either all bytes are
consumed or a failure occurred. No partial consumption can occur.
Simplify its calls to clarify this.
While at it, adjust style of callers as well.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Several filename encoding tests were copied from KOI8-R or EUC-JP
cases, but now use CP1251 or CP932/SJIS instead.
Update skip messages, conversion messages, and comments to match the
locales and byte sequences used by the tests.
Also fix a typo in an EUC-JP ZIP filename skip message.
archive_read_format_7zip_read_header() already returns before this
point if zip_entry is NULL. The folder pointer is also assigned from an
in-bounds folder array entry, so the extra NULL checks are redundant.
The later DONT_KNOW reset is redundant as well, since the same state is
handled earlier in the function. Remove the dead checks without changing
the encryption detection logic.
If a file close to SSIZE_MAX in size is mapped into memory and checked
for mtree, a signed integer overflow could occur.
While this this generally harmless and the scenario very unlikely, fix
the issue with a proper cast.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
`archive_read_format_7zip_read_header()` initialized `entry_crc32` with
`crc32(0, NULL, 0)`, which is equivalent to zero for this accumulator.
The same function also formatted the constant string `"7-Zip"` into an
internal buffer for every entry, then assigned it as the archive format name.
The format name can be set directly to the constant string, so the extra
buffer and per-entry `snprintf()` are unnecessary.
Remove the unused buffer and redundant per-entry work.
assertEqualIntA() uses the archive pointer to print archive error details
when the assertion fails. Passing `a` while the asserted expression is
`archive_free(a)` can make the failure path inspect the archive after it has
already been freed.
We used to cache the entire target path, which presented a problem when
we used that target path to write a symlink. Future archive entries
could write "through" that symlink because we cached it as safe.
If values are only parsed and verified but not used later on, skip field
assignments. This reduces amount of fields to audit.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Make sure that all bytes are actually skipped. Normally, the result can
be ignored because the bytes were read previously. Here, more bytes than
previously read will be skipped.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If more than UINT_MAX byte are available, e.g. if a very large archive
is mapped into memory, the BZ2/DEFLATE avail_in/avail_out values are
truncated.
Set to maximum allowed value in such cases.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
With correct data types in place, simplify the overflow check by using
checked arithmetic and simple comparison.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Follow idea of internal libarchive APIs:
- File offsets and sizes are int64_t
- Memory offsets and sizes are size_t
Also use correct data types for iterator variables.
This refactoring also makes parse_7zip_uint64 obsolete by turning it
into parse_7zip_int64.
Resolves GHSA-m4vx-w6j3-jp82.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use checked arithmetic and unify unpack_streams check to cover all
cases. This should be a purely defensive measure.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Introduce parse_7zip_size, which performs the UMAX_ENTRY check.
Generally, use this function whenever memory allocation sizes or indices
into memory allocations are used, i.e. when size_t is meant.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use checked arithmetic to avoid overflows. Use it also where checks are
already in place for better readability.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Since we have checked integer arithmetic now, use it.
Clarify that INT64_MAX is the maximum according to standard.
With that, switch to int64_t to avoid (unsigned long)-1 casts.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Verify that sum of numUnpackStreams will never overflow numDigests. For
this, also set numDigests to size_t because that's how the variable is
used later on.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If the minimum amount of bytes could overflow size_t after alignment,
allocate exact minimum amount instead.
This avoids integer overflow and will most likely lead to regular OOM
error handling instead.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The skip_stream function is supposed to skip a specified amount of bytes
from input stream. Since streams could be larger than 4 GB even on 32
bit systems, do not cast to size_t. Instead, read the data in small
chunks until all bytes are skipped.
This also helps to avoid large allocations on 64 bit systems.
Also verify that skipped_bytes tracking cannot overflow.
While at it, change return value to int because it's only needed for
status information, i.e. if operation was successful or not. This avoids
another cast issue with uint64_t values larger than INT64_MAX.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Exercise atou64() through a TOC numeric element (<inode>) read via the
expat backend, which hands the parser its raw, non-NUL-terminated
character-data buffer. Mirrors test_read_format_xar_base64_oob; build
with ASan for the out-of-bounds coverage.
The final seek check never advanced actual_offset for the trailing
hole created by the EOF pad_to(), so extracting any sparse file whose
last region is a hole failed with ARCHIVE_FATAL "Seek error".
This affects the systemd CI which does such an operation, and now
fails since PR https://github.com/libarchive/libarchive/pull/3128
was merged and appeared in Archlinux:
[ 269.636946] TEST-13-NSPAWN.sh[5439]: + run0 --pipe -u testuser importctl -m --user import-tar - inodetest2
[ 270.132545] TEST-13-NSPAWN.sh[5440]: Enqueued transfer job 7. Press C-c to continue download in background.
[ 270.137660] TEST-13-NSPAWN.sh[5440]: Exporting '/home/testuser/.local/state/machines/inodetest', saving to 'pipe:[79515]' with compression 'uncompressed'.
[ 270.148434] TEST-13-NSPAWN.sh[5441]: Enqueued transfer job 8. Press C-c to continue download in background.
[ 270.173410] TEST-13-NSPAWN.sh[5441]: Importing 'pipe:[79515]', saving as 'inodetest2'.
[ 270.178790] TEST-13-NSPAWN.sh[5441]: Operating on image directory '/home/testuser/.local/state/machines'.
[ 270.386155] TEST-13-NSPAWN.sh[5440]: Operation completed successfully.
[ 270.389171] TEST-13-NSPAWN.sh[5440]: Exiting.
[ 270.689994] TEST-13-NSPAWN.sh[5441]: Failed to unpack regular file 'testfile': Seek error
https://github.com/systemd/systemd/actions/runs/28159904022/job/83436896561?pr=42736
A local and small regression test is added that replicates that test's
behaviour.
Miscellaneous fixes for 3.8.8
* configure.ac: Remove unused checks
* libarchive: Fix `TIME_MAX` and `TIME_MIN`
* test: Fix off-by-one in bounds check
Fixes: 0e4fa9ff0d ("test: promote the win32-repairing systemf wrapper to all systemf calls")
* bsdunzip_test: Set umask before asserting permissions
Fixes: 34b3da0013 ("unzip: Add symlink tests")
* archive_write_disk_posix: Fix type issues
Fix type issues in the HFS+ (de)compression code, primarily to avoid
arithmetic overflow when calculating the block count during compression.
* archive_write_disk_posix: Miscellaneous cleanup
Use `S_IS*()` macros consistently and clean up some `#ifdef`s.
* archive_write_disk_posix: Symlinks can have AppleDouble data
* libarchive: Fix format string issues
(applies to 32-bit builds of libarchive)
Files within one ZF_BLOCK_SIZE of 4 GiB (which is, incidentally, the max
zisofs-compressible file size) would trigger an overflow in the
calculation of the number of block _pointers_ they would require. We
could have rejected those files, but the truth is that they just require
exactly 131073 block pointers (131072 for the file itself and one for
the trailer.)
Writing a file near 4 GiB causes an overflow when we calculate the
number of block pointers we need to store, which leads to us allocating
the wrong amount of memory to store them.
This is visible with ASan:
Exercising: libarchive 3.9.0dev zlib/1.3.1 liblzma/5.4.4 bz2lib/1.0.8 libzstd/1.5.6 cng/2.0 libb2/bundled
706: test_write_format_iso9660_zisofs_overflow
=================================================================
==40444==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x08c004b4 at pc 0x006acb3b bp 0x002edb44 sp 0x002edb38
WRITE of size 1 at 0x08c004b4 thread T0
#0 0x006acb3a in archive_le32enc libarchive\archive_endian.h:219
#1 0x006c81d0 in zisofs_write_to_temp libarchive\archive_write_set_format_iso9660.c:7760
#2 0x006ca1bd in write_iso9660_data libarchive\archive_write_set_format_iso9660.c:1785
- Use contrib/untar's system_mkdir for extended portability
- Compile warning function only if it's actually used
- Use %H:%M instead of %R in strftime for extended portability
- Introduce system_unlink to add special directory symlink handling
Copy missing functionality from libarchive and cpio.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Crafted TOC with a base64-encoded <name> of odd length, which used to
make strappend_base64() read past the character-data buffer. The trailing
incomplete base64 character is dropped; the rest still decodes.
tar: mark bsdtar as long-path aware on Windows
tar -C/--directory option calls SetCurrentDirectoryW(), which is limited to MAX_PATH (260) characters unless the process opts in to longpath support via an application manifest and enables a registry setting or sets an option in developer settings.
Verify that symlink creations and overwriting works, i.e. that dangling
symlinks are properly overwritten.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Verify that -u does not override files which have a newer timestamp.
This also verifies that creation of timestamps properly works.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Verify that -f does not create new files and also does not override
files which have a newer timestamp.
This also verifies that creation of timestamps properly works.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
bsdtar's -C/--directory option calls SetCurrentDirectoryW(), which is
limited to MAX_PATH (260) characters unless the process opts in to long
path support via an application manifest declaring longPathAware.
Extracting into (or changing into) a directory whose path exceeds 260
characters fails with "could not chdir".
Embed a longPathAware application manifest into bsdtar.exe so these
operations work for long destination paths. Both Windows build systems
(CMake and the autotools/Makefile.am build) embed it, using different
mechanisms per toolchain:
- MinGW/Clang (GNU ld) do not generate an application manifest, so it
is compiled from a resource script (bsdtar.rc) via windres. This
manifest replaces the toolchain default, so it also declares the
asInvoker execution level explicitly.
- MSVC's linker already embeds a manifest (resource id 1); a duplicate
RT_MANIFEST resource fails to link (CVT1100). Instead the manifest is
merged into the linker's default manifest (asInvoker) by adding it to
the target sources.
The local functions be32dec and le32dec lead to conflicts on NetBSD. Use
a proper prefix as suggested by Dustin L. Howett.
Resolves#3170.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The input file in test_P_encryption uses deflate, which is only
available if zlib is available. Skip the test if built without zlib.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Allocate enough memory with initial malloc for possible additions, which
removes any kind of error handling required in idr_extend_identifier.
The additional memory allocated was already large enough. Clarify
exactly where the required amount comes from by using named constants.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Do not set 0 as error code, since this will be printed as success. Use
proper error codes in various places for better error messages.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Add archive_ckd_add_size and archive_ckd_mul_size. Use them for checked
arithmetic throughout the source code where explicit SIZE_MAX checks
were performed.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
entry_bytes_remaining goes negative for a streaming entry after the AES
header is subtracted; zip_read_data_zipx_xz passed it to zip_read_decrypt
as a size_t, causing an out-of-bounds read.
Co-Authored-By: Marco <marco@mpreimann.de>
Set the third argument to NULL if the amount of actually read bytes is
not needed. This simplifies code audits.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If a filename is larger than INT_MAX, eventual size_t/int/size_t
conversions lead to huge size_t values and subsequent OOB reads.
Use correct values. Before this would lead to unnecessary writes, the
filename length field check already aborts the operation.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If the filename length cannot be represented as octal in its field,
return an error instead of silently truncating the value.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Archives are not necessarily 256 bytes long, e.g. the last part of a
multi volume archive. Only try to read up to 256 bytes when looking for
an archive or disk name, not necessarily exactly 256 bytes. This matches
the behavior of regular entry name lookups.
Also, disk names can be empty. Handle this as well.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The cab reader does not support multi volume archives, but it should be
able to read the files to print information about content.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Make sure that the content of the link can fit into a size_t. This
should be always true, but be cautious with 32 bit systems and very
weird filesystems (possibly through fuse).
I took SSIZE_MAX as upper limit due to signedness and eventual readlink
calls which would fail with larger values anyway.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use size_t for avail_in, avail_out and stream_in for ppmd streams.
The fields avail_in and avail_out values are set in function decompress
based on size_t variables (t_avail_in/t_avail_out) and eventually
written back. The stream_in field is only incremented.
The actual use case happens within ppmd_read to support situations in
which not enough bytes are available. In such cases, more bytes are read
on demand but not written into next_in.
In such cases, avail_in can turn negative and next_in can point outside
of its allocated memory area.
Since stream_in is always incremented by one, it won't overflow on real
hardware, given that size_t would address the whole available heap
space.
Make sure that avail_in never turns negative (which allows the size_t
usage) and also guarantee that t_avail_in will never wrap around,
leading to a huge "used" value.
As a bonus, __archive_read_ahead can be reliably called with a NULL
argument now, since no more casting occurs for second argument, which
was missing in the test.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The cab_read_ahead_remaining function might return more bytes through
avail than initially asked for. The given limit is 255+1, i.e. the
maximum file name length.
While it's not a big deal for libarchive to handle file names longer
than that, the CAB format does not allow longer names.
Limit the amount of available bytes to the given argument for proper
checking.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If not enough bytes are available, __archive_read_ahead will return the
amount of bytes still available, which can be larger than 0. Only in
error cases, a negative value is returned.
Check the return pointer instead. It simplifies the error handling,
allows a NULL argument, and covers more truncation issues.
Right now, a NULL pointer with a non-zero size could be further
processed, which just asks for more technical or logical issues to
arise.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Avoid requesting the wide-character pathname when the tar reader only
needs to check whether a regular entry name ends in '/'.
archive_entry_pathname_w() can lazily convert the pathname to WCS. In the
common tar read path, the multibyte pathname is already available, so
checking it first avoids unnecessary per-entry conversion. The WCS fallback
is kept for cases where the multibyte pathname is unavailable.
The archive_integer.h header offers various integer operations to
libarchive, utilizing system functionality where possible.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
For empty-prefix ustar entries, copy the fixed-width name field
directly into the archive entry instead of first building a temporary
archive_string.
This avoids a temporary buffer allocation and intermediate copy in the
common case. It also fixes a small fatal-error leak by freeing the
temporary prefix/name buffer before returning on pathname conversion
failure.
lz4: Fix double-free on reallocation failure
Alternative version of https://github.com/libarchive/libarchive/pull/2945 which removes the test (which requires a modified malloc to actually fail the 4 MB allocation).
lz4_allocate_out_block() frees state->out_block without NULLing
the pointer. If the subsequent malloc fails, the function returns
ARCHIVE_FATAL with a dangling pointer. lz4_filter_close() later
calls free(state->out_block) again, triggering a double-free.
Also, state->out_block_size was updated before checking if malloc
succeeded, leaving inconsistent metadata on failure.
Fix both lz4_allocate_out_block() and lz4_allocate_out_block_for_legacy():
- NULL the pointer immediately after free
- Move size update to after malloc succeeds
- Reset size to 0 on allocation failure
Validate the accumulated unpack stream count before allocating
per-substream metadata.
Reject folders that declare multiple substreams without kSize metadata,
since the parser needs those sizes to describe the stream layout.
Drop the explicit digest zeroing loop. The arrays are allocated with
calloc(), so the entries are already initialized.
Add a 7z regression test for malformed SubStreamsInfo metadata that
declares more than one unpack stream without the kSize data needed to
describe those streams.
Store the archive as a .7z.uu file, matching the existing malformed
7z tests.
This is overly broad, and will reject some well-formed archives which
contain symlinks to trees which exist in the archive; however, this is
the best we can do without some rudimentary path parsing.
Fixes GHSA-q4gj-vh8c-67p8
This PR does not fix any reachable issue, but fixes the code in question nonetheless to prevent regressions in the future:
- Do not call `archive_copy_error` after `archive_read_free` to prevent a user after free bug
- Reset `vtable` to `NULL` to prevent `close` from being called after filter initialization error, since `data` is already freed and set to `NULL`, preventing a `NULL` pointer dereference
If a system with sizeof(wchar_t)=2 (e.g. Cygwin) tries to convert a wide
character string into a multi byte string representation, it
precalculates the required length with sizeof(wchar_t) instead of
MB_LEN_MAX. This can lead to short memory allocation for filenames which
have a shorter representation in wchar_t than in UTF-8.
Resolves GHSA-crm5-q56g-xw29.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
A system with sizeof(wchar_t)=2 (Cygwin on Windows) can trigger an out
of boundary write in archive_read_open_filenames_w when converting the
wide character string into a multi byte string.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The current finite state machine carefully handles short reads, i.e. the
loop can enter as often as needed until enough bytes arrive for the
current state to perform its actions.
This can be simplified by relying on __archive_filter_read_ahead to
return the amount of bytes actually needed. I assume that this did not
happen in the original code due to its age (2009) and evolution of
libarchive's internals over time.
Also, headers are only skipped at the beginning. As soon as the reader
starts returning data (ST_ARCHIVE reached), the filter pretty much
becomes a pass-through filter.
Split the initial lead and header skipping into its own function and
only keep track if the initial skipping was performed or not. This
greatly simplifies the reader function.
Also, it avoids book keeping of internal states and "total_in" tracking,
which I don't have to properly audit for edge cases anymore.
Last but not least, this refactoring properly reports truncated streams
now.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Allocate nulls only when needed, i.e. when sparse files are encountered.
This removes another malloc call from regular use cases.
While at it, call archive_set_error in case of allocation failure for
better error messages.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If lseek fails to seek to correct position, e.g. because the descriptor
is in append mode, do not use errno because it's not set.
In such a case, fall back to ARCHIVE_ERRNO_MISC.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The POSIX and Windows code did not check the result of
archive_entry_clone. Handle this error condition properly.
While at it, unify POSIX and Windows code a bit.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
LLP64 could theoretically overflow the 32 bit counter. It still needs a
lot memory to achieve this, but let's better be safe than sorry.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Check if allocation succeded. If not, properly handle the error instead
of eventually performing a NULL pointer dereference.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
We do not have to guess the amount of remaining bytes, since
archive_read_ahead returns the information.
Simplify the function.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
A malformed RAR5 archive with data_size=1 forces bytes_remaining
(ssize_t) to wrap to -2 when a compressed block header consumes
to_skip=3 bytes (CWE-191). That negative value is then implicitly
cast to size_t ~0 inside malloc(), requesting a ~16-exabyte
allocation — confirmed heap buffer overflow via ASAN/UBSan on a
48-byte crafted archive requiring no authentication.
Three guards added to archive_read_support_format_rar5.c:
1. Reject data_size > SSIZE_MAX before assigning to bytes_remaining
(CWE-195, unsafe unsigned-to-signed conversion)
2. Reject to_skip > bytes_remaining in process_block() before the
subtraction — this is the primary fix for the underflow (CWE-191)
3. Change cur_block_size == 0 to cur_block_size <= 0 in merge_block()
as defense-in-depth so that any negative bytes_remaining reaching
read_ahead() is caught before it becomes a malloc size (CWE-122)
A malformed RAR5 archive with data_size=1 forces bytes_remaining
(ssize_t) to wrap to -2 when a compressed block header consumes
to_skip=3 bytes (CWE-191). That negative value is then implicitly
cast to size_t ~0 inside malloc(), requesting a ~16-exabyte
allocation — confirmed heap buffer overflow via ASAN/UBSan on a
48-byte crafted archive requiring no authentication.
Reproducer: 48-byte crafted RAR5 archive; ASAN confirms
"allocation-size-too-big 0xfffffffffffffffe".
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Sometimes, 24 bit operations are needed. Enough users exist that moving
this into archive_endian.h is worth it.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Fix an unsigned integer overflow in get_pe_sfx_offset to properly
address the correct section.
Reported by shxz9u.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If vtable is not set to NULL, close function would be called during
shutdown. Since data is already freed and set to NULL, this would lead
to a NULL pointer dereference later on.
The called library functions should never fail though, so this is a
purely defensive measure against future lzma changes.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If archive_read_next_header in add_pattern_from_file would ever return
anything but ARCHIVE_OK or ARCHIVE_EOF, a use after free would occur
when copying error information.
Since this is impossible with current setup (format raw without any
further filter, thus only open_filename code), this change is a purely
defensive measure against future changes.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If a filename is larger than INT_MAX, an out of boundary write could
occur on 64 bit systems. Fix data types and ignore such huge file names.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If the original name cannot be duplicated, return ARCHIVE_FAILED instead
of ARCHIVE_WARN. The latter implies that the option is unknown, which is
not the case.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
All arithmetical operations are unsigned, and it makes sense to keep it
unsigned: The total_in value is written at the end of the stream and if
the value overflows, it's pretty much expected to be % UINT32_MAX.
Very unlikely that int64_t will ever overflow, but the fix is cheap.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The CreateSymbolicLinkW function is available since 0x0600 and is also
part of the Nano Server APIs. On earlier systems, don't even try.
Otherwise use it directly to simplify code.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
- Use a stack array for 22 bytes
- Entering the if-branch already implies that we will add data
- Use snprintf instead of strcpy
Even though snprintf is slower than strcpy, it's easier to verify and
since nobody complained so far about the malloc overhead, this should be
okay (for now).
As a bonus, this code cannot fail anymore, which previously meant that
file attributes were silently ignored.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The oldest version we support is 0x0502, while _WIN32_WINNT_XP is
0x0500. Drop support code for even older versions.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Define the macro WINAPI_FAMILY_PARTITION if it's missing, which can
happen on old Windows versions like Windows XP. Also add other missing
definitions to support compilation.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use proper memory barrier while checking for availability of function
WaitForInputIdle.
Since Vista, InitOnceExecuteOnce is a very simple idiom to handle such a
singleton setup. For earlier setups, accept a possible data race and
recover gracefully if two concurrent threads performed the initial
setup.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Move a static variable into struct archive_write_disk, which is a safe
location from a thread perspective.
Only create and use it if it's really needed, i.e. for systems which
lack support for extended attributes.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
This avoids all possible concurrency issues.
As a bonus, turn the whole array const to move it into read-only
section.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
This avoids all possible concurrency issues.
As a bonus, turn the whole array const to move it into read-only
section.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
This avoids all possible concurrency issues.
As a bonus, turn the whole array const to move it into read-only
section.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
It is possible to trigger an out of boundary write on 32 bit systems
with around 1 GB of data (with a line consuming most of that data) when
opened with archive_read_open_memory.
Cap the amount of data read at once at 2 * UUENCODE_BID_MAX_READ to
allow range checks to take place before a possible SSIZE_MAX overflow
can occur through avail_in. Also, discard any line longer than
UUENCODE_BID_MAX_READ since this should definitely be more than
enough, especially since in_cnt check already takes care of that.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The check for end of file (`avail == 0`) happens after the check for a `NULL` return value of `__archive_read_filter_ahead`.
Remove it, because at this stage, it's definitely truncated, not just end of file.
7zip: fix a number of issues in zstd detection
- -Wunused-function when ZSTD_compressStream is unavailable
- Incorrect automatic selection of 7Z_ZSTD when ZSTD_compressStream is unavailable
- Other instances of HAVE_ZSTD_H not matching HAVE_LIBZSTD
Co-authored-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Without this fix, the 7zip writer will fall back to zstd (when it is the
last available option) even if it could not be linked, then fail at
runtime with an unexpected error message.
The XXH32_init function allocates memory. Check if the allocation was
successful and return ARCHIVE_FATAL on error.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Some filter comments were not updated when the execution of external
filter programs was introduced.
Sync them with reality, including the actually performed commands.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The 7-ZIP archives written by libarchive can be streamed. Support such
archives by using a fallback if seek is not supported: As long as the
target position is somewhere ahead in stream, consume bytes until the
position is reached.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The third argument (available bytes) is not always evaluated. It is
perfectly fine to pass a NULL argument if it's not needed.
Also, some checks of the third argument can be removed, because in case
of an error (< 0) or in case of end of stream (0) the return value is
NULL anyway. If a NULL check is already in place, just use it.
Simplifies code audits because it's obvious that the available bytes are
not needed within the function if they are never stored.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Do not assume that enough bytes will be provided by filter if not
explicitly requested. The requested size is 76, but some checks expect
116 bytes. If these were not supplied by filter, th afio header
detection erroneously skips headers which otherwise could be found.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If enough bytes have to be skipped, a signed integer overflow could
occur (most realistically on 32 bit systems). Use an unsigned type,
which could still overflow but has only negative impact on diagnostic
warning message.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Verify that afio headers are detected even on unfortunate filter read
splits, i.e. when magic is found but rest of header has yet to be
retrieved.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
process_base_block() returned ARCHIVE_RETRY for HEAD_MAIN (and
HFL_SKIP_IF_UNKNOWN) blocks without consuming the body bytes the
sub-parser did not read. rar5_read_header() then re-parsed the same
region, turning an O(1) skip into O(N) and letting a crafted RAR5 file
stall the reader (GHSA-9h2c-464f-j3hj).
Record the block body start and skip any unconsumed bytes through a
small helper rar5_skip_remaining_block() before returning ARCHIVE_RETRY.
Add two regression tests derived from test_read_format_rar5_stored, each
with extra unread bytes appended to a no-data block's body (HEAD_MAIN and
an unknown HFL_SKIP_IF_UNKNOWN block); both fail on master and pass with
the fix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
If __archive_read_filter_consume fails, report information about
detected truncation.
In many cases, the return value can be ignored since
__archive_read_ahead already performed the check. In this case, we never
read the data before, so add the check here.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Integer promotion stops at unsigned int, thus addition of 4 to a
uint32_t can lead to an integer overflow.
Use int64_t to support all values.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Switch the release-time config.guess/config.sub fetches from http:// to
https:// and add -fsSL so a failed fetch errors out instead of writing an
error page into the helper script (CWE-494).
Apply same logic of gzip_filter_read from commit
256c71ad4e
to consumer_header, which contains the identical issue.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The variable is never read and it only allows a very unlikely integer
overflow if more than INT64_MAX bytes are read.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The pointer arithmetic could overflow with a large min value. Prevent
this by using subtraction instead of addition.
Resolves#3025.
Co-authored-by: ypp <alen0421@gmail.com>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The check for end of file (avail == 0) happens after the check for a
NULL return value of __archive_read_filter_ahead.
Remove it, because at this stage, it's definitely truncated, not just
end of file.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If not enough memory is available, skip tests. This can happen on 32 bit
systems with ASAN enabled. While these tests run perfectly fine if run
directly with libarchive_test, the test harness fails with eventual NULL
pointer dereferences, since these assert-checks do not stop processing.
Happens with lzma and xz and compression level 9.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Solaris iconv may perform an implementation-defined conversion for
non-identical characters instead of failing. In the tested conversions it
substitutes question marks and reports success, so these negative tests do
not match Solaris behavior.
On systems with a 32 bit time_t, archive_parse_date covers less values
than with a 64 bit time_t.
Adjust the expected return value for an overly large value.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The codec field is of type uint64_t, which is an unsigned long on many
64 bit platforms (LP64), but 32 bit platforms and LLP64 platforms cannot
use the l modifier for correct output.
Cast to uintmax_t for portable j support.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The Rock Ridge rr_move functionality for paths with a depth larger than 8
has a few bugs in it. For one, it might loop over the same entries over
and over. And second, rr_move/ directory entries are not verified to be
unique. Both issues can lead to NULL pointer dereferences.
Add a test case which highlights the NULL pointer dereference.
Based on issue #2936.
cpio: Fix UAF in error path
Add entry only after its full initialization into list. Otherwise the error handling of a failing strdup would have to unlink the entry again.
Fixes: 16ad931073 ("cpio reader: Validate pathname in record_hardlink")
Resolves#3053.
In case of errors, assertEqualIntA offers more information by accessing
archive's errno and error message.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The CMake build script was modifying global CMake variables (CMAKE_REQUIRED_LIBRARIES
and CMAKE_REQUIRED_INCLUDES) during crypto library checks (OpenSSL, MbedTLS, Nettle)
and Haiku libbsd checks without saving/restoring them using
CMAKE_PUSH_CHECK_STATE() and CMAKE_POP_CHECK_STATE().
This caused side effects where subsequent system-level checks (like
CHECK_TYPE_SIZE or CHECK_FUNCTION_EXISTS) inherited these library dependencies,
leading to incorrect feature detection in cross-compilation environments
(e.g., reporting that basic types like 'pid_t' are missing).
This patch ensures all such checks are properly scoped, improving build
robustness across different architectures (x86_64, AArch64, MIPS64, LoongArch64, etc.)
and cross-toolchain environments.
The slurp_central_directory function has been changed to always use
seek, which is not needed and also breaks streamable 7-Zip archives.
Resolves an issue observed in #3068.
Fixes: 71ef1c3852 ("7zip: Merge skip_sfx into get_data_offset")
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The C standard dictates that integer promotion increases data types up
to int or the largest argument in arithmetic.
This becomes problematic in `offset_in_buffer += 4 + frame_data_size`
which effectively is `size_t += int + uint32_t`.
If frame_data_size is UINT32_MAX, adding 4 overflows before the
overflown value is added to size_t.
On 32 bit systems, this can easily lead to an endless loop or out of
boundary access.
Fix this by properly checking if integer overflows would occur.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
It is possible to trigger an endless loop on 32 bit systems due to
a triggerable integer overflow.
Add a regression test for this.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use size_t where size_t arguments are expected (and cast avail to size_t
because we know at this point that it cannot be negative).
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use C comments, define variables at start of block, keep 80 chars per
line. While at it, turn a few variables const to clarify that they do
not change.
No functional change.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
In case of errors, assertEqualIntA accesses the given archive and prints
diagnostic information about errno and error string.
Since archive_read_free and archive_write_free free the memory of the
archive, this would just lead to memory issues in case of errors.
Use assertEqualInt instead.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
_warc_header() initialises a local archive_string hdr, fills it via
_popul_ehdr(), and frees it on the success path at the bottom. The
intermediate error return on r < 0 (the populated header exceeds
MAX_HDR_SIZE=512, reachable with a long enough pathname carried by
WARC-Target-URI) returns ARCHIVE_WARN without freeing hdr.
By the time _popul_ehdr returns -1 hdr already holds the WARC version
line, WARC-Type, WARC-Target-URI (long path), WARC-Date, Last-Modified,
WARC-Record-ID, and Content-Length headers --- ~1 KB+ per entry in
practice.
Reproduces with the bundled bsdtar against a regular file with a
~400+ char pathname:
ASAN_OPTIONS=detect_leaks=1 bsdtar --format=warc -cf out.warc <long_path>
=> LeakSanitizer: ~1 KB direct leak; stack pierces _popul_ehdr ->
_warc_header (archive_write_set_format_warc.c:245) ->
_archive_write_header -> ... -> main.
Code unchanged since the file's introduction in 2014; no existing test
exercises the long-pathname path. The warcinfo branch above (line ~217)
handles this correctly already (frees hdr unconditionally outside the
"if (r >= 0)" block); only the WT_RSRC branch was missed.
Identified by Neurolog, a code-analysis tool the reporter is developing
that combines Souffle Datalog with LLM-assisted fact extraction. The
reproducer was hand-validated under LeakSanitizer against current master.
If init_unpack fails to allocate memory for given window size, a
dangling pointer can lead to an eventual double free.
Resolves#3069.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
file_new() at archive_write_set_format_7zip.c:1688 calls free(file)
on the symlink-UTF8-failure branch, leaving file->utf16name
(allocated at line 1666) leaked. The two earlier free(file) calls
in this function (lines 1656, 1668) are correct because they happen
BEFORE utf16name is allocated, but the third one happens after.
The rest of the function uses file_free() on every other
post-utf16name error path; file_free() does free(file->utf16name)
followed by free(file), which is the cleanup convention. Replacing
free(file) with file_free(file) makes the symlink-error branch
consistent with everything else.
Reproduces with bsdtar in a non-UTF-8 locale with a non-UTF-8
symlink target:
ln -s "$(printf 'broken_\\xff\\xfe_link')" sym
LC_ALL=C ASAN_OPTIONS=detect_leaks=1 \
bsdtar --format=7zip -cf out.7z sym
=> LeakSanitizer: 30-48 byte direct leak; allocation site is
file_new (archive_write_set_format_7zip.c:1666).
Equivalent trigger: any libarchive caller that sets AE_IFLNK
filetype on an entry without ever calling
archive_entry_set_symlink() (then archive_entry_symlink_utf8()
returns NULL and the error branch fires).
Code unchanged since the 7zip writer's introduction; no existing
test exercises this error path.
Identified by Neurolog, a code-analysis tool the reporter is
developing that combines Souffle Datalog with LLM-assisted fact
extraction. The reproducer was separately validated under LeakSanitizer
against current master.
file_free() releases pathname, symlink, uname, gname, and hardlink but
omits fflags_text. When a XAR archive describes a file with <flags> or
<ext2> children (e.g. <SystemNoUnlink/>, <Compress/>), xml_parse_file_flags
/ xml_parse_file_ext2 populate xar_file->fflags_text via archive_strcat,
which heap-allocates. The buffer leaks on every file_free().
Reproduces with ASan+LSan via the bundled bsdtar:
bsdtar -tvf <xar-with-flags>
=> Direct leak of N bytes ... archive_strcat ... xml_parse_file_flags
Same shape as commit 6767cbe3 ("Free XAR xattr fstype metadata"), which
fixed the analogous miss in xattr_free().
Existing release of fflags_text in archive_string_free is a no-op when
the field was never populated (.s == NULL, free(NULL) is safe), so the
patch is harmless on the non-flags path.
Resolves#3058.
Avoid passing negative suffix offsets to the duplicate identifier
resolver. Clamp them where ISO9660 and Joliet identifiers decide
where the duplicate suffix should be written.
When libarchive is compiled with FreeBSD's native iconv instead of
libiconv, as happens with libarchive in the base system, we need to
configure iconv(3) to handle invalid sequences by returning -1, as
iconv_strncat_in_locale() assumes GNU iconv semantics.
Add entry only after its full initialization into list. Otherwise the
error handling of a failing strdup would have to unlink the entry again.
Fixes: 16ad931073 ("cpio reader: Validate pathname in record_hardlink")
Resolves#3053.
The integer-code dispatch table in archive_write_add_filter() maps
ARCHIVE_FILTER_LZOP to the function pointer archive_write_add_filter_lzip
(the lzip filter setter) instead of archive_write_add_filter_lzop.
Add a test for ISO9660 entries whose Joliet identifiers collide
after default identifier truncation. The writer should resolve
the duplicate identifiers successfully when the archive is closed.
Apply the same fix as done in commit
c246ec5d05 for non-wide character
strings.
Without this, empty strings lead to out of boundary read accesses.
Resolves#3046.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Make sure that empty wide character names do not lead to out of boundary
accesses.
Reported by shura-turtle.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The debug_buff array is populated but never read. If multiple threads
process compress streams, they access the same index variable, which
could lead to out of boundary writes.
Delete the debug code to fix this issue.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If an archive has more than UINT_MAX (4 GB) bytes available, the bzip2
filter will enter an endless loop.
Apply same logic of gzip filter from commit
256c71ad4e.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Low-distance repeat state is scoped to the current RAR3 LZ table. Reusing it after a new table starts can make the next match use the wrong offset, producing incorrect output and a file CRC error.
Reset lastlowoffset and numlowoffsetrepeats when parsing a new LZ table. Add a small handcrafted RAR3 fixture that leaves a low-distance repeat pending, switches tables, and verifies the decoded bytes after the boundary.
RAR3 recovery records can be stored as NEWSUB RR blocks with payloads larger than the old 1 MiB extended-header limit. These records are metadata-only for this reader, so after validating the header and guarding against header-size plus payload-size overflow, consume the remaining header bytes and payload instead of rejecting the archive.
Add a reference archive with a NEWSUB RR payload just over 1 MiB, and keep the malformed huge-size case covering the overflow/truncation path.
Currently, the code calls GetTempPathW to figure out required size for a
buffer larger enough to contain the temporary directory path, allocates
the memory, and then calls GetTempPathW again to populate the memory.
Since libarchive is designed with multi-threading in mind, the worst
situation would be that another thread modifies the environment variable
between these two calls.
Use a buffer of MAX_PATH + 1 (261) to basically cover all regular
situations. If long paths are enabled, reallocate until enough bytes
were available (32 kb is maximum) without another thread intefering.
Realistically, this will happen only once.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Reject malformed CPIO entries whose pathname field exceeds 1 MiB before asking the read-ahead layer to satisfy the padded pathname length.
This prevents newc archives with attacker-controlled c_namesize values from forcing large metadata read-ahead and pathname allocation during archive listing. Add a regression test that fails on the unpatched reader and passes once the cap is enforced.
Signed-off-by: datauwu <datauwu@users.noreply.github.com>
If multiple fflags are added to archive, a stack out of boundary is
triggered due to insufficient array allocation.
Also, if flags with long names are supplied, an out of boundary read can
occur.
Resolves GHSA-wfvr-54j8-47r9.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
It is possible to trigger an out of boundary stack write and out of
boundary reads with fflags.
Based on proof of concept reported by Cris Jacob Maamor.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
It is only relevant to know if a separator has to be inserted. This can
be simplified by only checking if string already contains characters.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Since libarchive already contains infrastructure to safely work with
strings, use it instead of manually constructing strings.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
window_buf and filtered_buf were allocated via calloc without checking
for NULL. Change init_unpack to return int and propagate ARCHIVE_FATAL
on allocation failure to the caller.
Add documentation for
archive_free
archive_position_compressed
archive_position_uncompressed
archive_read_open_filenames
archive_write_add_filter_by_name
archive_write_set_format_filter_by_ext_def
Note that archive_write_add_filter.* can return ARCHIVE_WARN, and
archive_read_support_filter.* can return ARCHIVE_FATAL
Chase rename of archive_position to archive_filter_bytes
The strcpy and sprintf functions are generally hard to reason about.
While they are safe in this context, I think, it's easy to refactor the
code to avoid them completely.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The code can be simplified to avoid strcpy usage. While not exactly a
much safer approach by manually adjusting characters in a string, this
attempt reduces size of libarchive and avoids unneeded copy operations.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The cmdline_set_path function contains logic to reallocate memory of
command path and copy a new value into it.
Inline a simpler free+strdup alternative into __archive_cmdline_parse.
Since the function is always called with empty data, free could be
removed as well. I've kept it that way just to make sure that it's 100 %
compatible with previous code.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Switching a multi-volume archive file with another active filter, e.g.
decompression, can lead to memory corruption due to modifying the wrong
private data (self->data).
Use highest upstream filter to replace the correct private data.
Resolves GHSA-qf8j-cq3h-8m2m.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Check that no memory corruption occurs when switching files which are
decompressed through filters.
Test case supplied by Stuart Wallace.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use a dedicated variable for archive instead of referencing
self->archive again and again. Also, inline client_open_proxy.
While at it, fix space intendation.
No functional change.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Parsing a string into an integer also means that boundary checks have to
be performed. Check within atou64 as well as outside by verifying that
subsequent casts won't truncate numbers.
Use error handling available by callers.
Reported by Muhammed Hussein.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The result of atol8 is cast to mode_t, which is also unsigned.
This brings atol8 closer to atol10.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The xml_data function is void, but some functions called in there might
fail. Prepare an error propagation for upcoming changes.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The variable is size_t and argument expects a size_t. Do not cast to
int, which could only lead to trouble (in theory).
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Add a test which contains a mode which does not fit into mode_t and
check if such an entry is properly rejected.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
- Release memory in case of allocation failure.
- Treat failure to add an entry to rr_moved as fatal error
Fixes ASAN test run with test_write_format_iso9660_null_deref
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The XAR xattr cleanup helper released the xattr name string but not the parallel fstype archive_string. Release fstype from the same xattr_free() ownership boundary.
Add a focused XAR sample with xattr fstype metadata and exercise it through the public read/free path. This gives leak-checking builds coverage for the xattr cleanup ownership boundary.
The len variable is used later on. Introduce a new one (debug length) to
limit the amount of characters written to stderr.
Could be further improved with %.*s but let's keep it as simple as
possible, since DEBUG is not default and has to be manually set in
line 89.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
A malformed Rock Ridge CE entry can point back into a continuation
area that has already been processed for the same file. This causes
the ISO9660 reader to parse the same SUSP records repeatedly.
Track the end offset of the CE data already processed for each
file_info and reject later CE entries whose start offset is before
that point. This keeps forward nested continuations valid, including
same-block continuations, while rejecting backward or overlapping CE
chains.
This check is done in read_CE(), where continuation data is actually
processed.
Check that archive_entry_sparse_reset resets all internal variables.
Test case created and issue reported by Linke Li.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Avoid requesting NEWSUB extended data through read-ahead while parsing the header. The full NEWSUB block size is still validated and consumed, but the extended data is not required to be present in memory during header parsing.
Add a test for a malformed NEWSUB header with a large packed size.
Allocate enough memory for possible addition of 3 characters within the
range of 0-Z. Since UTF-16 is in use, allocate 6 bytes + 2 bytes for the
terminating NUL character.
Also keep in mind that "l" is already size in bytes, which means that a
multiplication of 2 is not needed (and prevented overflow issues with
longer filenames).
Resolves#2935.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
It is possible to trigger an out of boundary write with short filenames
which contain illegal ISO9660 characters. For these files, Joliet IDs
are generated. If multiple files lead to the same ID (which can happen
because illegal characters are replaced with an underscore), 3
characters/digits in the range of 0-Z are added.
The addition could overflow the memory.
Based on PoC in #2935.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
3 characters/digits base 36 means that 46656 combinations are possible.
If a directory with even more conflicting identifiers is encountered, the
code would trigger an endless loop.
Fail with an error in such a case instead.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Strings pointed to by these variables are actually modified. They point
to modifiable data areas (own stack arrays or argv arguments), so the
code does not erroneously modify them. Instead, clarify that they are
modifiable by removing the qualifier.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Since glibc-2.43:
For ISO C23, the functions bsearch, memchr, strchr, strpbrk, strrchr,
strstr, wcschr, wcspbrk, wcsrchr, wcsstr and wmemchr that return pointers
into their input arrays now have definitions as macros that return a
pointer to a const-qualified type when the input argument is a pointer
to a const-qualified type.
Signed-off-by: Rudi Heitbaum <rudi@heitbaum.com>
The supplied nanoseconds of time keyword could be truncated due to
casting from int64_t to long (relevant for Windows and x86), resulting
in an incorrect value.
Since the implementation already caps the value at specific limits for
bug compatibility, just use the correct data type for parsing to not
make things worse.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
According to 7zip standard, up to 15 bytes can be used for the Codec ID,
but not more than 63 bit shall be used (by today):
- Switch to uint64_t to avoid 32 bit unsigned long on Windows
- Verify that Codec ID parser does not overflow the integer
If the Codec ID does not fit, treat it as a file format error.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If no flags are supplied, anchor flags are supposed to be not special.
This means that ^ at the beginning of a pattern should be treated as a
regular character.
This breaks current behavior, but complies with comments in code, i.e.
archive_pathmatch.h line 41/42.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The errno value is not correct here. If the error string of a failed
match is printed, use also its error code.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Patterns with a lot of asterisks may overflow the call stack, crashing
the application. Check the recursion depth. If it is too deep, fail
with an error.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
These functions can return negative values, in which case operation
itself failed. While internal libarchive libraries handle these cases,
the tools don't. Check for negative values in them as well.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
(UBSan occasionally finds something interesting and
often reports whacky non-bugs like this one. "Fixing"
it will make the real UB bugs easier to identify, so...)
According to C's integer promotion rules, `unsigned short` gets
promoted to _signed_ `int`, and shifting into the sign bit of an `int`
is technically UB. Explicit cast to `unsigned` quiets UBSan.
1. The XAR writer's path normalization code uses strcpy() to move
parts of a path string within the same buffer. The source and
destination ranges overlap, which is undefined behavior for strcpy().
2. Failure to check string length before accessing the last character
of a path component. For empty components (e.g., //), the length is 0,
and length-1 underflows to SIZE_MAX.
The bracket expression matching [ in the pathmatch engine fails to
handle malformed patterns, specifically when a closing ] is missing or
when high-byte characters are used. The scanner advances the pattern
pointer beyond the allocated buffer.
The ACL parser fails to validate buffer length when processing PAX
attributes (SCHILY.acl.access/default). The next_field() function
attempts to read a separator character from a pointer even when the
remaining length is zero.
Reported-by: Kamil Frankowicz
If the basename is '.', it means it is the root directory ('/'). Do not
append '/' to '.', as this will produce a path '/.', resulting in an
invalid mtree entry.
For example, using base.txz from FreeBSD:
tar -c -f - --format=mtree @base.txz | mtree -C
Only the Unix epoch format `@<timestamp>` can have a number with
more than 4 digits. So let's break out the numeric parsing into
a standalone uint64 parser and use it separately to parse epoch
timestamps (which are only limited by the range of time_t) and
other date components.
It also avoids a time-consuming leap-year correction for
nonsensically large year values.
Check for truncations before casting uint64_t to size_t for 32 bit
system safety.
Reported by Asaf Meizner.
Resolves additional notes in GHSA-rf5v-vf7c-6wvg.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
`list.h` contains a list of all the tests and is generated
by grepping the test source files for `DEFINE_TEST`.
Previously, it was generated at configure time.
This meant that if you added a new test to an existing
source file, you had to manually reconfigure.
This adds the necessary dependencies so that `list.h`
is regenerated whenever any C test source changes.
This ensures that new tests are always discovered automatically.
Note: If someone wants to update the autoconf-based
build system to do this, please send a PR.
As noted by @dunhor, the logic here was a little unclear.
Reorganize to do all the checks, then set up the decode
parameters only if the checks pass.
This has no functional change, it's just a legibility improvement.
The change to return FAILED for entry-specific issues uncovered
flaws in RAR5 handling of filter types:
* Supported filter types are verified in `parse_filter` and
were being also checked in `run_filter` -- the duplication
confused the error handling here.
* `do_uncompress_file` was only checking for `FATAL` from
the upstream filter logic, so failed to properly pass
`FAILED` errors through
The change to return `FAILED` instead of `FATAL` for issues that
impact a single entry (but don't necessarily terminate the entire archive)
created a bug in header parsing since `FAILED` wasn't handled in a
header-check loop.
Thank goodness for fuzzing CI!
archive_read_format_7zip_read_data_skip used to coerce any negative
skip_stream() return into ARCHIVE_FATAL. That is wrong in principle:
ARCHIVE_FAILED can legitimately propagate up from setup_decode_folder()
through read_stream() and skip_stream(), and the wrapper should not
upgrade it.
In the current encryption-partially test case this is empirically a
no-op because skip_stream() still returns ARCHIVE_FATAL via a second,
deeper code path through extract_pack_stream(). An inline TODO comment
flags that asymmetry for a follow-up audit.
Follow-up to 9fa772ab. An audit of the rar5 reader found many more
ARCHIVE_FATAL returns in data-decode paths that should be ARCHIVE_FAILED
so the caller can move on to the next entry after a corrupt one:
apply_filters Unsupported filter type
parse_tables Truncated huffman tables, decode failures (8 sites)
parse_block_header Unsupported header size, block checksum error
do_uncompress_block Decode code length / distance slot failures
Distance pointer overflow
decode_number / decode_code_length failures
merge_block Block size == 0 during merge
do_uncompress_file Invalid window size declaration
verify_checksums CRC32 / BLAKE2 mismatch
Programmer assertions, ENOMEM, true I/O errors, and propagation of
copy_string's window-buf-NULL FATAL return are intentionally kept as
ARCHIVE_FATAL because they are not recoverable per-entry conditions.
Follow-up to 4f148608. A code review found additional ARCHIVE_FATAL
returns in RAR4 data-decode paths that should be ARCHIVE_FAILED so the
caller can move on to the next entry:
read_data_block Truncated RAR file data
read_data_compressed PPMd "Invalid symbol" (3 sites)
parse_codes Zero window size is invalid
add_value Prefix found (second site)
make_table_recurse Huffman tree was not created
make_table_recurse Invalid location to Huffman tree specified
These are all per-entry parse/decode failures. As with the earlier
batch, the rar4 input position is tracked by rar_br_fillup so
read_data_skip will correctly advance past the damaged entry, and
RAR4 solid mode is not supported, so subsequent entries are not at
risk from a half-consumed shared decoder state.
Per-entry data errors (encryption, invalid filters, bad bitstream) now
return ARCHIVE_FAILED instead of ARCHIVE_FATAL. Update tests that were
asserting the old incorrect ARCHIVE_FATAL return codes.
The one exception is test_read_format_7zip_encryption_partially line 71,
which asserts ARCHIVE_FATAL after archive_read_next_header on the entry
following an encrypted entry: 7zip cannot skip an encrypted entry (the
decode-folder setup fails), so the skip legitimately returns ARCHIVE_FATAL
and the archive is done.
setup_decode_folder() returned ARCHIVE_FATAL for both header-level and
data-level encryption/filter errors. Header encryption is a true
archive-fatal condition; data encryption is per-entry.
Distinguish the two by returning ARCHIVE_FATAL when decoding archive
headers (header==1) and ARCHIVE_FAILED when decoding file content
(header==0). Fix the call site in read_stream() to propagate the
actual return value rather than mapping all errors to ARCHIVE_FATAL.
Two per-entry errors in the RAR5 data-reading path were returning
ARCHIVE_FATAL, preventing iteration over subsequent entries:
- "Reading encrypted data is not currently supported": the encryption
is per-entry; unencrypted entries in the same archive remain readable.
- "Invalid filter encountered": a malformed filter spec in one entry
does not make the rest of the archive unreadable.
Change both to ARCHIVE_FAILED.
ARCHIVE_FATAL means the entire archive is unreadable and no further
operations are valid. ARCHIVE_FAILED means the current entry cannot
be processed but iteration over subsequent entries may still succeed.
The RAR4 decompressor was returning ARCHIVE_FATAL from a large number
of data-parsing failures (invalid Huffman prefix, invalid PPMd sequence,
bad CRC, invalid symbol, etc.) that are per-entry errors. Because each
entry's compressed data region can be skipped using the packed_size
recorded in its file header, a decompressor error does not prevent
reading the next entry's header.
Change all such per-entry errors in the data-reading path
(read_data_stored, read_data_compressed, parse_codes, create_code,
add_value, make_table_recurse, expand, copy_from_lzss_window,
copy_from_lzss_window_to_unp) to return ARCHIVE_FAILED. OOM errors
and true I/O failures (rar_br_preparation truncated-data) remain
ARCHIVE_FATAL.
Three entry points in archive_read.c could return ARCHIVE_FATAL from
the format layer without setting a->archive.state = ARCHIVE_STATE_FATAL,
so subsequent API calls would not see the archive as fatally broken:
- archive_read_data_skip() unconditionally reset state to HEADER even
when the format's skip returned ARCHIVE_FATAL.
- archive_seek_data() and _archive_read_data_block() forwarded FATAL
from the format layer without recording it in the archive state.
Fix all three so that ARCHIVE_FATAL causes the state to become
ARCHIVE_STATE_FATAL, consistent with the existing behavior of
archive_read_next_header().
Clear invalid data when a header is invalid.
Check for data being larger than the allocated buffer.
Thanks to @ReverseWarrior for pointing out this issue,
and providing the fix.
An empty pathname caused a one-byte OOB read before the heap buffer in
write_path() due to two compounding bugs:
(1) misuse of bitwise & instead of logical &&, and
(2) missing gaurd for an empty pathname
isint() and isint_w() previously clamped values >= INT_MAX to INT_MAX
and returned success, allowing malformed ACL text to silently set IDs
to an arbitrary sentinel value. Change them to return -1 (a new
"overflow" indication) instead, and update all callers in both the
NFS4 and POSIX parsers (narrow and wide) to treat overflow as
ARCHIVE_WARN and skip the offending entry.
Add test_acl_nfs4_text.c with four test functions covering NFS4 ACL
text round-trips, audit/alarm entry types, numeric-ID handling
including the overflow boundary (INT_MAX - 1 accepted, INT_MAX
rejected), and malformed-entry error paths.
Improves the parsing of timestamps in a couple of ways:
* Saturate when timestamps exceed the range of time_t.
In particular, this provides more rational behavior on
systems with 32-bit time_t.
* Validate the format of overlong pax timestamps.
We previously failed to check that high-resolution
timestamps had only digits in the fractional part.
We now notice and ignore those with a warning.
archive_acl_text_len() counted the trailing ":id" digits only when
ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID was set, but archive_acl_to_text_l()
always writes them for USER/GROUP entries whose name is NULL. With a
7-digit or larger id the allocated buffer was too short, causing
append_id() to write past its end.
Fix the estimator to also count the extra colon and digits when the
name is NULL, matching the serializer's logic.
The wide serializer (archive_acl_to_text_w) had the opposite problem:
it passed id=-1 to append_entry_w() for NULL-name entries regardless
of the id value, causing a garbage character to be written in the name
field and the numeric id to be omitted entirely. Fix it to mirror the
narrow serializer by setting id = ap->id when wname is NULL.
Add tests for both the narrow and wide paths.
Darwin covers a wide range of platforms with similar but not identical
sets of libraries. MD5, SHA1 and SHA2 are available from libsystem on
all of these, but macOS also has them in libc and libmd. Restricting
our search to only libsystem means we can run configure on macOS and get
a config.h that also works for other Darwin platforms.
Also clarify that all systems will handle these SFX files, no matter on
which system libarchive is used.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If 7zip header cannot be found within roughly first 8 MB of data, skip
detection and assume that file is not a valid 7zip archive.
This avoids various 32 bit truncation issues as well as out of memory
conditions.
Reported by various individuals:
- Amemoyoi
- b4sh5i
- Jie Zhu
- Tikket
- wooseokdotkim
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use desired data types: Offsets in files are (u)int64_t, sizes in memory
are (s)size_t. Use signedness when needed or specified by APIs.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Return an int for error information and supply offset through a given
argument. This fits other function declarations and makes it much easier
to differentiate between status and "return value".
While at it, merge fallback mechanism of both functions.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
This removes the function skip_sfx which makes slurp_central_directory
and archive_read_format_7zip_bid use the same code now.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Its functionality is split off from archive_read_format_7zip_bid
and returns offset to actual data, i.e. it handles self extracting
(SFX) files if offset 0 is not already a 7zip magic.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Prepare content of archive_read_format_7zip_bid to be split into its own
function which eventually can be reused by slurp_central_directory.
No real functional change (split if-blocks).
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Directly request an unsigned char pointer. This removes casts within the
function check_7zip_header_in_sfx and its surrounding caller code.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
We allocate space early on to support the advertised number of
files. A malicious archive can set a nonsensical value here to exhaust
memory. This adds a check comparing the number of files to the number
of streams and the size of the total header.
Note that the just-added test does not actually fail without this.
The existing code recovers if the allocation fails, which it typically
will. The new check tightens the limit so that we reject nonsensical
file counts and avoid problems from large memory allocations.
Extend test coverage to ensure all supported filters can be appended
to an archive reader before it is opened, matching the behavior
required to fix#2514.
Added tests for: BZIP2, LRZIP, LZ4, LZIP, LZMA, ZSTD, RPM, UU, and NONE.
Calling archive_read_append_filter(a, ARCHIVE_FILTER_COMPRESS) would
previously trigger a crash because compress_bidder_init() attempted to
read header bits from the upstream filter immediately. If the archive
was not yet opened (common when setting up filters), the upstream filter
state was not ready for reading.
This commit defers the header reading and decompressor initialization
until the first read operation (lazy initialization), consistent with
other filter implementations in libarchive.
Added test_read_append_compress_filter.
Bug: https://github.com/libarchive/libarchive/issues/2514
Test: ./libarchive_test test_read_append_compress_filter
Initialize the Huffman table to invalid values, which doesn't otherwise
affect the computation but avoids use of uninitialized values upon
extraction of some archives (as reported by `valgrind`).
Currently, libarchive_{random,util}.c use a couple bcrypt functions
regardless of whether HAVE_BCRYPT_H is defined as there are no other
implementations for Windows, but the actual <bcrypt.h> header is
included only under this macro.
To be able to build libarchive with ENABLE_CNG=OFF (for example, to
prefer a different crypto/digest engine) on Windows, don't guard
the include in these two files. In that case, bcrypt will still be
used, but only as an RNG.
This won't break anything because, as mentioned above, bcrypt is
used unconditionally here and if it's not present in the system,
the library won't build either way, with or without the change.
At least until we implement an RNG for Windows based on something
else.
Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
If a checksum has been provided through archive_entry and no data is
written, then digest internal memory is not released.
Fix this by always calling final functions, even if result is not
needed.
Using cmake within VSCode leads to creation of files which are not covered
by .gitignore so far. Covers Linux and Windows builds.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The function isofile_gen_utility_names could resolve .. directory
entries in a way that dirname will start with "../". If this happens,
the while-loop is unable to detect this because it forwards until the
cursor detects a slash again.
Fix this by also taking "../" at the beginning into account. Such an
entry can happen if "../../" points before the top directory.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If dot dot directory entries point before top directory, it can happen
that "../" will stay at the start of the path.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The isofile_gen_utility_names function normalizes directories, including
dot dot directory entries. If such an entry has multiple slahes and leads
to the top directory, then the new path erroneously becomes absolute.
Skip multiple slashes.
If rp is not NULL, then it points to a slash already. Takes this into
account to unify the rp and dirname cases a bit more.
Resolves#2937.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Normalizing dot dot directory entries with multiple slashes leads to
memory issues further down the call stack.
Reported in #2937.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Resolving paths like "dir/../filename" to "filename" can lead
to a strcpy call with overlapping memory. Use memmove instead,
which already happens at times in isofile_gen_utility_names.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Ubuntu patches zlib to use DFLTCC hardware DEFLATE acceleration on s390x
on compression levels 1-6, causing levels 1 and 6 to produce output with
the same lengths. Avoid the resulting test failure by comparing the
output lengths of levels 1 and 9 instead.
Tested on Ubuntu 24.04 on s390x hardware.
Fixes: https://github.com/libarchive/libarchive/issues/1515
There have been multiple instances of test cases being added to the CMake
build but not the Autotools one, thus omitting them from the released dist
tarball. Prevent this by testing the CMake build during `make distcheck`.
Remove an #include controlled by a preprocessor symbol that nothing
defines. I'm not sure if this has ever been needed, or what for, but
it serves no purpose today.
The link resolver is a helper utility that tracks linked
entries so they can be correctly restored. Clients add link information
to the link resolver and incrementally query it to correctly
link entries as they are restored to disk. The link resolver
incrementally releases entries as they are consumed in order
to minimize memory usage.
The `archive_entry_linkresolver_free()` method cleans up
by repeatedly querying the cache and freeing each entry.
But this conflicted with the incremental clean up,
leading to double-frees of leftover items.
The easy fix here is to have `archive_entry_linkresolver_free()`
just repeatedly query the list without trying to free, relying
on the incremental clean up mechanism.
Credit: tianshuo han reported the issue and suggested the fix.
A crafted ISO with pz_log2_bs=2 and pz_uncompressed_size=0xFFFFFFF9
causes an integer overflow in the block pointer allocation in
zisofs_read_data(). On 32-bit, (ceil+1)*4 wraps size_t to 0, malloc(0)
returns a tiny buffer, and the code writes ~4GB past it.
The pz_log2_bs validation fix prevents this. Add a regression test with
a crafted 48KB ISO that triggers the overflow on unfixed 32-bit builds.
Those 9 are not used anywhere outside the file (the actual
functionality is exported as a callback structure).
Make them static for a bit better compiler optimization
opportunities and, more important, to avoid symbol conflict
when static linking libarchive and any library which uses
the original Ppmd*.c from the LZMA SDK (like minizip-ng).
Also remove a couple declarations and macros not used
anywhere at all while we're here.
Signed-off-by: Alexander Lobakin <alobakin@mailbox.org>
These two filters failed to correctly set a name when
being registered, which prevented them from working
correctly with archive_read_append_filter()
Thanks to @fdegros for the test case demonstrating the lrzip failure
If the -R command line argument is supplied with user/group names
multiple times, memory leaks occur.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The anchor characters ^ and $ have only special meanings if they are
located at the beginning (^) or at the end ($) of the pattern. And even
then they are supposed to be only special if flags are set.
If they are located within the pattern itself, they are regular
characters regardless of flags.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
- Keep a whitespace between text and brackets
- No whitespace between text and colon
- No newline at end of error message
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
By only removing periods from error messages in Windows specific code,
but not adjusting its POSIX counterpart, the test fails on Windows but
not on POSIX systems.
Fix this by removing the period in test and in POSIX error messages.
Fixes: 3e0819b59e ("libarchive: Remove period from error messages")
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Calling archive_set_error with a message and errno already indicates
that a failure occurred. Only a minority of functions did that: Unify
with the rest.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The error messages are mostly written without a period. This makes
sense, because they can be accompanied with a strerror(errno) call,
giving more information: most likely a colon is appended.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The buff variable is only used in entry_to_archive. Moving it into the
specific code block where it is actually used reduces its visibility and
thus makes it easier to read the code:
Since Windows indeed uses unsigned for read, this makes it much easier
to verify that buff never grows and cannot be too large.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The API allows to set int64_t uid/gid values. When writing pax archives,
such large values are properly set in the USTAR header of actual data in
base256, i.e. everything works.
The pax header entries might be missing though because the check
truncates these values to unsigned int. Larger values could be truncated
in a way that they seem smaller than (1 << 18).
The check in line 1427 which sets the uid/gid values into the PAX header
block is correct, truncating the actual value to max. octal
representation.
This is a purely defensive change to support parsers which actually
allow such large uid/gid values but do not understand base256 encoding.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Make sure that supplied values cannot overflow int64_t, which would be
an undefined behavior. Also do not accept empty strings as numbers.
Last but not least, check values before casting them to target data
types to avoid truncations.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Make sure that supplied option values fit into data types. If not,
return an error instead of silently truncating values.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Otherwise an unknown error is printed, which does not help the user
(especially since a custom message is printed in this case already).
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Make sure that supplied arguments actually fit into target data types.
Print an error if numbers are too large instead of silently truncating
them.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Only a minority of calls to lafe_errc state explicitly that an error
occurred. Remove these few cases for a more unified argument handling.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Do not print that at least one mode has to be supplied. Exactly one mode
has to be supplied instead.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Complain only if contradicting mode options are supplied. Passing the
same argument multiple times should not lead to errors.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If a filter option is recognized but its value is invalid, return
ARCHIVE_FAILED instead of ARCHIVE_WARN. The latter is used for unknown
options, e.g. at the end of the option setter functions.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The buffer has a fixed 1024 limit, which can be smaller than the maximum
allowed path length. Increasing the limit to 4096 would partially help,
but since leading spaces are stripped, input lines could be valid and
longer.
Use getline instead.
Let cpio_rename work with the entry directly. In this initial step, it
moves buff into the stack.
The comment suggests that this was done in the past already.
### Summary
This PR fixes a NULL pointer dereference in `archive_acl_from_text_nl()` (located in `archive_acl.c`) that occurs when parsing malformed PAX tar archives containing a short "default" ACL prefix.
### Technical Details
This is a variant of the bug previously fixed in the wide-character version `archive_acl_from_text_w()` via commit 7a6549a6 (Issue #2744).
The vulnerability exists because `st = field[n].start + 1` is calculated before verifying if the field length is zero. In cases of malformed entries, `field[n].start` can be NULL, leading to Undefined Behavior/SIGILL. This patch applies the same logic from the wide-char fix: moving the pointer increment after the length guard.
### Validation
- **Environment:** Ubuntu 24.04, compiled with AddressSanitizer (ASan) and UndefinedBehaviorSanitizer (UBSan).
- **Reproduction:** Verified that the minimized PAX tar archive provided in issue #2904 no longer triggers the crash.
- **Regression:** All existing library tests pass.
Closes#2904
On LLP64 systems like Windows 11, overly large uid/gid values would be
truncated.
Same is true on Linux 32 bit.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
LLP64 systems like Windows 11 have a 32 bit long. Cast int64_t to
long long for better output.
Same is true for 32 bit systems.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
When parsing a malformed CAB file, the skip routine (cab_checksum_finish) blindly calculated the checksum on an uninitialized cfdata->memimage. This patch adds a NULL check before the checksum calculation and includes a standalone test case with a minimized malformed payload to prevent regressions.
When a PPMd-compressed block updates dictionary_size, the LZSS window
from a prior block is not reallocated. The allocation guard only checks
if dictionary_size is zero or the window pointer is NULL, not whether
the existing window is large enough. This allows copy_from_lzss_window()
to read past the allocated buffer.
Fix the guard to also check whether the current window is undersized.
Add bounds checks in copy_from_lzss_window() and parse_filter() as
defense in depth.
The zisofs block size exponent (pz_log2_bs) read from the Rock Ridge ZF
extension entry is used directly in shift expressions without validation.
The zisofs specification only permits values 15, 16, or 17 (corresponding
to 32K, 64K, and 128K block sizes).
When pz_log2_bs >= 64 on 64-bit systems (or >= 32 on 32-bit), the
expression (size_t)1UL << pz_log2_bs is undefined behavior per C11
6.5.7. On 32-bit systems, a large exponent also causes the block pointer
allocation size computation (ceil + 1) * 4 to overflow to zero, leading
to a heap buffer overflow write after malloc(0).
Fix: reject any pz_log2_bs outside the range [15, 17] by disabling
zisofs for the entry (file->pz = 0), which prevents the zisofs
decompression path from executing.
Found by fuzzing with ASAN/UBSAN.
`archive_read_support_format_cab` allocates a fresh context structure on
each call before registering the CAB format with libarchive. On the
second call, however, the registration step reports the format is
already registered, so the function frees the newly allocated context
structure — it is not needed, since the one from the first call is
already in use.
The context structure contains a `ws` field of type `archive_wstring`.
During initialization, `archive_wstring_ensure` is called on that field,
which performs its own heap allocation.
The cleanup path described above frees the context structure without
also releasing the memory owned by the `ws` field, causing a leak.
Fixed by calling `archive_wstring_free` on the `ws` field before freeing
the context structure.
Fixes#1980.
When the same archive_read object registers rar5 format more than once,
__archive_read_register_format returns ARCHIVE_WARN (duplicate bid).
The error path then called rar5_cleanup(ar), which dereferences
a->format to get the rar5 context — but a->format is NULL until
archive_read_open() is called, causing a SIGSEGV. The newly allocated
rar struct was also leaked.
Fix by introducing rar5_deinit() (the inverse of rar5_init) and using
it in both rar5_cleanup() and the registration error path. On failure,
the locally allocated rar is freed directly without going through
a->format. The function now always returns ARCHIVE_OK, consistent with
all other format handlers.
Fixes#1963.
If a file declares more than 8192 filters at once, without consuming
them, then the filter allocation function leaks memory.
The backing array used for storing filter pointers can hold up to 8192
pointers. After that it won't accept any new entries. The problem was
that the caller code didn't check if the backing array has accepted the
pointer; it silently assumed the pointer was registered, disposing the
only variable that was holding the pointer to allocated memory.
The fix is to fail the creation of a new filter structure if the backing
array is full. This will result in failure to unpack, but having more
than 8192 filters in one file at the same time seems unlikely.
Fixes issue #2891
The fuzzer fails to update entries[i] after archive_entry_linkify()
modifies the pointer (caching or swapping entries). This causes the
cleanup loop to free entries that were already freed via spare or
the drain loop, resulting in heap-use-after-free.
Fix: add entries[i] = entry after the linkify call to track ownership.
Calculate the filename length only once during read_file_data
initialization and replace strcpy with memcpy.
As a side effect it also helps silencing valgrind warnings due to strcpy
word-based optimization
https://github.com/libarchive/libarchive/issues/2887
If run as root (as is the case in CI), switch to an unprivileged user
(default: nobody) before running each test, and switch back after.
This makes it possible to write tests that rely on file permissions.
Note that tests that use the UID or GID must now check the EUID / EGID
instead as they will be different while the test is running. The only
way to avoid that is to run each test case in a child process, which
would hugely increase the complexity of test_run().
When create_dir() fails to create a directory, it immediately checks to
see if the directory already exists, which can happen if it's been given
something like foo/../foo. Unfortunately, this clobbers errno, which
means that create_dir() always reports ENOENT, regardless of the actual
error. Fix this by only performing this extra check if errno is EEXIST,
then reset errno to either EEXIST or ENOTDIR depending on the outcome.
The header is a series of blocks, most of which contain
values of just a few bytes (sizes, times, etc). The only
exceptions are the filename and directory name attributes,
which will be limited by the MSDOS/Windows maximum file length
limit. So it seems unlikely that this will ever exceed 64k.
(If we find counter-examples, we can easily extend this limit.)
Previously, we tested many combinations of FreeBSD version, build system, and filesystem.
In practice, that's redundant, and we've started seeing these excessive requests get throttled by Cirrus.
Cutting back to just 3 combinations should suffice and reduce the risk of throttling.
If an incorrect option value has been passed to a filter, it is possible
that library operations continue without even printing a warning.
This can happen because the special value "ARCHIVE_WARN - 1" is only
checked for filter issues, not format issues. Since this special value
is larger than ARCHIVE_FAILED, such failures are silently discarded.
Fix this by checking for this magic value for formats as well.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The crash reported in this issue is caused by a segmentation fault
that happens when a pointer `p` passed to `check_7zip_header_in_sfx`
is dereferenced. That argument `p` is defined as `p = buff + offset` in
`archive_read_format_7zip_bid` with `offset` determined by a call
to `find_elf_data_sec`.
Within `find_elf_data_sec`, a 64-bit ELF section offset is cast
to `ssize_t`. For a sufficiently large offset, such as an unsigned
64-bit value exceeding `SSIZE_MAX`, the `ssize_t` will be negative,
which leads to `p` to point before the start of the buffer.
This patch eliminates the problem by:
1. Reading the ELF section offset into a `uint64_t`.
2. Validating that the offset does not exceed `SSIZE_MAX`.
3. If the offset is invalid, breaking the loop to retain the safe
default `SFX_MIN_ADDR`.
These checks ensure that `min_addr` is always within the bounds of the buffer.
This PR includes a regression test `test_malformed3` in
`test_read_format_7zip_malformed.c` using a crafted reproducer file.
CodeMender verified with AddressSanitizer that the crash is resolved and the
test suite passes.
Co-authored-by: CodeMender <codemender-patching@google.com>
Fixes: https://issues.oss-fuzz.com/issues/470963352
Verify that archive_entry_acl_from_text() and
archive_entry_acl_from_text_w() return ARCHIVE_WARN instead of
crashing when given a bare "d" or "default" string with no
subsequent tag field.
Without the accompanying fix in archive_acl.c this test triggers
a NULL-pointer dereference (SEGV) in archive_acl_from_text_w().
When parsing a short "default" ACL prefix (e.g. L"d") with no
subsequent tag field, field[n] is left as {NULL, NULL} and the
code dereferences it unconditionally in the switch statement,
causing a SEGV.
Add a zero-length check after computing the field length so that
malformed entries are skipped with ARCHIVE_WARN, matching the
documented contract. Also move the st pointer computation after
the guard to avoid dereferencing a NULL start pointer.
Fixeslibarchive/libarchive#2744
If an entry pathname is set only by WCS or UTF-8, it may not have any
MBS representation in the archive's hdrcharset. Do not crash or create
an archive with an empty pathname. Furthermore, the entry pathname may
not have any MBS representation in the current locale. Do not report a
`(null)` pathname in the error message.
On Windows, since commit ae54394104 (Implement a string conversion
interface..., 2011-05-09, v3.0.0a~398), `archive_string_append_from_wcs`
no longer returns an error on use of replacement characters in non-C
locales. Restore the error to avoid silent use of replacement
characters, and for consistency across platforms.
Prior to commit c30f279475 (Complete support for UTF8 encoding
conversion, 2020-05-31, v3.5.0~32^2), `archive_mstring_get_mbs_l` returned
an error on failure to convert WCS to MBS. Restore the error so callers
can distinguish conversion failure from having no string in any form.
On Windows, `archive_mstring_get_mbs_l` from WCS with `sconv != NULL`
internally calls `archive_string_append_from_wcs_in_codepage` more than
once, with both `sconv != NULL` and `sconv == NULL`. Due to another bug
in the `sconv == NULL` case, we cannot enable all test combinations yet.
This change adds support for the following test scenarios:
- 15.0-RELEASE - ZFS
- 15.0-STABLE - UFS
- 15.0-STABLE - ZFS
This additional testing aims to catch issues with 15.x, as well as
ensure libarchive use doesn't regress when run on ZFS-based hosts.
Signed-off-by: Enji Cooper <yaneurabeya@gmail.com>
Building on illumos currently fails with:
```
ld: fatal: unrecognized option '--gc-sections'
```
This happens because `--gc-sections` isn't supported on illumos `ld`.
This patch updates CMakeLists.txt to skip unsupported linker options on
illumos. The flags used on other operating systems are optimizations
that don't affect correctness, so this change is safe.
Complete the refactoring of all 25 fuzzers:
- Remove duplicate Buffer struct definitions from 15 format fuzzers
- Remove duplicate DataConsumer class from 7 API fuzzers
- Update consume_bytes() calls to match new signature
- All fuzzers now use shared helpers from fuzz_helpers.h
This eliminates ~1000 lines of duplicated code.
The test runner already lists available tests if it fails to parse the
command line, but add a -l option to explicitly do this without also
printing an error message and a summary of options.
Adds a 'bsdtar-recovery' Android build target for use in Android recoveries
as a static binary, and fixes some build failures on the get-go.
Tested on halium-7.1, halium-9.0 & halium-13.0.
Change-Id: I9b656e7016d4bf21517e2edb18f2a7733edc6982
This reverts commit d8aaf88c9f, reversing
changes made to ee49ac8106.
tree_reopen() and tree_dup() return NULL only of they
are unable to allocate memory. Otherwise libarchive enters
ARCHIVE_FATAL if trying to walk an enterable but unreadable
directory.
__archive_ensure_cloexec_flag() operates only on fd >= 0
so there is no need to skip it
I have reimplemented the check around fdopendir()
Reported by: Christian Weisgerber from OpenBSD
Uses the sed-like way (and Java-like, and .Net-like, and Javascript-like…) to fix this issue of advancing the string to be processed by one if the match is zero-length.
Fixeslibarchive/libarchive#2725 and solves libarchive/libarchive#2438.
Since LZ4 and zstd share the same format for skippable frames, we need
to skip over these frames when trying to detect the format of compressed
data. Let's read up to something like 64kb of data when performing this
scanning.
Note that the LZ4 specification advises against starting with a skippable
frame, but doesn't forbid it:
> For the purpose of facilitating identification, it is discouraged to
> start a flow of concatenated frames with a skippable frame. If there
> is a need to start such a flow with some user data encapsulated into
> a skippable frame, it's recommended to start with a zero-byte LZ4
> frame followed by a skippable frame. This will make it easier for
> file type identifiers.
Resolves#2692.
This is expected to fail until a followup commit, because lz4 and zstd
skippable frames are the same format and we don't skip over those when
performing format detection (yet).
Relates to #2692.
Whenever we need to create a temporary file while writing to disk on a
POSIX system, try to create it in the same directory as the final file
instead of the current working directory. The target directory can
reasonably be expected to be writable (and if it isn't, creating the
file will fail anyway), but the current working directory may not be.
While here, consistently use __archive_mkstemp(), and increase the
template from six to eight random characters.
Fixes: 2e73ea3a7d ("Fix max path-length metadata writing (#2243)")
Fixes: e12c955dca ("Unify temporary directory handling")
Raise the maximum size of Mac metadata from 4 MiB to 10 MiB, as that is
the value used by Apple themselves in the version of libarchive included
in Darwin.
Provide preprocessor macros for two recurring magic numbers in the zip
support code: the length of the local file header (30 bytes) and the
maximum allowable size for Mac metadata (4 MiB).
In archive_util.c, we have a private function named get_tempdir() which
is used by __archive_mktemp() to get the temporary directory if the
caller did not pass one.
In archive_read_disk_entry_from_file.c, we use the same logic with a
slight twist (don't trust the environment if setugid) to create a
temporary file for metadata.
Merge the two by renaming get_tempdir() to __archive_get_tempdir() and
unstaticizing it (with a prototype in archive_private.h).
When compiling libarchive using clang in module mode a special
module.modulemap file describes the structure of the header files
so that they can be imported modularly. Having this file makes
it easier for modular uses of the library out of the box so that
clients don't need to write their own, potentially making errors
in doing so.
Add a module.modulemap in the public header file location so that
clang and related tools can find it easily.
Our tar header parsing tracks a count of bytes that need to be
consumed from the input. After each header, we skip this many bytes,
discard them, and reset the count to zero. The `V` header parsing
added the size of the `V` entry body to this count, but failed to
check whether that size was negative. A negative size (from
overflowing the 64-bit signed number parsing) would decrement this
count, potentially leading us to consume zero bytes and leading to an
infinite loop parsing the same header over and over.
There are two fixes here:
* Check for a negative size for the `V` body
* Check for errors when skipping the bytes that
need to be consumed
Thanks to Zhang Tianyi from Wuhan University for finding
and reporting this issue.
Depending on header search path ordering, we can easily
confuse libarchive_fe/err.h with the system header.
Rename ours to lafe_err.h to avoid the confusion.
Rename libarchive_fe/err.c to match.
We reuse the compression buffer to format the gzip header,
but didn't check for an overlong gzip original_filename.
This adds that check. If the original_filename is
over 32k (or bigger than the buffer in case someone shrinks
the buffer someday), we WARN and ignore the filename.
In archive_write_header(), if the format method or a filter flush method
fails, we set the archive state to fatal, but we did not do this in
archive_write_data() or archive_write_finish_entry(). There is no good
reason for this discrepancy. Not setting the archive state to fatal
means a subsequent archive_write_free() will invoke archive_write_close()
which may retry the operation and cause archive_write_free() to return
an unexpected ARCHIVE_FATAL.
If a fatal error occurs, the closer will not be called, so neither will
BZ2_bzCompressEnd(), and we will leak memory. Fix this by calling it a
second time from the freer. This is harmless in the non-error case as
it will see that the compression state has already been cleared and
immediately return BZ_PARAM_ERROR, which we simply ignore.
The closer will not be called if a fatal error occurs, so the current
arrangement results in a memory leak. The downside is that the freer
may be called even if we were not fully constructed, so it needs to
perform additional checks. On the other hand, knowing that the freer
always gets called and will free the client state simplifies error
handling in the opener.
Close all the file descriptors in the range [3 ..
sysconf(_SC_OPEN_MAX)-1] before executing a filter program to avoid
leaking file descriptors into subprocesses.
Bug: https://github.com/libarchive/libarchive/issues/2520
This fixes an unhelpful "Couldn't visit directory: Unknown error: -1" message.
Fixes: 3311bb52cb ("Bring the code supporting directory traversals from bsdtar/tree.[ch] into archive_read_disk.c and modify it. Introduce new APIs archive_read_disk_open and archive_read_disk_descend.")
RAR5 reader had inconsistent sanity checks for directory entries that
declare data. On one hand such declaration was accepted during the
header parsing process, but at the same time this was disallowed during
the data reading process. Disallow logic was returning the
ARCHIVE_FAILED error code that allowed the client to retry, while in
reality, the error was non-retryable.
This commit adds another sanity check during the header parsing logic
that disallows the directory entries to declare any data. This will make
clients fail early when such entry is detected.
Also, the commit changes the ARCHIVE_FAILED error code to ARCHIVE_FATAL
when trying to read data for the directory entry that declares data.
This makes sure that tools like bsdtar won't attempt to retry unpacking
such data.
Fixes issue #2714.
This commit fixes multiple issues found in the function that parses
extra fields found in the "file"/"service" blocks.
1. In case the file declares just one extra field, which is an
unsupported field, the function returns ARCHIVE_FATAL.
The commit fixes this so this case is allowed, and the unsupported
extra field is skipped. The commit also introduces a test for this
case.
2. Current parsing method of extra fields can report parsing errors in
case the file is malformed. The problem is that next iteration of
parsing, which is meant to process the next extra field (if any),
overwrites the result of the previous iteration, even if previous
iteration has reported parsing error. A successful parse can be
returned in this case, leading to undefined behavior.
This commit changes the behavior to fail the parsing function early.
Also a test file is introduced for this case.
3. In case the file declares only the EX_CRYPT extra field, current
function simply returns ARCHIVE_FATAL, preventing the caller from
setting the proper error string. This results in libarchive returning
an ARCHIVE_FATAL without any error messages set. The PR #2096 (commit
adee36b00) was specifically created to provide error strings in case
EX_CRYPT attribute was encountered, but current behavior contradicts
this case.
The commit changes the behavior so that ARCHIVE_OK is returned by the
extra field parsing function in only EX_CRYPT is encountered, so that
the caller header reading function can properly return ARCHIVE_FATAL
to the caller, at the same time setting a proper error string. A test
file is also provided for this case.
This PR should fix issue #2711.
Use posix_spawn() with POSIX_SPAWN_CLOEXEC_DEFAULT on systems that
define this constant, in order to avoid leaking file descriptors into
subprocesses.
Bug: https://github.com/libarchive/libarchive/issues/2520
to avoid use of undefined content of buf, in case when custom
locale makes the result string longer than buf length.
Signed-off-by: Marcin Mikula <marcin@helix.pl>
Some experiments showed strange things happen if you
provide an invalid type value when appending a new ACL entry.
Guard against that, and while we're here be a little more
paranoid elsewhere against bad types in case there is another
way to get them in.
When testing the feature with `bsdtar -acf test.zip --options
zip:compression=zstd …` on a tree of ~100MB, the execution would
appear to "hang" while writing a multi-gigabytes ZIP file.
setup_mac_metadata currently concates the template after TMPDIR without
adding a path separator, which causes mkstemp to create a temporary file
next to TMPDIR instead of in TMPDIR. Add a path separator to the
template to ensure that the temporary file is created under TMPDIR.
I hit this while rebuilding libarchive in nixpkgs. Lix recently started
using a dedicated build directory (under /nix/var/nix/builds) instead of
using a directory under /tmp [1]. nixpkgs & Lix support (optional)
on macOS sandboxing. The default sandbox profile allows builds to access
paths under the build directory and any path under /tmp. Because the
build directory is no longer under /tmp, some of libarchive's tests
started to fail as they accessed paths next to (but not under) the build
directory:
cpio/test/test_basic.c:65: Contents don't match
Description: Expected: 2 blocks
, options=
file="pack.err"
0000_62_73_64_63_70_69_6f_3a_20_43_6f_75_6c_64_20_6e_bsdcpio: Could n
0010_6f_74_20_6f_70_65_6e_20_65_78_74_65_6e_64_65_64_ot open extended
0020_20_61_74_74_72_69_62_75_74_65_20_66_69_6c_65_0a_ attribute file.
Sandbox: bsdcpio(11215) deny(1) file-write-create /nix/var/nix/builds/nix-build-libarchive-3.8.0.drv-7tar.md.5EUrQu
[1]: https://lix.systems/blog/2025-06-24-lix-cves/
If it's already known that we use variables for calculations with
size_t, use size_t for them directly instead of int, even if values
fit.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The off_t datatype in Windows is 32 bit, which leads to issues when
handling files larger than 2 GB.
Add a wrapper around fstat/stat calls to return a struct which has a
properly sized st_size variable. On systems with an off_t representing
the actual system limits, use the native system calls.
This also fixes mtree's checkfs option with large files on Windows.
Fixes https://github.com/libarchive/libarchive/issues/2685
Fixes 89b8c35ff4
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
It is possible to handle entries and files with sizes which do not fit
into off_t of the current system (Windows always has 32 bit off_t and
32 bit systems without large file support also have 32 bit off_t).
Set sizes to 0 in such cases. The fstat system call would return -1 and
set errno to EOVERFLOW, but that's not how archive_entry_set_size acts.
It would simply ignore negative values and set the size to 0.
Actual callers of archive_entry_stat from foreign projects seem to not
even check for NULL return values, so let's try to handle such cases as
nice as possible.
Affects mtree's checkfs option as well (Windows only, 32 bit systems
would simply fail in fstat/stat).
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The new `__la_wopen` wrapper is a copy of `__la_open` that
expects--rather than converts--a wcs parameter.
The `sopen` variants are offered as "more secure" variants of `open` and
`wopen`; I cannot vouch for their security, but some build systems are
strict about the use of "banned insecure APIs".
I've confirmed that `_wsopen_s` and `_open_s` are present in the Windows
Vista SDK.
I did not confirm that they are available in the Windows XP Platform
SDK, in part because in e61afbd463 (2016!) Tim says:
> I'd like to completely remove support for WinXP and earlier.
For write, 0 may not mean an error at all. We need to instead check for the length not being the same.
With fwrite, because 0 could mean an error, but not always. We must check that we wrote the entire file!
Note that unlike write, fwrite's description according to POSIX does not mention returning a negative type at all. Nor does it say you can retry unlike write.
Finally, with write, we need to check less than 0, not 0, as 0 is a valid return and does not mean an error.
Make sure that the string table size is not smaller than 6 (and also
not larger than SIZE_MAX for better 32 bit support).
Such small values would lead to a large loop limit which either leads to
a crash or wrong detection of a ".data" string in possibly uninitialized
memory.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The 64 bit format requires at least 63 bytes, so increase this limit.
Such small binaries most likely don't contain 7zip data anyway.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If a sparse hole is located at the end of an entry, then the tar
parser returns ARCHIVE_EOF while updating the offset where 0 bytes of
data will follow.
If archive_read_data encounters such an ARCHIVE_EOF return value, it
has to recheck if the offsets (data offset and output offset) still
match. If they do not match, it has to keep filling 0 bytes.
This changes assumes that it's okay to call archive_read_data_block
again after an EOF. As far as I understood the parsers so far, this
should be okay, since it's always ARCHIVE_EOF afterwards.
Fixes https://github.com/libarchive/libarchive/issues/1194
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If an entry reaches its end of file, the offset is not necessarily
the same as unp_size. This is especially true for links which have
a "0 size body" even though the unpacked size is not 0.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
When _warc_read encounters end of entry, it adds 4 bytes to the last
offset for \r\n\r\n separator, which is never written. Ignore these
bytes since they are not part of the returned entry.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If xar_read_data has no further data, set offset to end of entry,
not to total size of parsed archive so far.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The string constants can be used directly for comparison, which makes
this code robust against future changes which could lead to names being
longer than str could hold on stack.
Also removes around 100 bytes from compiled library (with gcc 15).
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If zlib is not supported, do not run tests to avoid false positives.
Also adjust tests to support latest gzip versions (1.10+) which store
less information for improved reproducibility. The gzip binary is
used as a fallback if zlib is not available.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If a unix system has no iconv support, the best effort function will
be unable to convert KOI8 to UTF-8. Skip the test if such support is
missing.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If no encryption support exists, the -P option will always fail.
"Skip" the test by making sure that there really is no encryption
support according to libarchive functions.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Some functions might return -1 in case of library error. Use an
own return value if a stub function was used for better error
messages.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
After importing the latest libarchive into FreeBSD, Shawn Webb @
HardenedBSD noted that the test build is broken when FORTIFY_SOURCE=2
while building the base system. Braced initializer lists are a special
case that need some extra fun parentheses when we're dealing with the
preprocessor.
While it's not a particularly common setup, the extra parentheses don't
really hurt readability all that much so it's worth fixing for wider
compatibility.
Fixes: libarchive/libarchive#2657
Ignoring SIGCHLD gets passed to child processes. Doing that has
influence on waitpid, namely that zombie processes won't be
created. This means that a status can never be read.
We can't enforce this in library, but libarchive's tools can be
protected against this by enforcing default handling.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use pid_t since waitpid returns a pid_t. Also check for a negative
return value in writer as well to avoid reading the possibly
unitialized status value.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Calling CloseHandle multiple times for the same handle can lead to
exceptions while debugging according to documentation.
Mimic the waitpid handling for success cases to behave more like the
Unix version which would "reap the zombie".
Doing this for an unsuccessful call is off, but the loop is never
entered again, so I guess it's okay and worth it to reduce the amount
of Windows specific definitions in source files.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If the waitpid version for Windows fails, preserve the error code and
avoid overwriting it with a possible CloseHandle error.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The archive_utility_string_sort function won't be part of the 4.0.0 API
anymore. No users were found and such a task should be done outside of
the library.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The utility function "archive_utility_string_sort" is a custom qsort
implementation. Since qsort is specified in C11 and POSIX.1-2008
which libarchive is based on, use system's qsort directly.
The function is not used directly in libarchive, so this is a good
way to save around 500 bytes in resulting library without breaking
compatibility for any user of this function (none found).
Also allows more than UINT_MAX entries which previously were limited
by data type and (way earlier) due to recursion.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Test cases already get a C locale, which is sufficient for this test.
IF LC_TIME was not previously set, the used en_US.UTF-8 would stay
as an environment variable, possibly affecting other test cases.
Since en_US.UTF-8 is not guaranteed to be available, C is a better
choice.
Fixes https://github.com/libarchive/libarchive/issues/2560
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Reset current locale settings through setlocale and also all
environment variables which might affect test cases which
spawn children through systemf which in turn would call setlocale
on their own, e.g. bsdtar.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Explicitly use goto to turn a recursive call into an iterative one.
Most compilers do this on their own with default settings, but MSVC
with default settings would create a binary which actually performs
recursive calls.
Fixes call stack overflow in binaries compiled with low optimization.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The sparse 1.0 parser skips lines with comments. The amount of skipped
bytes is stored in a ssize_t variable, although common 32 bit systems
allow files larger than 4 GB.
Gracefully handle files with more than 2 GB bytes full of comments to
prevent integer truncations.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If a pax global header specifies a negative size, it is possible to
reduce variable `unconsumed` by 512 bytes, leading to a re-reading
of the pax global header. Fortunately the loop verifies that only one
global header per entry is allowed, leading to a later ARCHIVE_FATAL.
Avoid any form of negative size handling and fail early.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Skip all entry bytes after sparse entries were encountered. This matches
GNU tar behavior.
I have adjusted (and fixed) the existing test case for this. The test
case test_read_format_gtar_sparse_skip_entry did not work with GNU tar.
In #2558 it was explained that the pax size always overrides the header
size (correct). Since the pax size in the test case was way larger than
the actual entry bytes in archive, GNU tar choke on the test file.
The libarchive parser did not skip any bytes not already read due to
references by sparse entries, so the huge pax size was not detected.
By adjusting the test case to have a leftover byte (only 3 bytes are
referenced through sparse entry now, leaving one extra byte) with a
correct pax size and an invalid header size (after all it is overridden
by pax size), GNU tar works and libarchive gets off its 512 byte
alignment, not being able to read the next entry.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Consecutive to 16fd043f51
IID_ISequentialStream is required by the code.
This GUID is defined in uuid.lib or libuuid.a in mingw-w64. It is required
to link with that library to get the definition of the GUID. Some toolchains
add it by default but not all.
Some error messages already use the ll length modifier, which results
in raw formatter output, i.e. "%lld" instead of a number.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If a pax attribute has a 0 length value and no newline, the tar reader
gets out of sync with block alignment.
This happens because the pax parser assumes that variable value_length
(which includes the terminating newline) is at least 1. To get the
real value length, 1 is subtracted. This result is subtracted from
extsize, which in this case would lead to `extsize -= -1`, i.e.
the remaining byte count is increased.
Such an unexpected calculation leads to an off-by-one when skipping
to the next block. In supplied test case, bsdtar complains that the
checksum of the next block is wrong. Since the tar parser was not
properly 512 bytes aligned, this is no surprise.
Gracefully handle such a case like GNU tar does and warn the user that
an invalid attribute has been encountered.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
AppleDouble extension entries are present as separate files immediately
preceding the corresponding real files. In libarchive, we process the
entire metadata file (headers + data) as if it were a header in the real
file. However, the code forgets to reset the accumulated header state
before parsing the real file's headers. In one code path, this causes
the metadata file's name to be used as the real file's name.
Specifically, this can be triggered with a tar containing two files:
1. A file named `._badname` with pax header containing the `path` attribute
2. A file named `goodname` _with_ a pax header but _without_ the `path` attribute
libarchive will list one file, `._badname` containing the data of `goodname`.
This code is pretty brittle and we really should let the client deal with
it :(
Fixes#2510.
Signed-off-by: Zhaofeng Li <hello@zhaofeng.li>
Pax extended headers may specify negative time values for files older
than the epoch.
Adjust the code to clear values to 0.0 more often and set ps to
INT64_MIN to have a proper error specifier, because the parser does
not allow anything below -INT64_MAX.
Fixes https://github.com/libarchive/libarchive/issues/2562
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Iterating over a size_t with unsigned could lead to an endless loop
while adding uid/gid to a list which already counts 4 billion
entries.
I doubt that this can ever happen, given that the routines become
very slow with insertions, but better be safe than sorry.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Turn unmatched_count into a size_t to support as many entries as
possible on the machine.
If more than INT_MAX entries are not matched, truncate the result
of archive_match_path_unmatched_inclusions for external callers.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The count fields are merely used to check if a list is empty or not.
A check for first being not NULL is sufficient and is already in
place while iterating over the linked elements (count is not used).
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The operations for key and node comparison depend on the platform
libarchive is compiled for. Since these values do not change
during runtime, set them only once during initialisation.
Further simplify the code by declaring only one "rb_ops" with
required functions based on platform.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The cygwin FAQ states that __CYGWIN__ is defined when building for a
Cygwin environment. Only a few test files check (inconsistently) for
CYGWIN, so adjust them to the recommended __CYGWIN__ definition.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Cast address of "version" to BYTE pointer for CryptGetProvParam.
Fix "major" variable assignment for picky compilers like MSVC.
The "length" variable is an in/out variable. It must be set to the size
of available memory within "version". Right now it is undefined behavior
and 0 would crash during runtime.
Fixes https://github.com/libarchive/libarchive/issues/2628
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Bumps the all-actions group with 4 updates:
`actions/checkout` from 4.2.1 to 4.2.2
`actions/upload-artifact` from 4.4.3 to 4.6.2
`github/codeql-action` from 3.26.12 to 3.28.18
`ossf/scorecard-action` from 2.4.0 to 2.4.1
We should not get here, but given that the check exists, we should not let it happen if this is NULL because otherwise we just dereference it later on.
When looping over program header entries (e_shnum)
we need to increment sec_tbl_offset by e_shentsize
and not by fixed values.
Fixes OSS-Fuzz issue 418349489
Copy ae digests to mtree_entry. This simplifies porting non-archive
formats to archive formats while preserving supported message
digests specifically in cases where recomputing digests is not
viable.
Signed-off-by: Nicholas Vinson <nvinson234@gmail.com>
The size_t to int conversion is especially required on Windows systems
to support their int-based functions. These variables should be properly
checked before casts. This avoids integer truncations with large
strings.
I prefer size_t over int for sizes and adjusted variables to size_t
where possible to avoid casts.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If vsnprintf fails with errno EOVERFLOW, the results are very platform
dependent but never useful. The implementation in glibc fills bytes with
blanks, FreeBSD fills them with zeros, OpenBSD and Windows set first
byte to '\0'.
Just stop processing and don't print anything, which makes it follow
the OpenBSD and Windows approach.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The vsnprintf calls might return INT_MAX with very long strings.
Prevent a signed integer overflow when taking an additional nul
byte into account.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
If the format buffer shall not be further increased in size, the
length value mistakenly takes the terminating nul byte into account.
This is in contrast to a successful vsnprintf call.
Also use the correct string length if fallback to stack buffer is
required.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The stack buffer is never cleared, which can become an issue depending
on vsnprintf implementation's behavior if -1 is returned. The code
would eventually fall back to stack buffer which might be not
nul terminated.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Hi,
This PR adds support for setting a forced mtime on all written files
(`--mtime` and `--clamp-mtime`) in bsdtar.
The end goal will be to support all functionalities in
<https://reproducible-builds.org/docs/archives/#full-example>, namely
`--sort` and disabling other attributes (atime, ctime, etc.).
Fixes#971.
## History
- [v1](https://github.com/zhaofengli/libarchive/tree/forced-mtime-v1):
Added `archive_read_disk_set_forced_mtime` in libarchive. As a result,
it was only applied when reading from the filesystem and not from other
archives.
- [v2](https://github.com/zhaofengli/libarchive/tree/forced-mtime-v2):
Refactored to apply the forced mtime in `archive_write`.
- v3 (current): Reduced libarchive change to exposing
`archive_parse_date`, moved clamping logic into bsdtar.
---------
Signed-off-by: Zhaofeng Li <hello@zhaofeng.li>
Co-authored-by: Dustin L. Howett <dustin@howett.net>
A filter block size must not be larger than the lzss window, which is
defined
by dictionary size, which in turn can be derived from unpacked file
size.
While at it, improve error messages and fix lzss window wrap around
logic.
Fixes https://github.com/libarchive/libarchive/issues/2565
---------
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Co-authored-by: Tim Kientzle <kientzle@acm.org>
If a system is capable of handling 4 billion nodes in memory, a double
free could occur because of an unsigned integer overflow leading to a
realloc call with size argument of 0. Eventually, the client will
release that memory again, triggering a double free.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
To detect 7z SFX files, libarchive currently searches for the 7z header
in a hard-coded addr range of the PE/ELF file
(specified via macros SFX_MIN_ADDR and SFX_MAX_ADDR). This causes it to
miss SFX files that may stray outside these values (libarchive fails to
extract 7z SFX ELF files created by recent versions of 7z tool because
of this issue). This patch fixes the issue by finding a more robust
starting point for the 7z header search: overlay in PE or the .data
section in ELF. This patch also adds 3 new test cases for 7z SFX to
libarchive.
Fixes https://github.com/libarchive/libarchive/issues/2075
---------
Co-authored-by: Masoud Mehrabi Koushki <masoud.mehrabi.koushki1@huawei.com>
Co-authored-by: Martin Matuška <martin@matuska.de>
This new test archive contains a C hello world executable built like so
on a ubuntu 24.04 machine:
```
int main(int argc, char *argv[]) {
printf("hello, world\n");
return 0;
}
```
`powerpc-linux-gnu-gcc hw.c -o hw-powerpc -Wall`
The test archive that contains this executable was created like so,
using 7-Zip 24.08: `7zz a -t7z -m0=lzma2 -mf=ppc
libarchive/test/test_read_format_7zip_lzma2_powerpc.7z hw-powerpc`
The new test archive is required because the powerpc filter for lzma is
implemented in liblzma rather than in libarchive.
This commit adds support for reading and writing XAR archives on Windows
using the built-in xmllite library. xmllite is present in all versions
of Windows starting with Windows XP.
With this change, no external XML library (libxml2, expat) is required
to read or produce XAR archives on Windows.
xmllite is a little bit annoying in that it's entirely a COM API--the
likes of which are annoying to use from C.
Signed-off-by: Dustin L. Howett <dustin@howett.net>
Depends on e619342dfaCloses#1811
A few small tweaks to improve reading/writing of the legacy GNU tar
format.
* Be more tolerant of redundant 'K' and 'L' headers
* Fill in missing error messages for redundant headers
* New test for reading archive with redundant 'L' headers
* Earlier identification of GNU tar format in some cases
These changes were inspired by Issue #2434. Although that was determined
to not technically be a bug in libarchive, it's relatively easy for
libarchive to tolerate duplicate 'K' and 'L' headers and we should be
issuing appropriate error messages in any case.
The refactoring of https://github.com/libarchive/libarchive/pull/2553
introduced three issues:
1. Introduction of a modifiable global static variable
This violates the goal of having no global variables as stated in [the
README.md](https://github.com/libarchive/libarchive/blob/b6f6557abb8235f604eced6facb42da8c7ab2a41/README.md?plain=1#L195)
which in turn leads to concurrency issues. Without any form of mutex
protection, multiple threads are not guaranteed to see the correct
min/max values. Since these are not needed in regular use cases but only
in edge cases, handle them in functions with local variables only.
Also the global variables are locale-dependent which can change during
runtime. In that case, future calls leads to issues.
2. Broken 32 bit support
The writers for zip and others affected by the previously mentioned PR
and test-suite on Debian 12 i686 are broken, because the calculation of
maximum MS-DOS time is not possible with a 32 bit time_t. Treat these
cases properly.
3. Edge case protection
Huge or tiny int64_t values can easily lead to unsigned integer
overflows. While these do not affect stability of libarchive, the
results are still wrong, i.e. are not capped at min/max as expected.
In total, the functions are much closer to their original versions again
(+ more range checks).
---------
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Make sure that size_t casts do not truncate the value of packed_size on
32 bit systems since it's 64 bit. Extensions to RAR format allow 64 bit
values to be specified in archives.
Also verify that 64 bit signed arithmetics do not overflow.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
It is possible to trigger a call stack overflow by repeatedly entering
the rar_read_ahead function. In normal circumstances, this recursion is
optimized away by common compilers, but default settings with MSVC keep
the recursion in place. Explicitly turn the recursion into a goto-loop
to avoid the overflow even with no compiler optimizations.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Reset avail_in and next_in if the next entry of a split archive is
parsed to always update its internal structure to access next bytes when
cache runs empty.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Support header sizes larger than 32 bit even on 32 bit systems, since
these normally have large file support. Otherwise an unsigned integer
overflow could occur, leading to erroneous parsing on these systems.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Most source files use tabs instead of spaces, but
archive_read_support_format_rar.c uses spaces most of the time. A few
lines contain a mixture of tabs and spaces, which leads to poorly
formatted output with many default settings.
Unify the style. No functional change and preparation for upcoming
changes to get rid of white space diffs.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
PR #2258 hardened AFIO parsing by converting all inode values >= 2^63 to
zero values. This turns out to be problematic for filesystems that use
very large inode values; it results in all such files being viewed as
hardlinks to each other.
PR #2587 partially addressed this by instead considering inode values >=
2^63 as invalid and just ignoring them. This prevented the accidental
hardlinking, but at the cost of losing all hardlinks that involved large
inode values.
This PR further improves things by stripping the high order bit from
64-bit inode values in the AFIO parser. This allows them to be mostly
preserved and should allow hardlinks to get properly processed in the
vast majority of cases. The only false hardlinks would be when there are
inode values that differ exactly in the high order bit, which should be
very rare.
A full solution will require expanding inode handling to use unsigned
64-bit values; we can't do that without a major version bump, but this
PR also sets the stage for migrating inode support in a future
libarchive 4.0.
The calculations for the suffix and prefix can increment the endpoint
for a trailing slash. Hence the limits used should be one lower than the
maximum number of bytes.
Without this patch, when this happens for both the prefix and the
suffix, we end up with 156 + 100 bytes, and the write of the null at the
end will overflow the 256 byte buffer. This can be reproduced by running
```
mkdir -p foo/bar
bsdtar cvf test.tar foo////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////bar
```
when bsdtar is compiled with Address Sanitiser, although I originally
noticed this by accident with a genuine filename on a CHERI capability
system, which faults immediately on the buffer overflow.
Make sure to not skip past end of file for better error messages. One
such example is now visible with rar testsuite. You can see the
difference already by an actually not useless use of cat:
```
$ cat .../test_read_format_rar_ppmd_use_after_free.rar | bsdtar -t
bsdtar: Archive entry has empty or unreadable filename ... skipping.
bsdtar: Archive entry has empty or unreadable filename ... skipping.
bsdtar: Truncated input file (needed 119 bytes, only 0 available)
bsdtar: Error exit delayed from previous errors.
```
compared to
```
$ bsdtar -tf .../test_read_format_rar_ppmd_use_after_free.rar
bsdtar: Archive entry has empty or unreadable filename ... skipping.
bsdtar: Archive entry has empty or unreadable filename ... skipping.
bsdtar: Error exit delayed from previous errors.
```
Since the former cannot lseek, the error is a different one
(ARCHIVE_FATAL vs ARCHIVE_EOF). The piped version states explicitly that
truncation occurred, while the latter states EOF because the skip past
the end of file was successful.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The size_t data type is only 32 bit on 32 bit sytems while off_t is
generally 64 bit to support files larger than 2 GB.
If an entry is declared to be larger than 4 GB and the entry shall be
skipped, then 32 bit systems truncate the requested amount of bytes.
This leads to different interpretation of data in tar files compared to
64 bit systems.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Solves a Windows compile issue when OpenSSH/mbedTLS/Nettle is activated
and on the build system's paths by making the Windows API backend higher
priority on Windows (meaning that only RIPEMD160 will use
OpenSSH/mbedTLS/Nettle anymore).
Fixes#2536 and starts on #2320.
If a warc archive claims to have more than INT64_MAX - 4 content bytes,
the inevitable failure to skip all these bytes could lead to parsing
data which should be ignored instead.
The test case contains a conversation entry with that many bytes and if
the entry is not properly skipped, the warc implementation would read
the conversation data as a new file entry.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The skip functions are limited to 1 GB for cases in which libarchive
runs on a system with an off_t or long with 32 bits. This has negative
impact on 64 bit systems.
Instead, make sure that _all_ subsequent functions truncate properly.
Some of them already did and some had regressions for over 10 years.
Tests pass on Debian 12 i686 configured with --disable-largefile, i.e.
running with an off_t with 32 bits.
Casts added where needed to still pass MSVC builds.
---------
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
My attempt to fix#2404 just made the confusion between the size of the
extracted file and the size of the contents in the tar archive worse
than it was before.
@ferivoz in #2557 showed that the confusion stemmed from a point where
we were setting the size in the entry (which is by definition the size
of the file on disk) when we read the `GNU.sparse.size` and
`GNU.sparse.realsize` attributes (which might represent the size on disk
or in the archive) and then using that to determine whether to read the
value in ustar header (which represents the size of the data in the
archive).
The confusion stems from three issues:
* The GNU.sparse.* fields mean different things depending on the version
of GNU tar used.
* The regular Pax `size` field overrides the value in the ustar header,
but the GNU sparse size fields don't always do so.
* The previous libarchive code tried to reconcile different size
information as we went along, which is problematic because the order in
which this information appears can vary.
This PR makes one big structural change: We now have separate storage
for every different size field we might encounter. We now just store
these values and record which one we saw. Then at the end, when we have
all the information available at once, we can use this data to determine
the size on disk and the size in the archive.
A few key facts about GNU sparse formats:
* GNU legacy sparse format: Stored all the relevant info in an extension
of the ustar header.
* GNU pax 0.0 format: Used `GNU.sparse.size` to store the size on disk
* GNU pax 0.1 format: Used `GNU.sparse.size` to store the size on disk
* GNU pax 1.0 format: Used `GNU.sparse.realsize` to store the size on
disk; repurposed `GNU.sparse.size` to store the size in the archive, but
omitted this in favor of the ustar size field when that could be used.
And of course, some key precedence information:
* Pax `size` field always overrides the ustar header size field.
* GNU sparse size fields override it ONLY when they represent the size
of the data in the archive.
Resolves#2548
Hello,
- The `CMAKE_COMPILER_IS_*` variables are deprecated and
`CMAKE_C_COMPILER_ID` can be used in this case instead.
- The legacy `endif()` command argument also simplified to avoid
repeating the condition.
Moving archive_entry_set_digest() to the public API simplifies porting
non-archive formats to archive formats while preserving supported
message digests specifically in cases where recomputing digests is not
viable.
The lafe_errc function adds a newline by itself already, so do not
insert one into the message.
You can reproduce with the following commands:
```
touch archive.tar
bsdtar -xf archive.tar -C /non-existing
```
```
bsdtar --exclude ""
```
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Hi,
please find my approach to fix the CVE-2025-1632 and CVE-2025-25724
vulnerabilities in this pr.
As both error cases did trigger a NULL pointer deref (and triggered
hopefully everywhere a coredump), we can safely replace the actual
information by a predefined invalid string without breaking any
functionality.
---------
Signed-off-by: Peter Kaestle <peter@piie.net>
Adding missing librairies to `archive_version_details()`'s output. I put
"system" if the library doesn't give a way to query its version and
"bundled" if there's a choice between the system copy of a library and a
bundled one and we took the bundled copy (Only one library in that case,
libb2. Maybe also xxhash in the future?).
I would have a question for the Windows specialists though: is there a
way to query the interface version of a CNG cryptographic provider?
Because I know of a way for Crypto API providers but I haven't found any
for CNG ones, despite `<bcrypt.h>` having an interface version
structure.
Fixes#2300.
As remarked in #2521, this test has unreachable code on Windows, which
triggers a build failure in development due to warnings-as-errors.
(Release versions should not have warnings-as-errors.)
The outer if checks !S_ISDIR(a->st.st_mode), so we know that the file
being overwritten is not a directory, and thus we can rename(2) over it
if we want to, but whether we can use a temporary regular file is a
property of the file being extracted. Otherwise, when replacing a
regular file with a directory, we end up in this case and create a
temporary regular file for the new directory, but with the permissions
of the directory (which likely includes x), and rename it over the top
at the end. Depending on where the archive_entry came from, it may have
a non-zero size that also isn't ovewritten with 0 (e.g. if it came from
stat(2)) and so the API user may then try to copy data (thus failing if
read(2) of directories isn't permitted, or writing the raw directory
contents if it is), but if the size is zero as is the case for this tar
test then it will end up not writing any data and "successfully"
overwrite the file with an empty file, not a directory.
`Int32x32To64` macro internally truncates the arguments to int32, while
`time_t` is 64-bit on most/all modern platforms. Therefore, usage of
this macro creates a Year 2038 bug.
I detailed this issue a while ago in a writeup, and spotted the same
issue in this repository when updating the list of affected
repositories:
<https://cookieplmonster.github.io/2022/02/17/year-2038-problem/>
A few more notes:
1. I changed all uses of `Int32x32To64` en masse, even though at least
one of them was technically OK and used with int32 parameters only. IMO
better safe than sorry.
2. This is untested, but it's a small enough change that I hope the CI
success is a good enough indicator.
Endianness is easy to determine at runtime, but detecting this a single
time and then reusing the cached result might require API changes.
However we can use compile-time detection for some known compiler macros
without API changes fairly easily. Let's start by enabling this for
Clang and GCC.
This new test archive contains a C hello world executable built like so
on a ubuntu 24.04 machine:
```
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("hello, world\n");
return 0;
}
```
`powerpc-linux-gnu-gcc hw.c -o hw-powerpc -Wall`
The test archive that contains this executable was created like so,
using 7-Zip 24.08:
`7zz a -t7z -m0=deflate -mf=ppc
libarchive/test/test_read_format_7zip_deflate_powerpc.7z hw-powerpc`
This test fails in the first commit in this PR, and passes in the second
commit.
When the -s/regexp/replacement/ option was used with the b flag more
than once, the result of the previous substitution was appended to the
previous subject instead of replacing it. Fixed it by making sure the
subject is made the empty string before the call to realloc_strcat().
That in effect makes it more like a realloc_strcpy(), but creating a new
realloc_strcpy() function for that one usage doesn't feel worth it.
Resolves Issue libarchive/libarchive#2414
Co-authored-by: Stephane Chazelas <stephane@chazelas.org>
We previously told make to run as many threads as it likes on these CI
jobs, but that might sometimes hit resource limits like RAM or the
allowed number of open files.
These numbers were found experimentally by using `sysctl -n hw.ncpu` on
mac and `nproc` on linux.
`i4le()` returns an unsigned int, so `'%d'` is incorrect.
Reported by `clang -Wformat`. (Many more such fixes to come, but this is
the simplest set of them.)
7-Zip 24.05 and liblzma 5.5.1alpha added a RISC-V BCJ filter. Let's
enable this combination if we can.
Note that this does not allow the use of the RISC-V filter with other
compressors.
Previously skipped tests were reported like this when running the *_test
binaries:
```
4: test_acl_platform_nfs4 ok (S)
```
Let's make this more obvious:
```
4: test_acl_platform_nfs4 skipped
```
This plumbing is required for cmake/ctest to recognise and report
skipped tests.
Now skipped tests in cmake ci jobs are reported like so:
```
Start 7: libarchive_test_acl_platform_posix1e_read
7/785 Test #7: libarchive_test_acl_platform_posix1e_read ................................***Skipped 0.02 sec
```
And there is a list of skipped tests shown at the end of the test run.
Prior to this change, the ci autoconf jobs weren't looking for homebrew
headers or libraries unless pkg-config was used, so for example the
"MacOS (autotools)" ci job wasn't testing lz4 or zstd code.
Relates to #2426.
A few of libarchive's CI jobs don't find all the local support libraries
that they could be using. This change makes it easier to see which of
them are used.
We currently use XZ Utils 5.6.3 on windows CI jobs, but the Windows
(msvc)
job which uses cmake seems to only be looking for the old library name,
liblzma.lib:
```
-- Looking for lzma_auto_decoder in C:/Program Files (x86)/xz/lib/liblzma.lib
-- Looking for lzma_auto_decoder in C:/Program Files (x86)/xz/lib/liblzma.lib - not found
-- Looking for lzma_easy_encoder in C:/Program Files (x86)/xz/lib/liblzma.lib
-- Looking for lzma_easy_encoder in C:/Program Files (x86)/xz/lib/liblzma.lib - not found
-- Looking for lzma_lzma_preset in C:/Program Files (x86)/xz/lib/liblzma.lib
-- Looking for lzma_lzma_preset in C:/Program Files (x86)/xz/lib/liblzma.lib - not found
-- Could NOT find LibLZMA (missing: LIBLZMA_HAS_AUTO_DECODER LIBLZMA_HAS_EASY_ENCODER LIBLZMA_HAS_LZMA_PRESET) (found version "5.6.3")
```
We need to update build/ci/github_actions/ci.cmd to look for lzma.lib
instead.
The fallback for when `getline` is not implemented in libc was not
compiling due to the fact that the definition for it was missing, so add
the definition.
I have been using this for years without realizing it decompresses rar.
+ add rar to supported decompression formats
+ use section references to link sections (this makes them clickable in
GUIs)
+ add paragraph breaks for consistent spacing
+ pdtar is not this program, so use Sy per mdoc style guide
+ do almost the same in reverse for bsdtar
+ remove parenthetical around a complete sentance
Thank you so much, this is wonderful software.
This change fixes the autotools build to work with xz-utils 5.6.3, which
changed library names on windows, and fixes a couple of tests that I
noticed had dependencies on liblzma.
Moving the tests' integer reading functions to test_utils so that they
all use the same as well as moving the few using the archive_endian
functions over to the test_utils helper.
Follow-up from libarchive#2390.
When the pax `size` field is present, we should ignore the size value in
the ustar header. In particular, this fixes reading pax archives created
by GNU tar with entries larger than 8GB.
Note: This doesn't impact reading pax archives created by libarchive
because libarchive uses tar extensions to store an accurate large size
field in the ustar header. GNU tar instead strictly follows ustar in
this case, which prevents it from storing accurate sizes in the ustar
header.
Resolves#2404
These two new test archives contain a C hello world executable built
like so on a ubuntu 24.04 machine:
```
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("hello, world\n");
return 0;
}
```
`sparc64-linux-gnu-gcc hw.c -o hw-sparc64 -Wall`
The two test archives that contain this executable were created like so,
using the https://github.com/tehmul/p7zip-zstd fork of 7-Zip:
`7z a -t7z -m0=zstd -mf=SPARC
libarchive/test/test_read_format_7zip_zstd_sparc.7z hw-sparc64`
`7z a -t7z -m0=lzma2 -mf=SPARC
libarchive/test/test_read_format_7zip_lzma2_sparc.7z hw-sparc64`
Two test files are required, because the 7zip reader code has two
different paths, one for lzma and one for all other compressors.
The test_read_format_7zip_lzma2_sparc test is expected to pass, because
LZMA BCJ filters are implemented in liblzma.
The test_read_format_7zip_zstd_sparc test is expected to fail in the
first commit, because libarchive does not currently implement the SPARC
BCJ filter. The second commit will make test_read_format_7zip_zstd_sparc
pass.
Two problems are prompting this revert:
* In order to change the Create OS value to "Windows", we would need to
record other data (such as `external_attributes`) in Windows format as
well.
* Changing the Create OS value doesn't actually fix the
filename-encoding issue that originally motivated this PR
This reverts commit 755af84301.
The `-P` flag is uppercase, so the test file should be named
test_option_P_upper.c for consistency with the other test files in this
directory.
Sorry about the noise.
Submitting this test at the request of @kientzle on issue #2041.
Note that failure is currently expected, as the feature it tests is not
yet implemented!
This commit prepares the XAR writer for another XML writing backend.
Almost everything in this changeset leaves the code identical to how
it started, except for a new layer of indirection between the xar writer
and the XML writer.
The things that are not one-to-one renames include:
- The removal of `UTF8Toisolat1` for the purposes of validating UTF-8
- The writer code made a copy of every filename for the purposes of
checking whether it was Latin-1 stored as UTF-8. In xar, Non-Latin-1
gets stored Base64-encoded.
- I've replaced this use because (1) it was inefficient and (2)
`UTF8Toisolat1` is a `libxml2` export.
- The new function has slightly different results than the one it is
replacing for invalid UTF-8. Namely, it treats illegal UTF-8 "overlong"
encodings of Latin-1 codepoints as _invalid_. It operates on the principle
that we can determine whether something is Latin-1 based entirely on how
long the sequence is expected to be.
- The move of `SetIndent` to before `StartDocument`, which the
abstraction layer immediately undoes. This is to accommodate XML writers
that require indent to be set _before_ the document starts.
PPMD may come later but I'd rather first iron out style issues with the
ones needing only to wire up libraries already-used in Libarchive before
going at the ones possibly requiring implementing algorithms as well.
Closes#1046 and resolves#1179.
Cygwin 3.5.4 (same applies for 3.5.3),
I get a compile error as shown below after a simple ./configure and
make. Adding <windef.h> solves the problem.
Co-authored-by: vco <god@universe.sys>
Bumps the all-actions group with 4 updates:
actions/checkout from 4.1.6 to 4.2.1
actions/upload-artifact from 4.3.3 to 4.4.3
github/codeql-action from 3.25.6 to 3.26.12
ossf/scorecard-action from 2.3.3 to 2.4.0
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This is the first part of converting the project to use SPDX license
identifiers instead using the verbose license text.
The patches are semi-automated and I've went through manually to ensure
no license changes were made. That said, I would welcome another pair of
eyes, since I am only human.
See https://github.com/libarchive/libarchive/issues/2298
---------
Signed-off-by: Emil Velikov <emil.l.velikov@gmail.com>
People really should never, ever, ever use libarchive internal headers. And they definitely should not expect libarchive internal headers to work in a C++ compiler. (C++ and C are really just not that compatible.)
However, people do a lot of things they shouldn't: Avoid the reserved C++ keyword `template`
Followup from #2346
Add libbsd to make/cmake configuration for linking readpassphrase on
Haiku.
Maybe there is a better way to do this for cmake, I'm not that familiar
with it.
Co-authored-by: vco <god@universe.sys>
I previously tried to find documentation on how symlinks are expected to
be stored in 7zip files, however the best reference I could find was
[here](https://py7zr.readthedocs.io/en/latest/archive_format.html). That
site suggests that symlink paths are stored as UTF-8 encoded strings:
Currently, the RAR5 code always reports
`ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED` for
`archive_read_has_encrypted_entries`, nor does it set any of the
entry-specific properties, even though it has enough information to
properly report this information. Accurate reporting of encryption is
super useful for applications because reporting an error message such as
"the archive is encrypted, but we don't currently support encryption" is
a lot better than a not generally useful `errno` value and a
non-localizable error string with a confusing and unpredictable error
message.
Fixes#1661
Change to read absolute symlinks as verbatim paths instead of NT paths:
as far as I can see, libarchive can deal with verbatim paths while it
can't with NT ones.
Fixes#2274.
Remove the incorrect 4th argument from `AC_CHECK_FUNCS` calls. The macro
uses only three arguments, so it was ignored anyway. Furthermore, in at
least once instance it was wrong -- due to a typo in `attr/xatr.h`
header name.
The tar header parsing overhaul in #2127 introduced a systematic
mishandling of truncated files: the code incorrectly checks for whether
a given read operation failed, and ends up dereferencing a NULL pointer
in this case. I've gone back and double-checked how
`__archive_read_ahead` actually works (it returns NULL precisely when it
was unable to satisfy the read request) and reworked the error handling
for each call to this function in archive_read_support_format_tar.c
Resolves#2353
Resolves https://issues.oss-fuzz.com/issues/42537231
OSS-Fuzz managed to construct a small gzip input that decompresses into
another gzip input with an extremely large filename field. This causes
libarchive to hang processing the inner gzip.
Address this by rejecting any gzip input where the filename or comment
fields exceed 1MiB.
Credit: OSS-Fuzz
No functional change, just a tiny style improvement.
Use `crc32_computed` to refer to the crc32 that the reader has computed
and `crc32_read` to refer to the value that we read from the archive.
That hopefully makes this code a tiny bit easier to follow. (It confused
me recently when I was double-checking something in this area, so I
thought an improvement here might help others.)
We always print the error message with or without -v, but for some
reason, we were omitting the path being processed. Simplify so that we
always print the full error including context.
This fixes various code quality issues I encountered while chasing a
memory leak reported by test automation. I failed to reproduce the
memory leak, but I hope you find this useful nonetheless.
PR #2127 failed to clean up the linkpath storage between entries. As a
result, after the first hard/symlink entry in a pax format archive, all
subsequent entries would get the same link information.
I'm really unsure how this bug failed to trip CI. I'll do some digging
in the test suite before I merge this.
Resolves#2331 , #2337
P.S. Thanks to Brad King for noting that the linkpath wasn't being
managed correctly, which was a big hint for me.
Synchronize the last use of `attr/xattr.h` to support using
`sys/xattr.h` instead. The former header is deprecated on GNU/Linux, and
this replacement makes it possible to build libarchive without the
`attr` package.
Fix memory leaks introduced by #2127:
* `struct tar` member `entry_linkpath` was moved at the same time as
other members were removed, but its cleanup was accidentally removed
with the others.
* `header_pax_extension` local variable `attr_name` was not cleaned up.
Resolves#2336
Some ISO images don't have valid timestamps for the root directory
entry. Parsing such timestamps can generate nonsensical results, which
in one case showed up as an unexpected overflow on a 32-bit system.
Add some validation logic that can check whether a 7-byte or 17-byte
timestamp is reasonable-looking, and use this to ignore invalid
timestamps in various locations. This also requires us to be a little
more careful about tracking which timestamps are actually known.
Resolves issue #2329
Followup to #2318 which accidentally made zlib required.
Tested locally by increasing the version in CMakeLists.txt to 1.4.1
(which does not exist yet), and confirming that the build reports that a
suitable version of zlib was not found, while the build continued..
Before writing a zip entry, its' pathname might be modified for two
reasons:
1. Path using Windows path separators will be converted to POSIX style.
2. Path using local encoding will be transcoded if a target charset is
set.
Must make sure these two mechanisms can coexist without overwriting each
other.
zlib 1.2.0 added this improvement for inflate:
"Raw inflate no longer needs an extra dummy byte at end"
libarchive does not feed zlib extra data beyond end of stream, so it
does not work with zlib < 1.2.0.
With 26 and 27, the sub-test is pushing 2G and 4G memory respectively.
There is no particular reason why we need to push for higher limits
here, so let's pick 23 with weights around 0.25G. The test suite overall
is in the 0.25 - 0.5G range and this fits perfectly.
Closes: https://github.com/libarchive/libarchive/issues/2080
Signed-off-by: Emil Velikov <emil.l.velikov@gmail.com>
Fix `test_write_format_zip_stream` failure when `HAVE_ZLIB_H` is not
defined.
If `libz` is present, `zip` archives would be compressed by default,
which requires `zip_version=20`. Otherwise, the archive is not
compressed and only requires `zip_version=10`. I'm building libarchive
on a machine not intended for developing, so basicly there's no optional
dependencies like `libz` available, guess that's why nobody else has
reported this issue.
Pax introduced new headers that appear _before_ the legacy
headers. So pax archives require earlier properties to
override later ones.
Originally, libarchive handled this by storing the early
headers in memory so that it could do the actual parsing
from back to front. With this scheme, properties from
early headers were parsed last and simply overwrote
properties from later headers.
PR #2127 reduced memory usage by parsing headers in the
order they appear in the file, which requires later headers
to avoid overwriting already-set properties. Apparently,
when I made this change, I did not fully consider how charset
translations get handled on Windows, so failed to consistently
recognize when the path or linkname properties were in fact
actually set. As a result, the legacy path/link values (which have
no charset information) overwrote the pax path/link values (which
are known to be UTF-8), leading to the behavior observed in
#2248. This PR corrects this bug by adding additional
tests to see if the wide character path or linkname properties
are set.
Related: This bug was exposed by a new test added in #2228
which does a write/read validation to ensure round-trip filename
handling. This was modified in #2248 to avoid tickling the bug above.
I've reverted the change from #2248 since it's no longer necessary.
I have also added some additional validation to this test to
help ensure that the intermediate archive actually is a pax
format that includes the expected path and linkname properties
in the expected places.
It would seem as though #2127 conflicted with my change #2228.
I previously thought that the writer was putting info into the archive
that strings were encoded in UTF-8, but I'm not so sure of that
anymore... In any case, explicitly setting `hdrcharset` on the reader as
well is a reasonable alternative and something we do already.
The RAR5 reader is using a small stack of cached pointers to submit the
rendered data to the caller. In malformed files it's possible for this
pointer cache to be desynchronized with the memory buffer those pointers
are pointing to, making libarchive crash on invalid memory access.
OSS-Fuzz Issue: 70024
In particular, this ensures that we cannot overflow rounding-up
calculations. Recent tar changes put in a lot of sanity limits on the
sizes of particular kinds of data, but the usual behavior in most cases
was to skip over-large values. The skipping behavior required
rounding-up and accumulating values that could potentially overflow
64-bit integers. This adds some coarser checks that fail more directly
when an entry claims to be more than 1 exbibyte (2^60 bytes), avoiding
any possibility of numeric overflow along these paths.
OSS-Fuzz Issue: 70062
This is somewhat academic, since we don't actually expose any of the
ISO9660 header information that is stored in 17-byte date format, but
inspection revealed an off-by-one error in the parsing here.
This also proved a nice motivation to fill in some verification in our
most basic ISO9660 test case.
Currently updating archivemount which does
```c
pwd = getpwuid(st.st_uid);
if(pwd)
archive_entry_set_uname(node->entry, strdup(pwd->pw_name));
grp = getgrgid(st.st_gid);
if(grp)
archive_entry_set_gname(node->entry, strdup(grp->gr_name));
```
and I'm assuming the strdups are actually leaks? The manual is silent on
this.
Previous code added `.XXXXXX` to the end of the filename to write the
mac metadata. This is a problem if the filename is at or near the
filesystem max path length. This reuses the same code used by
create_tempdatafork to ensure that the filename is not too long.
The fuzzer constructed an AFIO (CPIO variant) archive that had a
rediculously large ino value, which caused an overflow of a signed
64-bit intermediate.
There are really three issues here:
* The CPIO parser was using a signed int64 as an intermediate type for
parsing numbers in all cases. I've addressed the overflow here by using
a uint64_t in the parser core, but left the resulting values as int64_t.
* The AFIO header parsing had no guards against rediculously large
values; it now rejects an archive when the ino or size fields (which are
allowed to be up to 16 hex digits long) overflow int64_t to produce a
negative value.
* The archive_entry would accept negative values for gid/uid/size/ino.
I've altered those so that these fields treat any negative value as zero
for these fields.
There was one test that actually verified that we could read a field
with size = -1. I've updated that to verify that the resulting size is
zero instead.
OSS-Fuzz Issue: 70019
The Rar5 reader would read the name size, then read the name, then check
whether the name size was beyond the maximum size allowed. This can
result in a very large memory allocation to read a name. Instead, check
the name size before trying to read the name in order to avoid excessive
allocation.
OSS-Fuzz Issue: 70017
I went through ~50 findings of SAST reports and identified a few of them
as true positives. I might still have missed some intended uses or some
magic in the code so please provide feedback if you think some of these
shouldn't be applied and why.
I explained the changes in the separate comments.
Microsoft's static analysis tool found some vulnerabilities from
unguarded null references that I changed in
[microsoft/cmake](https://github.com/microsoft/cmake). Pushing these
changes upstream so they can be added to
[kitware/cmake](https://github.com/Kitware/CMake).
The code currently uses `archive_entry_hardlink` to determine if an
entry is a hardlink, however on Windows, this call will fail if the path
cannot be represented in the current locale. This instead checks to see
if any entry in the `archive_mstring` is set.
All three parts of this change effectively stem from the same
assumption: most of the code in `archive_string.c` assumes that MBS <->
UTF-8 string conversion can be done directly and efficiently. This is
not quite true on Windows, where conversion looks more like MBS <-> WCS
<-> UTF-8. This results in a few inefficiencies currently present in the
code.
First, if the caller is asking for either the MBS or UTF-8 string, but
it's not currently set on the `archive_mstring`, then on Windows, it's
more efficient to first check if the WCS is set and do the conversion
with that. Otherwise, we'll end up doing a wasteful intermediate step of
converting either the MBS or UTF-8 string to WCS, which we already have.
Second, in the `archive_mstring_update_utf8` function, it's more
efficient on Windows to first convert to WCS and use that result to
convert to MBS, as opposed to the fallback I introduced in a previous
change, which converts UTF-8 to MBS first and disposes of the
intermediate WCS, only to re-calculate it.
We noticed an issue where we had an archive that, if you skipped the
first entry and tried to extract the second, you'd get a failure saying
`Truncated 7-Zip file body`. Turns out that this is because the first
file in the archive is a multiple of 65,536 bytes (the size of the
uncompressed buffer) and therefore after `read_stream` skipped all of
the first file, `uncompressed_buffer_bytes_remaining` was set to zero
(because all data was consumed) and then it calls
`get_uncompressed_data` with `minimum` set to zero. This then saw that
`minimum > zip->uncompressed_buffer_bytes_remaining` evaluated to false,
causing us to read zero bytes, which got interpreted as a truncated
archive.
The fix here is simple: we now always call `extract_pack_stream` when
`uncompressed_buffer_bytes_remaining` is zero before exiting the
skipping loop.
In order to match cpio output, format the reference date with _at least_
12 bytes instead of _exactly_ 12 bytes. This should fix a gratuitous
test failure on certain systems that default to multi-byte locales.
The CSRG ISOs have a non-standard PVD layout with a 68-byte root
directory record (rather than the 34-byte record required by
ECMA119/ISO9660). I built a test image with this change and modified the
ISO9660 reader to accept it.
While I was working on the bid logic to recognize PVDs, I added a number
of additional correctness checks that should make our bidding a bit more
accurate. In particular, this should more than compensate for the
weakened check of the root directory record size.
Resolves#2232
This rebuilds the tar reader to parse all header data incrementally as
it appears in the stream.
This definitively fixes a longstanding issue with unsupported pax
attributes. Libarchive must limit the amount of data that it reads into
memory, and this has caused problems with large unknown attributes. By
scanning iteratively, we can instead identify an attribute by name and
then decide whether to read it into memory or whether to skip it without
reading.
This design also allows us to vary our sanity limits for different pax
attributes (e.g., an attribute that is a single number can be limited to
a few dozen bytes while an attribute holding an ACL is allowed to be a
few hundred kilobytes). This allows us to be a little more resistant to
malicious archives that might try to force allocation of very large
amounts of memory, though there is still work to be done here.
This includes a number of changes to archive_entry processing to allow
us to consistently keep the _first_ appearance of any given value
instead of the original architecture that recursively cached data in
memory in order to effectively process all the data from back-to-front.
Resolves#1855Resolves#1939
Note: this is a partial cherry-pick from
https://github.com/libarchive/libarchive/pull/2095, which I'm going to
go through and break into smaller pieces in hopes of getting some things
in while discussion of other things can continue.
There's basically two fixes here:
The first is to check for the presence of the WCS pathname on Windows
before failing since the conversion from WCS -> MBS might fail. Later
execution already handles such paths correctly.
The second is to set the converted link name on the target entry where
relevant. Note that there has been prior discussion on this here:
https://github.com/libarchive/libarchive/pull/2095/files#r1531599325
certain rar files seem to have the lowest possible address here, so flip
the argument order to correctly evaluate this instead of invoking UB
(caught via sanitize=undefined)
---
the backtrace looks something like:
```
* frame #0: 0x00007a1e3898727b libarchive.so.13`execute_filter [inlined] execute_filter_e8(filter=<unavailable>, vm=<unavailable>, pos=<unavailable>, e9also=<unavailable>) at archive_read_support_format_rar.c:3640:47
frame #1: 0x00007a1e3898727b libarchive.so.13`execute_filter(a=<unavailable>, filter=0x00007a1e39e2f090, vm=0x00007a1e31b1efd0, pos=<unavailable>) at archive_read_support_format_rar.c:0
frame #2: 0x00007a1e38983ac3 libarchive.so.13`read_data_compressed [inlined] run_filters(a=0x00007a1e34209700) at archive_read_support_format_rar.c:3395:8
frame #3: 0x00007a1e38983a9e libarchive.so.13`read_data_compressed(a=0x00007a1e34209700, buff=0x00007a1e31a01fd8, size=0x00007a1e31a01fd0, offset=0x00007a1e31a01fc0, looper=1) at archive_read_support_format_rar.c:2083:12
frame #4: 0x00007a1e38981b10 libarchive.so.13`archive_read_format_rar_read_data(a=0x00007a1e34209700, buff=0x00007a1e31a01fd8, size=0x00007a1e31a01fd0, offset=0x00007a1e31a01fc0) at archive_read_support_format_rar.c:1130:11
frame #5: 0x00006158bc5d30d3 file-roller`extract_archive_thread(result=0x00007a1e3711e2b0, object=<unavailable>, cancellable=0x00007a1e3870bf20) at fr-archive-libarchive.c:999:17
frame #6: 0x00007a1e39928d6d libgio-2.0.so.0`run_in_thread(job=<unavailable>, c=<unavailable>, _data=0x00007a1e326e9740) at gsimpleasyncresult.c:899:5
frame #7: 0x00007a1e3990614e libgio-2.0.so.0`io_job_thread(task=<unavailable>, source_object=<unavailable>, task_data=0x00007a1e2307fc20, cancellable=<unavailable>) at gioscheduler.c:75:16
frame #8: 0x00007a1e399433bf libgio-2.0.so.0`g_task_thread_pool_thread(thread_data=0x00007a1e35c18ab0, pool_data=<unavailable>) at gtask.c:1583:3
frame #9: 0x00007a1e39db77e8 libglib-2.0.so.0`g_thread_pool_thread_proxy(data=<unavailable>) at gthreadpool.c:336:15
frame #10: 0x00007a1e39db5bfb libglib-2.0.so.0`g_thread_proxy(data=0x00007a1e378147d0) at gthread.c:835:20
frame #11: 0x00007a1e3a0b5c7b ld-musl-x86_64.so.1`start(p=0x00007a1e31a02170) at pthread_create.c:208:17
frame #12: 0x00007a1e3a0b8a8b ld-musl-x86_64.so.1`__clone + 47
```
note the 0xd which is 14 which is NegateOverflow in ubsan:
```
(lldb) x/1i $pc
-> 0x7a1e3898727b: 67 0f b9 40 0d other ud1l 0xd(%eax), %eax
```
for reference, the totally legal rar file is
https://img.ayaya.dev/05WYGFOcRPN9 , and this seems to only crash when
extracted via file-roller (or inside nautilus)
It appears that there are xar archives (in the form of Apple .pkg files)
that contain TOCs with duplicated name elements:
```xml
<file id="25">
<data> ... </data>
<type>file</type>
<name>PackageInfo</name>
<name>PackageInfo</name>
<name>PackageInfo</name>
</file>
```
When libarchive encounters one such file, it will produce an
archive_entry named PackageInfoPackageInfoPackageInfo.
To produce a test archive, the XAR writer was modified to emit two name
elements.
On Windows, the MBS pathname might be null if the string was set with a
WCS that can't be represented by the current locale. This is handled
properly by the rest of the code, but there's a sanity check that does
not make the proper distinction.
Note: this is a partial cherry-pick from
https://github.com/libarchive/libarchive/pull/2095, which I'm going to
go through and break into smaller pieces in hopes of getting some things
in while discussion of other things can continue.
There's no bug fix here - this just adds a test to verify that zip
creation when using the _w functions works as expected on Windows.
Note: this is a partial cherry-pick from
https://github.com/libarchive/libarchive/pull/2095, which I'm going to
go through and break into smaller pieces in hopes of getting some things
in while discussion of other things can continue.
This ensures that the buffer is properly initialized and does not
contain any leftover data from previous operations. It is used later in
the `archive_entry_copy_hardlink_l` function call and could be
uninitialized.
On Windows, if you are using `archive_entry_link_resolver` and give it
an entry that links to past entry whose pathname was set using a "wide"
string that cannot be represented by the current locale (i.e. WCS -> MBS
conversion fails), this code will crash due to a null pointer read. This
updates to use the `_w` function instead on Windows.
Note: this is a partial cherry-pick from
https://github.com/libarchive/libarchive/pull/2095, which I'm going to
go through and break into smaller pieces in hopes of getting some things
in while discussion of other things can continue.
On legacy systems the OS supplied `sys/queue.h` may lack the required
macros, so to avoid having to verify if the version of queue.h is of
use, opt to always to `la_queue.h` which will match expectations.
Allows libarchive to build on legacy Darwin where `STAILQ_FOREACH` would
be missing from `sys/queue.h`.
Resolves#2220
When using Clang in "MSVC mode" (i.e. clang-cl), command line arguments
are interpreted as MSVC would interpret them, at least when there are
conflicts. This means that `-Wall` - potentially among other switches -
is interpreted _dramatically_ differently by clang-cl compared to
"normal" Clang.
In CMake, this can be detected by testing for `if (MSVC)` in addition to
compiler id test, which is what I do here.
Note: this is a partial cherry-pick from #2095, which I'm going to go
through and break into smaller pieces in hopes of getting some things in
while discussion of other things can continue.
The tar utility reads from stderr to receive user input even when stdin
is a pipe. That is unfortunately unsupported on Windows.
The nearest equivalent is to reopen and read from the console input
handle.
Closes#2215
The parsers for 7zip and xar contain memory leaks. Also the testsuite
7zip leaks memory. Plug them.
Noticed while running test suite with address sanitizer on Linux.
Turning window_mask into ssize_t and adjusting the signature of
circular_memcpy reduces the amount of required casts. Since window_mask
depends on value window_size, which is ssize_t, this change is safe.
Also turned extra_data_size into int64_t, which is a no-op on 64 bit
systems but prevents 32 bit systems from truncating these huge values in
archives. Since these systems most likely have large file support, this
could have meant truncation -- in theory.
Reaching EOF for first time sets the correct amount of available bytes,
but each subsequent call returns 0.
Do not forget that the copy buffer can already contain data and return
the amount of bytes left in there.
See added test case.
This actually disabled CRC checks on regular builds and enabled it if
CRC checks were supposed to be disabled.
All other occurrences properly use ifndef instead of ifdef.
Bumps the all-actions group with 2 updates:
`actions/checkout` from 4.1.5 to 4.1.6
`github/codeql-action` from 3.25.5 to 3.25.6
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
`blocklength` should be bigger than `3` (channel count)
`byteoffset` should not be bigger than `2` (does not make sense as per the
last loop)
`src` should not overlap with `dst`.
There is no allocation in this function so it should be safe to return
early.
Security: GHSA-9qqv-q4qw-mf8m
If libarchive is built without zlib support, it is possible to trigger a
NULL pointer dereference with specially crafted 7zip files.
It takes multiple conditions to be able to reach the issue in crc32 and
all 7zip-specific ones are fixed with this PR.
Most of the functions already use size_t for sizes. Do the same for
xml_data. The libraries expat and libxml2 do not support strings larger
than 2 GB (limit is less than this) but it fixes one last Visual Studio
compiler warning which I missed previously.
Use casts where appropriate (values cannot exceed data type limits).
Fixes following warnings:
warning C4244: '=': conversion from 'int64_t' to 'long', possible loss
of data
warning C4244: '=': conversion from 'uint64_t' to 'size_t', possible
loss of data
Updates `actions/checkout` from 4.1.4 to 4.1.5
Updates `github/codeql-action` from 3.25.3 to 3.25.5
Updates `ossf/scorecard-action` from 2.3.1 to 2.3.3
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Adjust type of variables for their specific use case
Add a new inline function to unify casting (and clarify code blocks)
Use definition to explain magic number (and reduce casts)
Use `hpos` instead of magic number to highlight that additional bytes
will be parsed
Switch from int64_t to ssize_t for l and body, which is a no-op on 64
bit systems. On 32 bit systems, this change is okay because these
variables interact with other ssize_t variables in this scope.
Fixes compiler warning regarding line 584 in which ssize_t and int64_t
are mixed in calculations:
warning C4244: '=': conversion from 'int64_t' to 'long'
The cast in ppmd_read function is safe even on 32 bit systems. It is
called byte for byte which makes it impossible to actually reach
SIZE_MAX on any real world hardware.
Fixes Visual Studio warnings:
warning C4244: '=': conversion from 'uint64_t' to 'size_t', possible
loss of data
warning C4244: 'function': conversion from 'int64_t' to 'size_t',
possible loss of data
This setenv() call may clobber the memory pointed to by lang.
It is also insufficient, since you don't run in a clean environment, so
LANG may be overridden by an inherited LC_ALL or LC_CTYPE, or by the
user's .profile (remember that system() does not execute the command
directly, but passes it to a shell).
Reported-By: dag-erling (quoting)
copy_from_lzss_window_to_unp unnecessarily took an `int` parameter where
both of its callers were holding a `size_t`.
A lzss opcode chain could be constructed that resulted in a negative
copy length, which when passed into memcpy would result in a very, very
large positive number.
Switching copy_from_lzss_window_to_unp to take a `size_t` allows it to
properly bounds-check length.
In addition, this patch also ensures that `length` is not itself larger
than the destination buffer.
Security: CVE-2024-20696
If EOF is encountered while reading the new filename after choosing 'r',
avoid out of boundary access and usage of undefined memory content by
treating it the same way as if the question itself was not answered.
Processing excessively long lines could lead to out of boundary writes
or denial of service due to O(n^2) runtime complexity.
The OOB is properly fixed with first commit. The second commit stops
processing of lines which are longer than uu allows due to its
specification.
The comp size could be around INT_MAX on huge archives, which would lead
to eventual integer truncation to size_t in archives with version 1
headers when fixed value 2 is added to comp_size on 32 bit systems.
This fix is a no-op on 64 bit systems because size_t and uint64_t are of
same size there.
Cast any of cd_offset or cd_size to int64_t to avoid truncation of
result because both variables are of type uint32_t.
The calculation happens before comparison with current_offset, so it is
not automatically expanded to int64_t during calculation.
If an RPM file contains a huge header which is larger than 4 GB then
libarchive starts parsing the RPM header as actual archive instead of
skipping it.
Switched to uint64_t from size_t for proper 32 bit support as well.
When working with matches, the code does not care about the actual
amount of times when it matched, but just if it matched so far at least
once or never.
Turning the counter into a boolean flag has the advantage that even
insanely huge archives will never lead to integer overflow here.
If call of archive_read_support_format_raw fails, do not override the
error return value with the return value of
archive_read_support_format_empty(). Instead, return error code as expected.
Ensure that `src` won't move ahead of `dst`, so `src` will not OOB.
Since `dst` won't move in this function, and we are only increasing `src`
position, this check should be enough. It should be safe to early return
because this function does not allocate resources.
@@ -37,10 +37,13 @@ The top-level directory contains the following information files:
* **CMakeLists.txt** - input for "cmake" build tool, see INSTALL
* **configure** - configuration script, see INSTALL for details. If your copy of the source lacks a `configure` script, you can try to construct it by running the script in `build/autogen.sh` (or use `cmake`).
The following files in the top-level directory are used by the 'configure' script:
The following files in the top-level directory are related to the 'configure' script and are only needed by maintainers:
*`Makefile.am`, `aclocal.m4`, `configure.ac` - used to build this distribution, only needed by maintainers
*`Makefile.in`, `config.h.in` - templates used by configure script
*`configure.ac` - used (by autoconf) to build the configure script and related files
*`Makefile.am` - used (by automake) to generate Makefile.in
*`aclocal.m4` - auto-generated file (created by aclocal) used to build the configure script
*`Makefile.in` - auto-generated template (created by automake) used by the configure script to create Makefile
*`config.h.in` - auto-generated template (created by autoheader) used by the configure script to create config.h
## Documentation
@@ -85,13 +88,14 @@ Currently, the library automatically detects and reads the following formats:
* PWB binary cpio
* ISO9660 CD-ROM images (with optional Rockridge or Joliet extensions)
* ZIP archives (with uncompressed or "deflate" compressed entries, including support for encrypted Zip archives)
* ZIPX archives (with support for bzip2, ppmd8, lzma and xz compressed entries)
* ZIPX archives (with support for bzip2, zstd, ppmd8, lzma and xz compressed entries)
* GNU and BSD 'ar' archives
* 'mtree' format
* 7-Zip archives (including archives that use zstandard compression)
* Microsoft CAB format
* LHA and LZH archives
* RAR and RAR 5.0 archives (with some limitations due to RAR's proprietary status)
* WARC archives
* XAR archives
The library also detects and handles any of the following before evaluating the archive:
@@ -120,15 +124,18 @@ The library can create archives in any of the following formats:
* PWB binary cpio
* shar archives
* ZIP archives (with uncompressed or "deflate" compressed entries)
RUN C:\tools\cygwin\cygwinsetup.exe -q -P make,autoconf,automake,cmake,gcc-core,binutils,libtool,pkg-config,bison,sharutils,zlib-devel,libbz2-devel,liblzma-devel,liblz4-devel,libiconv-devel,libxml2-devel,libzstd-devel,libssl-devel
RUN C:\tools\cygwin\cygwinsetup.exe -q -P make,autoconf,automake,cmake,gcc-core,binutils,libtool,pkg-config,bison,sharutils,zlib-devel,libbz2-devel,liblzma-devel,liblz4-devel,libiconv-devel,libxml2-devel,libzstd-devel,libssl-devel
Loaded 100 of 892 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.