Index: libarchive-3.7.4/libarchive/archive_read_open_filename.c
===================================================================
--- libarchive-3.7.4.orig/libarchive/archive_read_open_filename.c
+++ libarchive-3.7.4/libarchive/archive_read_open_filename.c
@@ -479,20 +479,24 @@ file_skip_lseek(struct archive *a, void
 	struct read_file_data *mine = (struct read_file_data *)client_data;
 #if defined(_WIN32) && !defined(__CYGWIN__)
 	/* We use _lseeki64() on Windows. */
-	int64_t old_offset, new_offset;
+	int64_t old_offset, new_offset, skip = request;
 #else
-	off_t old_offset, new_offset;
+	off_t old_offset, new_offset, skip = (off_t)request;
 #endif
+	int skip_bits = sizeof(skip) * 8 - 1;
 
 	/* We use off_t here because lseek() is declared that way. */
 
-	/* TODO: Deal with case where off_t isn't 64 bits.
-	 * This shouldn't be a problem on Linux or other POSIX
-	 * systems, since the configuration logic for libarchive
-	 * tries to obtain a 64-bit off_t.
-	 */
+	/* Reduce a request that would overflow the 'skip' variable. */
+	if (sizeof(request) > sizeof(skip)) {
+		const int64_t max_skip =
+		    (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;
+		if (request > max_skip)
+			skip = max_skip;
+	}
+
 	if ((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0 &&
-	    (new_offset = lseek(mine->fd, request, SEEK_CUR)) >= 0)
+	    (new_offset = lseek(mine->fd, skip, SEEK_CUR)) >= 0)
 		return (new_offset - old_offset);
 
 	/* If lseek() fails, don't bother trying again. */
@@ -540,11 +544,24 @@ static int64_t
 file_seek(struct archive *a, void *client_data, int64_t request, int whence)
 {
 	struct read_file_data *mine = (struct read_file_data *)client_data;
+	off_t seek = (off_t)request;
 	int64_t r;
+	int seek_bits = sizeof(seek) * 8 - 1;
 
 	/* We use off_t here because lseek() is declared that way. */
-	/* See above for notes about when off_t is less than 64 bits. */
-	r = lseek(mine->fd, request, whence);
+
+	/* Reduce a request that would overflow the 'seek' variable. */
+	if (sizeof(request) > sizeof(seek)) {
+		const int64_t max_seek =
+		    (((int64_t)1 << (seek_bits - 1)) - 1) * 2 + 1;
+		const int64_t min_seek = ~max_seek;
+		if (request > max_seek)
+			seek = (off_t)max_seek;
+		else if (request < min_seek)
+			seek = (off_t)min_seek;
+	}
+
+	r = lseek(mine->fd, seek, whence);
 	if (r >= 0)
 		return r;

From c657e7901d1c1397d246960e7a694be1ea752cf6 Mon Sep 17 00:00:00 2001
From: Tobias Stoeckmann <tobias@stoeckmann.org>
Date: Sun, 13 Apr 2025 22:35:50 +0200
Subject: [PATCH] Do not skip past EOF while reading

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>
---
 libarchive/archive_read_open_fd.c       | 13 +++++++---
 libarchive/archive_read_open_file.c     | 33 +++++++++++++++++++------
 libarchive/archive_read_open_filename.c | 16 +++++++++---
 libarchive/test/test_read_format_rar.c  |  6 ++---
 4 files changed, 50 insertions(+), 18 deletions(-)

Index: libarchive-3.7.4/libarchive/archive_read_open_fd.c
===================================================================
--- libarchive-3.7.4.orig/libarchive/archive_read_open_fd.c
+++ libarchive-3.7.4/libarchive/archive_read_open_fd.c
@@ -52,6 +52,7 @@
 struct read_fd_data {
 	int	 fd;
 	size_t	 block_size;
+	int64_t	 size;
 	char	 use_lseek;
 	void	*buffer;
 };
@@ -95,6 +96,7 @@ archive_read_open_fd(struct archive *a,
 	if (S_ISREG(st.st_mode)) {
 		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
 		mine->use_lseek = 1;
+		mine->size = st.st_size;
 	}
 #if defined(__CYGWIN__) || defined(_WIN32)
 	setmode(mine->fd, O_BINARY);
@@ -151,9 +153,14 @@ file_skip(struct archive *a, void *clien
 	if (request == 0)
 		return (0);
 
-	if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0) &&
-	    ((new_offset = lseek(mine->fd, skip, SEEK_CUR)) >= 0))
-		return (new_offset - old_offset);
+	if ((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0) {
+		if (old_offset >= mine->size ||
+		    skip > mine->size - old_offset) {
+			/* Do not seek past end of file. */
+			errno = ESPIPE;
+		} else if ((new_offset = lseek(mine->fd, skip, SEEK_CUR)) >= 0)
+			return (new_offset - old_offset);
+	}
 
 	/* If seek failed once, it will probably fail again. */
 	mine->use_lseek = 0;
Index: libarchive-3.7.4/libarchive/archive_read_open_file.c
===================================================================
--- libarchive-3.7.4.orig/libarchive/archive_read_open_file.c
+++ libarchive-3.7.4/libarchive/archive_read_open_file.c
@@ -52,6 +52,7 @@
 struct read_FILE_data {
 	FILE    *f;
 	size_t	 block_size;
+	int64_t	 size;
 	void	*buffer;
 	char	 can_skip;
 };
@@ -90,6 +91,7 @@ archive_read_open_FILE(struct archive *a
 		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
 		/* Enable the seek optimization only for regular files. */
 		mine->can_skip = 1;
+		mine->size = st.st_size;
 	} else
 		mine->can_skip = 0;
 
@@ -129,6 +131,7 @@ file_skip(struct archive *a, void *clien
 #else
 	long skip = (long)request;
 #endif
+	int64_t old_offset, new_offset;
 	int skip_bits = sizeof(skip) * 8 - 1;
 
 	(void)a; /* UNUSED */
@@ -152,19 +155,33 @@ file_skip(struct archive *a, void *clien
 
 #ifdef __ANDROID__
         /* fileno() isn't safe on all platforms ... see above. */
-	if (lseek(fileno(mine->f), skip, SEEK_CUR) < 0)
+	old_offset = lseek(fileno(mine->f), 0, SEEK_CUR);
 #elif HAVE__FSEEKI64
-	if (_fseeki64(mine->f, skip, SEEK_CUR) != 0)
+	old_offset = _ftelli64(mine->f);
 #elif HAVE_FSEEKO
-	if (fseeko(mine->f, skip, SEEK_CUR) != 0)
+	old_offset = ftello(mine->f);
 #else
-	if (fseek(mine->f, skip, SEEK_CUR) != 0)
+	old_offset = ftell(mine->f);
 #endif
-	{
-		mine->can_skip = 0;
-		return (0);
+	if (old_offset >= 0) {
+		if (old_offset < mine->size &&
+		    skip <= mine->size - old_offset) {
+#ifdef __ANDROID__
+			new_offset = lseek(fileno(mine->f), skip, SEEK_CUR);
+#elif HAVE__FSEEKI64
+			new_offset = _fseeki64(mine->f, skip, SEEK_CUR);
+#elif HAVE_FSEEKO
+			new_offset = fseeko(mine->f, skip, SEEK_CUR);
+#else
+			new_offset = fseek(mine->f, skip, SEEK_CUR);
+#endif
+			if (new_offset >= 0)
+				return (new_offset - old_offset);
+		}
 	}
-	return (request);
+
+	mine->can_skip = 0;
+	return (0);
 }
 
 static int
Index: libarchive-3.7.4/libarchive/archive_read_open_filename.c
===================================================================
--- libarchive-3.7.4.orig/libarchive/archive_read_open_filename.c
+++ libarchive-3.7.4/libarchive/archive_read_open_filename.c
@@ -74,6 +74,7 @@ struct read_file_data {
 	size_t	 block_size;
 	void	*buffer;
 	mode_t	 st_mode;  /* Mode bits for opened file. */
+	int64_t	 size;
 	char	 use_lseek;
 	enum fnt_e { FNT_STDIN, FNT_MBS, FNT_WCS } filename_type;
 	union {
@@ -400,8 +401,10 @@ file_open(struct archive *a, void *clien
 	mine->st_mode = st.st_mode;
 
 	/* Disk-like inputs can use lseek(). */
-	if (is_disk_like)
+	if (is_disk_like) {
 		mine->use_lseek = 1;
+		mine->size = st.st_size;
+	}
 
 	return (ARCHIVE_OK);
 fail:
@@ -495,9 +498,14 @@ file_skip_lseek(struct archive *a, void
 			skip = max_skip;
 	}
 
-	if ((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0 &&
-	    (new_offset = lseek(mine->fd, skip, SEEK_CUR)) >= 0)
-		return (new_offset - old_offset);
+	if ((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0) {
+		if (old_offset >= mine->size ||
+		    skip > mine->size - old_offset) {
+			/* Do not seek past end of file. */
+			errno = ESPIPE;
+		} else if ((new_offset = lseek(mine->fd, skip, SEEK_CUR)) >= 0)
+			return (new_offset - old_offset);
+	}
 
 	/* If lseek() fails, don't bother trying again. */
 	mine->use_lseek = 0;
Index: libarchive-3.7.4/libarchive/test/test_read_format_rar.c
===================================================================
--- libarchive-3.7.4.orig/libarchive/test/test_read_format_rar.c
+++ libarchive-3.7.4/libarchive/test/test_read_format_rar.c
@@ -3829,8 +3829,8 @@ DEFINE_TEST(test_read_format_rar_ppmd_us
   assertA(ARCHIVE_OK == archive_read_next_header(a, &ae));
   assertA(archive_read_data(a, buf, sizeof(buf)) <= 0);
 
-  /* Test EOF */
-  assertA(1 == archive_read_next_header(a, &ae));
+  /* Test for truncation */
+  assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae));
 
   assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
   assertEqualInt(ARCHIVE_OK, archive_read_free(a));
@@ -3856,7 +3856,7 @@ DEFINE_TEST(test_read_format_rar_ppmd_us
   assertA(archive_read_data(a, buf, sizeof(buf)) <= 0);
 
   /* Test EOF */
-  assertA(1 == archive_read_next_header(a, &ae));
+  assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae));
 
   assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
   assertEqualInt(ARCHIVE_OK, archive_read_free(a));

From 51b4c35bb38b7df4af24de7f103863dd79129b01 Mon Sep 1700:00:00 2001
From: Tobias Stoeckmann <tobias@stoeckmann.org>
Date: Tue, 27 May 2025 17:09:12 +0200
Subject: [PATCH] Fix FILE_skip regression

The fseek* family of functions return 0 on success, not the new offset.
This is only true for lseek.

Fixes https://github.com/libarchive/libarchive/issues/2641
Fixes dcbf1e0ededa95849f098d154a25876ed5754bcf

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
---
 libarchive/archive_read_open_file.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

Index: libarchive-3.7.4/libarchive/archive_read_open_file.c
===================================================================
--- libarchive-3.7.4.orig/libarchive/archive_read_open_file.c
+++ libarchive-3.7.4/libarchive/archive_read_open_file.c
@@ -131,7 +131,7 @@ file_skip(struct archive *a, void *clien
 #else
 	long skip = (long)request;
 #endif
-	int64_t old_offset, new_offset;
+	int64_t old_offset, new_offset = -1;
 	int skip_bits = sizeof(skip) * 8 - 1;
 
 	(void)a; /* UNUSED */
@@ -169,11 +169,14 @@ file_skip(struct archive *a, void *clien
 #ifdef __ANDROID__
 			new_offset = lseek(fileno(mine->f), skip, SEEK_CUR);
 #elif HAVE__FSEEKI64
-			new_offset = _fseeki64(mine->f, skip, SEEK_CUR);
+			if (_fseeki64(mine->f, skip, SEEK_CUR) == 0)
+				new_offset = _ftelli64(mine->f);
 #elif HAVE_FSEEKO
-			new_offset = fseeko(mine->f, skip, SEEK_CUR);
+			if (fseeko(mine->f, skip, SEEK_CUR) == 0)
+				new_offset = ftello(mine->f);
 #else
-			new_offset = fseek(mine->f, skip, SEEK_CUR);
+			if (fseek(mine->f, skip, SEEK_CUR) == 0)
+				new_offset = ftell(mine->f);
 #endif
 			if (new_offset >= 0)
 				return (new_offset - old_offset);
