aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLasse Collin <lasse.collin@tukaani.org>2019-06-24 20:45:49 +0300
committerLasse Collin <lasse.collin@tukaani.org>2019-12-31 22:25:02 +0200
commitc8cace3d6e965c0fb537591372bf71b9357dd76c (patch)
treebd0adb7bb7fd71ea02ece0e92a27f06132baa868
parentTests: Remove a duplicate branch from tests/tests.h. (diff)
downloadxz-c8cace3d6e965c0fb537591372bf71b9357dd76c.tar.xz
xz: Fix an integer overflow with 32-bit off_t.
Or any off_t which isn't very big (like signed 64 bit integer that most system have). A small off_t could overflow if the file being decompressed had long enough run of zero bytes, which would result in corrupt output.
-rw-r--r--src/xz/file_io.c11
1 files changed, 9 insertions, 2 deletions
diff --git a/src/xz/file_io.c b/src/xz/file_io.c
index 2352b297..6db62e76 100644
--- a/src/xz/file_io.c
+++ b/src/xz/file_io.c
@@ -1273,8 +1273,15 @@ io_write(file_pair *pair, const io_buf *buf, size_t size)
// if the file ends with sparse block, we must also return
// if size == 0 to avoid doing the lseek().
if (size == IO_BUFFER_SIZE) {
- if (is_sparse(buf)) {
- pair->dest_pending_sparse += size;
+ // Even if the block was sparse, treat it as non-sparse
+ // if the pending sparse amount is large compared to
+ // the size of off_t. In practice this only matters
+ // on 32-bit systems where off_t isn't always 64 bits.
+ const off_t pending_max
+ = (off_t)(1) << (sizeof(off_t) * CHAR_BIT - 2);
+ if (is_sparse(buf) && pair->dest_pending_sparse
+ < pending_max) {
+ pair->dest_pending_sparse += (off_t)(size);
return false;
}
} else if (size == 0) {