summaryrefslogtreecommitdiff
path: root/inffast.c
diff options
context:
space:
mode:
authorNathan Moinvaziri <nathan@nathanm.com>2021-06-10 17:19:25 -0700
committerHans Kristian Rosbach <hk-git@circlestorm.org>2021-06-13 10:27:04 +0200
commitc79998f79069b3d0c6ec9a319beeba9da4feb421 (patch)
tree693665ec2ab88a4503cd00d1c4ddb34bdc76ff62 /inffast.c
parent558192b4a6f9c8142a0e17ff3c4574a20a2dc96f (diff)
Must use safe chunk copies due to inflateBack using the same allocation for output and window. In this instance if too many bytes are written it will not correctly write matches with distances close to the window size.
Diffstat (limited to 'inffast.c')
-rw-r--r--inffast.c18
1 files changed, 17 insertions, 1 deletions
diff --git a/inffast.c b/inffast.c
index c8d3da3..c431e50 100644
--- a/inffast.c
+++ b/inffast.c
@@ -128,6 +128,7 @@ void Z_INTERNAL zng_inflate_fast(PREFIX3(stream) *strm, unsigned long start) {
unsigned len; /* match length, unused bytes */
unsigned dist; /* match distance */
unsigned char *from; /* where to copy match from */
+ unsigned extra_safe; /* copy chunks safely in all cases */
/* copy state to local variables */
state = (struct inflate_state *)strm->state;
@@ -151,6 +152,11 @@ void Z_INTERNAL zng_inflate_fast(PREFIX3(stream) *strm, unsigned long start) {
lmask = (1U << state->lenbits) - 1;
dmask = (1U << state->distbits) - 1;
+ /* Detect if out and window point to the same memory allocation. In this instance it is
+ necessary to use safe chunk copy functions to prevent overwriting the window. If the
+ window is overwritten then future matches with far distances will fail to copy correctly. */
+ extra_safe = (out >= window && out + INFLATE_FAST_MIN_LEFT <= window + wsize);
+
/* decode literals and length/distances until end-of-block or not enough
input data or output space */
do {
@@ -259,12 +265,22 @@ void Z_INTERNAL zng_inflate_fast(PREFIX3(stream) *strm, unsigned long start) {
} else {
out = functable.chunkcopy_safe(out, from, len, safe);
}
- } else {
+ } else if (extra_safe) {
/* Whole reference is in range of current output. */
if (dist >= len || dist >= state->chunksize)
out = functable.chunkcopy_safe(out, out - dist, len, safe);
else
out = functable.chunkmemset_safe(out, dist, len, safe - out + 1);
+ } else {
+ /* Whole reference is in range of current output. No range checks are
+ necessary because we start with room for at least 258 bytes of output,
+ so unroll and roundoff operations can write beyond `out+len` so long
+ as they stay within 258 bytes of `out`.
+ */
+ if (dist >= len || dist >= state->chunksize)
+ out = functable.chunkcopy(out, out - dist, len);
+ else
+ out = functable.chunkmemset(out, dist, len);
}
} else if ((op & 64) == 0) { /* 2nd level distance code */
here = dcode + here->val + BITS(op);