From: Tobias Stoeckmann Date: Fri, 27 Jun 2025 15:06:00 +0000 (+0200) Subject: Ignore sizes which do not fit into off_t X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F2688%2Fhead;p=thirdparty%2Flibarchive.git Ignore sizes which do not fit into off_t 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 --- diff --git a/libarchive/archive_entry_stat.c b/libarchive/archive_entry_stat.c index c4906838e..345d3d29b 100644 --- a/libarchive/archive_entry_stat.c +++ b/libarchive/archive_entry_stat.c @@ -38,6 +38,7 @@ const struct stat * archive_entry_stat(struct archive_entry *entry) { + int64_t size; struct stat *st; if (entry->stat == NULL) { entry->stat = calloc(1, sizeof(*st)); @@ -74,7 +75,10 @@ archive_entry_stat(struct archive_entry *entry) st->st_ino = (ino_t)archive_entry_ino64(entry); st->st_nlink = archive_entry_nlink(entry); st->st_rdev = archive_entry_rdev(entry); - st->st_size = (off_t)archive_entry_size(entry); + size = archive_entry_size(entry); + st->st_size = (off_t)size; + if (st->st_size < 0 || (int64_t)st->st_size != size) + st->st_size = 0; st->st_mode = archive_entry_mode(entry); /* diff --git a/libarchive/archive_windows.c b/libarchive/archive_windows.c index 0ccea0f81..19180bba6 100644 --- a/libarchive/archive_windows.c +++ b/libarchive/archive_windows.c @@ -561,6 +561,8 @@ copy_stat(struct stat *st, struct ustat *us) st->st_mode = us->st_mode; st->st_nlink = us->st_nlink; st->st_size = (off_t)us->st_size; + if (st->st_size < 0 || (uint64_t)st->st_size != us->st_size) + st->st_size = 0; st->st_uid = us->st_uid; st->st_dev = us->st_dev; st->st_rdev = us->st_rdev; diff --git a/libarchive/test/test_entry.c b/libarchive/test/test_entry.c index 9b21b83ec..cff9c5c86 100644 --- a/libarchive/test/test_entry.c +++ b/libarchive/test/test_entry.c @@ -880,6 +880,17 @@ DEFINE_TEST(test_entry) if (pst == NULL) return; assertEqualInt(pst->st_uid, 22); + + /* Check behavior with large sizes. */ + archive_entry_set_size(e, INT64_MAX - 1); + assert((pst = archive_entry_stat(e)) != NULL); + if (pst == NULL) + return; + if (sizeof(pst->st_size) < sizeof(int64_t)) + assertEqualInt(pst->st_size, 0); + else + assertEqualInt(pst->st_size, INT64_MAX - 1); + /* We don't need to check high-res fields here. */ /*