Skip to content

Commit 56ac0f1

Browse files
committed
Implement write bounds checking within libzpaq to prevent corrupt or malicious input attempting to write beyond buffer size.
1 parent 843fa41 commit 56ac0f1

3 files changed

Lines changed: 42 additions & 11 deletions

File tree

libzpaq/libzpaq.h

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -503,13 +503,35 @@ struct bufRead: public libzpaq::Reader {
503503
struct bufWrite: public libzpaq::Writer {
504504
uchar *c_buf;
505505
i64 *c_len;
506-
bufWrite(uchar *buf_, i64 *n_): c_buf(buf_), c_len(n_) {}
506+
i64 max_len;
507+
bufWrite(uchar *buf_, i64 *n_, i64 max_ = -1LL): c_buf(buf_), c_len(n_), max_len(max_) {}
507508

508509
void put(int c) {
509-
c_buf[(*c_len)++] = (uchar)c;
510+
//c_buf[(*c_len)++] = (uchar)c;
511+
if (*c_len < 0) return;
512+
if (max_len >= 0 && *c_len >= max_len) {
513+
*c_len = -1;
514+
return;
515+
}
516+
c_buf[(*c_len)++] = static_cast<uchar>(c);
510517
}
511518

512519
void write(const char *buf, int n) {
520+
if (n <= 0 || *c_len < 0) return;
521+
if (max_len < 0) {
522+
memcpy(c_buf + *c_len, buf, n);
523+
*c_len += n;
524+
return;
525+
}
526+
i64 avail = max_len - *c_len;
527+
if (avail <= 0) {
528+
*c_len = -1;
529+
return;
530+
}
531+
if ((i64)n > avail) {
532+
*c_len = -1;
533+
return;
534+
}
513535
memcpy(c_buf + *c_len, buf, n);
514536
*c_len += n;
515537
}
@@ -527,16 +549,19 @@ extern "C" void zpaq_compress(uchar *c_buf, i64 *c_len, uchar *s_buf, i64 s_len,
527549
compress (&bufR, &bufW, level);
528550
}
529551

530-
extern "C" void zpaq_decompress(uchar *s_buf, i64 *d_len, uchar *c_buf, i64 c_len,
531-
FILE *msgout, bool progress, long thread)
552+
extern "C" int zpaq_decompress(uchar *s_buf, i64 *d_len, uchar *c_buf, i64 c_len,
553+
FILE *msgout, bool progress, long thread,
554+
i64 expected_len)
532555
{
533556
i64 total_len = c_len;
534557
int last_pct = 100;
535558

536559
bufRead bufR(c_buf, &c_len, total_len, &last_pct, progress, thread, msgout);
537-
bufWrite bufW(s_buf, d_len);
560+
bufWrite bufW(s_buf, d_len, expected_len);
538561

539562
decompress(&bufR, &bufW);
563+
if (*d_len < 0) return -1; // overflow attempt detected
564+
return 0;
540565
}
541566

542567
#endif // LIBZPAQ_H

lrzip_core.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (C) 2006-2016,2022 Con Kolivas
2+
Copyright (C) 2006-2016,2022,2026 Con Kolivas
33
Copyright (C) 2011 Peter Hyman
44
Copyright (C) 1998-2003 Andrew Tridgell
55
@@ -44,7 +44,7 @@ bool initialise_control(rzip_control *control);
4444
#define initialize_control(_control) initialise_control(_control)
4545
extern void zpaq_compress(uchar *c_buf, i64 *c_len, uchar *s_buf, i64 s_len, int level,
4646
FILE *msgout, bool progress, long thread);
47-
extern void zpaq_decompress(uchar *s_buf, i64 *d_len, uchar *c_buf, i64 c_len,
48-
FILE *msgout, bool progress, long thread);
47+
extern int zpaq_decompress(uchar *s_buf, i64 *d_len, uchar *c_buf, i64 c_len,
48+
FILE *msgout, bool progress, long thread, i64 expected_len);
4949

5050
#endif

stream.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ static int zpaq_decompress_buf(rzip_control *control __UNUSED__, struct uncomp_t
441441
{
442442
i64 dlen = ucthread->u_len;
443443
uchar *c_buf;
444-
int ret = 0;
444+
int zd_ret, ret = 0;
445445

446446
c_buf = ucthread->s_buf;
447447
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
452452
}
453453

454454
dlen = 0;
455-
zpaq_decompress(ucthread->s_buf, &dlen, c_buf, ucthread->c_len,
456-
control->msgout, SHOW_PROGRESS ? true: false, thread);
455+
zd_ret = zpaq_decompress(ucthread->s_buf, &dlen, c_buf, ucthread->c_len,
456+
control->msgout, SHOW_PROGRESS ? true: false, thread,
457+
ucthread->u_len);
457458

459+
if (unlikely(zd_ret < 0)) {
460+
print_err("Attempted to write beyond expected output size, corrupted input.\n");
461+
ret = -1;
462+
goto out;
463+
}
458464
if (unlikely(dlen != ucthread->u_len)) {
459465
print_err("Inconsistent length after decompression. Got %ld bytes, expected %"PRId64"\n", dlen, ucthread->u_len);
460466
ret = -1;

0 commit comments

Comments
 (0)