]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Rework size-overrun check to avoid arithmetic errors
authorTim Kientzle <kientzle@acm.org>
Fri, 3 Jul 2026 23:09:16 +0000 (16:09 -0700)
committerTim Kientzle <kientzle@acm.org>
Sat, 4 Jul 2026 04:41:08 +0000 (21:41 -0700)
libarchive/archive_read_data_into_fd.c

index 2e1b0a096b8df1cd005cb7f46f5ef04fa433c5fc..241a3ca64ea3770789132e600cfdcd35b6f121ad 100644 (file)
@@ -119,23 +119,20 @@ archive_read_data_into_fd(struct archive *a, int fd)
                const char *p = buff;
                int overran = 0;
 
-               /*
-                * Never write more than the entry declared, regardless of
-                * what the format reader hands back: callers of this
-                * function (e.g. "bsdtar -xO", bsdcat) write straight to an
-                * fd and skip archive_write_disk's own truncation safeguard.
-                * A block that ends exactly at declared_size is the normal,
-                * expected shape of the entry's last block, not an overrun.
-                */
-               if (declared_size >= 0 && target_offset + (int64_t)size >
-                   declared_size) {
-                       overran = 1;
-                       if (target_offset < declared_size)
-                               size = (size_t)(declared_size - target_offset);
-                       else
-                               size = 0;
-                       if (target_offset > declared_size)
+               if (declared_size >= 0) {
+                       /* We know the expected size, let's enforce that we don't overrun. */
+                       if (target_offset > declared_size) {
+                               /* We're already beyond the end? Don't write any more. */
                                target_offset = declared_size;
+                               size = 0;
+                               overran = 1;
+                       } else if ((int64_t)size > declared_size - target_offset) {
+                               /* Above is safe because target_offset <= declared_size */
+                               /* This block is bigger than the expected remainder */
+                               size = (size_t)(declared_size - target_offset);
+                               overran = 1;
+                       }
+                       /* Else size <= expected remainder of data and we're OK. */
                }
 
                if (target_offset > actual_offset) {