Commit 75340b5a authored by Jann Horn's avatar Jann Horn Committed by Kelsey Skunberg

lib/zlib: remove outdated and incorrect pre-increment optimization

BugLink: https://bugs.launchpad.net/bugs/1885932

[ Upstream commit acaab733 ]

The zlib inflate code has an old micro-optimization based on the
assumption that for pre-increment memory accesses, the compiler will
generate code that fits better into the processor's pipeline than what
would be generated for post-increment memory accesses.

This optimization was already removed in upstream zlib in 2016:
https://github.com/madler/zlib/commit/9aaec95e8211

This optimization causes UB according to C99, which says in section 6.5.6
"Additive operators": "If both the pointer operand and the result point to
elements of the same array object, or one past the last element of the
array object, the evaluation shall not produce an overflow; otherwise, the
behavior is undefined".

This UB is not only a theoretical concern, but can also cause trouble for
future work on compiler-based sanitizers.

According to the zlib commit, this optimization also is not optimal
anymore with modern compilers.

Replace uses of OFF, PUP and UP_UNALIGNED with their definitions in the
POSTINC case, and remove the macro definitions, just like in the upstream
patch.
Signed-off-by: default avatarJann Horn <jannh@google.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Cc: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Link: http://lkml.kernel.org/r/20200507123112.252723-1-jannh@google.comSigned-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Signed-off-by: default avatarKamal Mostafa <kamal@canonical.com>
Signed-off-by: default avatarKelsey Skunberg <kelsey.skunberg@canonical.com>
parent b7a26937
...@@ -10,17 +10,6 @@ ...@@ -10,17 +10,6 @@
#ifndef ASMINF #ifndef ASMINF
/* Allow machine dependent optimization for post-increment or pre-increment.
Based on testing to date,
Pre-increment preferred for:
- PowerPC G3 (Adler)
- MIPS R5000 (Randers-Pehrson)
Post-increment preferred for:
- none
No measurable difference:
- Pentium III (Anderson)
- M68060 (Nikl)
*/
union uu { union uu {
unsigned short us; unsigned short us;
unsigned char b[2]; unsigned char b[2];
...@@ -38,16 +27,6 @@ get_unaligned16(const unsigned short *p) ...@@ -38,16 +27,6 @@ get_unaligned16(const unsigned short *p)
return mm.us; return mm.us;
} }
#ifdef POSTINC
# define OFF 0
# define PUP(a) *(a)++
# define UP_UNALIGNED(a) get_unaligned16((a)++)
#else
# define OFF 1
# define PUP(a) *++(a)
# define UP_UNALIGNED(a) get_unaligned16(++(a))
#endif
/* /*
Decode literal, length, and distance codes and write out the resulting Decode literal, length, and distance codes and write out the resulting
literal and match bytes until either not enough input or output is literal and match bytes until either not enough input or output is
...@@ -115,9 +94,9 @@ void inflate_fast(z_streamp strm, unsigned start) ...@@ -115,9 +94,9 @@ void inflate_fast(z_streamp strm, unsigned start)
/* copy state to local variables */ /* copy state to local variables */
state = (struct inflate_state *)strm->state; state = (struct inflate_state *)strm->state;
in = strm->next_in - OFF; in = strm->next_in;
last = in + (strm->avail_in - 5); last = in + (strm->avail_in - 5);
out = strm->next_out - OFF; out = strm->next_out;
beg = out - (start - strm->avail_out); beg = out - (start - strm->avail_out);
end = out + (strm->avail_out - 257); end = out + (strm->avail_out - 257);
#ifdef INFLATE_STRICT #ifdef INFLATE_STRICT
...@@ -138,9 +117,9 @@ void inflate_fast(z_streamp strm, unsigned start) ...@@ -138,9 +117,9 @@ void inflate_fast(z_streamp strm, unsigned start)
input data or output space */ input data or output space */
do { do {
if (bits < 15) { if (bits < 15) {
hold += (unsigned long)(PUP(in)) << bits; hold += (unsigned long)(*in++) << bits;
bits += 8; bits += 8;
hold += (unsigned long)(PUP(in)) << bits; hold += (unsigned long)(*in++) << bits;
bits += 8; bits += 8;
} }
this = lcode[hold & lmask]; this = lcode[hold & lmask];
...@@ -150,14 +129,14 @@ void inflate_fast(z_streamp strm, unsigned start) ...@@ -150,14 +129,14 @@ void inflate_fast(z_streamp strm, unsigned start)
bits -= op; bits -= op;
op = (unsigned)(this.op); op = (unsigned)(this.op);
if (op == 0) { /* literal */ if (op == 0) { /* literal */
PUP(out) = (unsigned char)(this.val); *out++ = (unsigned char)(this.val);
} }
else if (op & 16) { /* length base */ else if (op & 16) { /* length base */
len = (unsigned)(this.val); len = (unsigned)(this.val);
op &= 15; /* number of extra bits */ op &= 15; /* number of extra bits */
if (op) { if (op) {
if (bits < op) { if (bits < op) {
hold += (unsigned long)(PUP(in)) << bits; hold += (unsigned long)(*in++) << bits;
bits += 8; bits += 8;
} }
len += (unsigned)hold & ((1U << op) - 1); len += (unsigned)hold & ((1U << op) - 1);
...@@ -165,9 +144,9 @@ void inflate_fast(z_streamp strm, unsigned start) ...@@ -165,9 +144,9 @@ void inflate_fast(z_streamp strm, unsigned start)
bits -= op; bits -= op;
} }
if (bits < 15) { if (bits < 15) {
hold += (unsigned long)(PUP(in)) << bits; hold += (unsigned long)(*in++) << bits;
bits += 8; bits += 8;
hold += (unsigned long)(PUP(in)) << bits; hold += (unsigned long)(*in++) << bits;
bits += 8; bits += 8;
} }
this = dcode[hold & dmask]; this = dcode[hold & dmask];
...@@ -180,10 +159,10 @@ void inflate_fast(z_streamp strm, unsigned start) ...@@ -180,10 +159,10 @@ void inflate_fast(z_streamp strm, unsigned start)
dist = (unsigned)(this.val); dist = (unsigned)(this.val);
op &= 15; /* number of extra bits */ op &= 15; /* number of extra bits */
if (bits < op) { if (bits < op) {
hold += (unsigned long)(PUP(in)) << bits; hold += (unsigned long)(*in++) << bits;
bits += 8; bits += 8;
if (bits < op) { if (bits < op) {
hold += (unsigned long)(PUP(in)) << bits; hold += (unsigned long)(*in++) << bits;
bits += 8; bits += 8;
} }
} }
...@@ -205,13 +184,13 @@ void inflate_fast(z_streamp strm, unsigned start) ...@@ -205,13 +184,13 @@ void inflate_fast(z_streamp strm, unsigned start)
state->mode = BAD; state->mode = BAD;
break; break;
} }
from = window - OFF; from = window;
if (write == 0) { /* very common case */ if (write == 0) { /* very common case */
from += wsize - op; from += wsize - op;
if (op < len) { /* some from window */ if (op < len) { /* some from window */
len -= op; len -= op;
do { do {
PUP(out) = PUP(from); *out++ = *from++;
} while (--op); } while (--op);
from = out - dist; /* rest from output */ from = out - dist; /* rest from output */
} }
...@@ -222,14 +201,14 @@ void inflate_fast(z_streamp strm, unsigned start) ...@@ -222,14 +201,14 @@ void inflate_fast(z_streamp strm, unsigned start)
if (op < len) { /* some from end of window */ if (op < len) { /* some from end of window */
len -= op; len -= op;
do { do {
PUP(out) = PUP(from); *out++ = *from++;
} while (--op); } while (--op);
from = window - OFF; from = window;
if (write < len) { /* some from start of window */ if (write < len) { /* some from start of window */
op = write; op = write;
len -= op; len -= op;
do { do {
PUP(out) = PUP(from); *out++ = *from++;
} while (--op); } while (--op);
from = out - dist; /* rest from output */ from = out - dist; /* rest from output */
} }
...@@ -240,21 +219,21 @@ void inflate_fast(z_streamp strm, unsigned start) ...@@ -240,21 +219,21 @@ void inflate_fast(z_streamp strm, unsigned start)
if (op < len) { /* some from window */ if (op < len) { /* some from window */
len -= op; len -= op;
do { do {
PUP(out) = PUP(from); *out++ = *from++;
} while (--op); } while (--op);
from = out - dist; /* rest from output */ from = out - dist; /* rest from output */
} }
} }
while (len > 2) { while (len > 2) {
PUP(out) = PUP(from); *out++ = *from++;
PUP(out) = PUP(from); *out++ = *from++;
PUP(out) = PUP(from); *out++ = *from++;
len -= 3; len -= 3;
} }
if (len) { if (len) {
PUP(out) = PUP(from); *out++ = *from++;
if (len > 1) if (len > 1)
PUP(out) = PUP(from); *out++ = *from++;
} }
} }
else { else {
...@@ -264,29 +243,29 @@ void inflate_fast(z_streamp strm, unsigned start) ...@@ -264,29 +243,29 @@ void inflate_fast(z_streamp strm, unsigned start)
from = out - dist; /* copy direct from output */ from = out - dist; /* copy direct from output */
/* minimum length is three */ /* minimum length is three */
/* Align out addr */ /* Align out addr */
if (!((long)(out - 1 + OFF) & 1)) { if (!((long)(out - 1) & 1)) {
PUP(out) = PUP(from); *out++ = *from++;
len--; len--;
} }
sout = (unsigned short *)(out - OFF); sout = (unsigned short *)(out);
if (dist > 2) { if (dist > 2) {
unsigned short *sfrom; unsigned short *sfrom;
sfrom = (unsigned short *)(from - OFF); sfrom = (unsigned short *)(from);
loops = len >> 1; loops = len >> 1;
do do
#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
PUP(sout) = PUP(sfrom); *sout++ = *sfrom++;
#else #else
PUP(sout) = UP_UNALIGNED(sfrom); *sout++ = get_unaligned16(sfrom++);
#endif #endif
while (--loops); while (--loops);
out = (unsigned char *)sout + OFF; out = (unsigned char *)sout;
from = (unsigned char *)sfrom + OFF; from = (unsigned char *)sfrom;
} else { /* dist == 1 or dist == 2 */ } else { /* dist == 1 or dist == 2 */
unsigned short pat16; unsigned short pat16;
pat16 = *(sout-1+OFF); pat16 = *(sout-1);
if (dist == 1) { if (dist == 1) {
union uu mm; union uu mm;
/* copy one char pattern to both bytes */ /* copy one char pattern to both bytes */
...@@ -296,12 +275,12 @@ void inflate_fast(z_streamp strm, unsigned start) ...@@ -296,12 +275,12 @@ void inflate_fast(z_streamp strm, unsigned start)
} }
loops = len >> 1; loops = len >> 1;
do do
PUP(sout) = pat16; *sout++ = pat16;
while (--loops); while (--loops);
out = (unsigned char *)sout + OFF; out = (unsigned char *)sout;
} }
if (len & 1) if (len & 1)
PUP(out) = PUP(from); *out++ = *from++;
} }
} }
else if ((op & 64) == 0) { /* 2nd level distance code */ else if ((op & 64) == 0) { /* 2nd level distance code */
...@@ -336,8 +315,8 @@ void inflate_fast(z_streamp strm, unsigned start) ...@@ -336,8 +315,8 @@ void inflate_fast(z_streamp strm, unsigned start)
hold &= (1U << bits) - 1; hold &= (1U << bits) - 1;
/* update state and return */ /* update state and return */
strm->next_in = in + OFF; strm->next_in = in;
strm->next_out = out + OFF; strm->next_out = out;
strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
strm->avail_out = (unsigned)(out < end ? strm->avail_out = (unsigned)(out < end ?
257 + (end - out) : 257 - (out - end)); 257 + (end - out) : 257 - (out - end));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment