diff --git a/libzpaq/libzpaq.h b/libzpaq/libzpaq.h index cbe211d..aab6564 100644 --- a/libzpaq/libzpaq.h +++ b/libzpaq/libzpaq.h @@ -503,13 +503,35 @@ struct bufRead: public libzpaq::Reader { struct bufWrite: public libzpaq::Writer { uchar *c_buf; i64 *c_len; - bufWrite(uchar *buf_, i64 *n_): c_buf(buf_), c_len(n_) {} + i64 max_len; + bufWrite(uchar *buf_, i64 *n_, i64 max_ = -1LL): c_buf(buf_), c_len(n_), max_len(max_) {} void put(int c) { - c_buf[(*c_len)++] = (uchar)c; + //c_buf[(*c_len)++] = (uchar)c; + if (*c_len < 0) return; + if (max_len >= 0 && *c_len >= max_len) { + *c_len = -1; + return; + } + c_buf[(*c_len)++] = static_cast(c); } void write(const char *buf, int n) { + if (n <= 0 || *c_len < 0) return; + if (max_len < 0) { + memcpy(c_buf + *c_len, buf, n); + *c_len += n; + return; + } + i64 avail = max_len - *c_len; + if (avail <= 0) { + *c_len = -1; + return; + } + if ((i64)n > avail) { + *c_len = -1; + return; + } memcpy(c_buf + *c_len, buf, n); *c_len += n; } @@ -527,16 +549,19 @@ extern "C" void zpaq_compress(uchar *c_buf, i64 *c_len, uchar *s_buf, i64 s_len, compress (&bufR, &bufW, level); } -extern "C" void zpaq_decompress(uchar *s_buf, i64 *d_len, uchar *c_buf, i64 c_len, - FILE *msgout, bool progress, long thread) +extern "C" int zpaq_decompress(uchar *s_buf, i64 *d_len, uchar *c_buf, i64 c_len, + FILE *msgout, bool progress, long thread, + i64 expected_len) { i64 total_len = c_len; int last_pct = 100; bufRead bufR(c_buf, &c_len, total_len, &last_pct, progress, thread, msgout); - bufWrite bufW(s_buf, d_len); + bufWrite bufW(s_buf, d_len, expected_len); decompress(&bufR, &bufW); + if (*d_len < 0) return -1; // overflow attempt detected + return 0; } #endif // LIBZPAQ_H diff --git a/lrzip_core.h b/lrzip_core.h index e424667..6f55a29 100644 --- a/lrzip_core.h +++ b/lrzip_core.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2006-2016,2022 Con Kolivas + Copyright (C) 2006-2016,2022,2026 Con Kolivas Copyright (C) 2011 Peter Hyman Copyright (C) 1998-2003 Andrew Tridgell @@ -44,7 +44,7 @@ bool initialise_control(rzip_control *control); #define initialize_control(_control) initialise_control(_control) extern void zpaq_compress(uchar *c_buf, i64 *c_len, uchar *s_buf, i64 s_len, int level, FILE *msgout, bool progress, long thread); -extern void zpaq_decompress(uchar *s_buf, i64 *d_len, uchar *c_buf, i64 c_len, - FILE *msgout, bool progress, long thread); +extern int zpaq_decompress(uchar *s_buf, i64 *d_len, uchar *c_buf, i64 c_len, + FILE *msgout, bool progress, long thread, i64 expected_len); #endif diff --git a/stream.c b/stream.c index 61e73a0..f4d30e4 100644 --- a/stream.c +++ b/stream.c @@ -441,7 +441,7 @@ static int zpaq_decompress_buf(rzip_control *control __UNUSED__, struct uncomp_t { i64 dlen = ucthread->u_len; uchar *c_buf; - int ret = 0; + int zd_ret, ret = 0; c_buf = ucthread->s_buf; ucthread->s_buf = malloc(round_up_page(control, dlen)); @@ -452,9 +452,15 @@ static int zpaq_decompress_buf(rzip_control *control __UNUSED__, struct uncomp_t } dlen = 0; - zpaq_decompress(ucthread->s_buf, &dlen, c_buf, ucthread->c_len, - control->msgout, SHOW_PROGRESS ? true: false, thread); + zd_ret = zpaq_decompress(ucthread->s_buf, &dlen, c_buf, ucthread->c_len, + control->msgout, SHOW_PROGRESS ? true: false, thread, + ucthread->u_len); + if (unlikely(zd_ret < 0)) { + print_err("Attempted to write beyond expected output size, corrupted input.\n"); + ret = -1; + goto out; + } if (unlikely(dlen != ucthread->u_len)) { print_err("Inconsistent length after decompression. Got %ld bytes, expected %"PRId64"\n", dlen, ucthread->u_len); ret = -1;