From 37f1668425f0af931ca867774d31401a314f7072 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Wed, 20 Apr 2022 09:59:54 -0400 Subject: [PATCH] Security fix for CVE-2022-28041 (fix RHBZ#2077020, fix RBHZ#2077019) Backports PR#1297 from upstream. --- 1297.patch | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++ stb.spec | 16 ++++ 2 files changed, 260 insertions(+) create mode 100644 1297.patch diff --git a/1297.patch b/1297.patch new file mode 100644 index 0000000..9fe840c --- /dev/null +++ b/1297.patch @@ -0,0 +1,244 @@ +From fa43122a169eb79ced5789f2f261cee7fd4db221 Mon Sep 17 00:00:00 2001 +From: Neil Bickford +Date: Tue, 22 Feb 2022 23:48:42 -0800 +Subject: [PATCH 1/4] Add checks for PNM integer read overflows, add a 1GB + limit on IDAT chunk sizes to fix an OOM issue, and check for a situation + where a sequence of bad Huffman code reads could result in a left shift by a + negative number. + +--- + stb_image.h | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/stb_image.h b/stb_image.h +index d60371b95..6321f5e02 100644 +--- a/stb_image.h ++++ b/stb_image.h +@@ -2283,6 +2283,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; ++ if (s > j->code_bits) return stbi__err("bad huffman code","Combined length longer than code bits available"); + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); +@@ -5116,6 +5117,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } ++ if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; +@@ -7486,6 +7488,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c) + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); ++ if((value > 214748364) || (value == 214748364 && *c > '7')) ++ return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); + } + + return value; +@@ -7516,9 +7520,13 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width ++ if(*x == 0) ++ return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height ++ if (*y == 0) ++ return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + +From 83739b31eeddaaf683948051661ece39af6795cd Mon Sep 17 00:00:00 2001 +From: Neil Bickford +Date: Wed, 23 Feb 2022 00:53:34 -0800 +Subject: [PATCH 2/4] Add range checks to fix a few crash issues in stb_image + issues 1289 and 1291 + +--- + stb_image.h | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/stb_image.h b/stb_image.h +index 6321f5e02..800c83db3 100644 +--- a/stb_image.h ++++ b/stb_image.h +@@ -1985,9 +1985,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) +- for (i=0; i < 16; ++i) +- for (j=0; j < count[i]; ++j) ++ for (i=0; i < 16; ++i) { ++ for (j=0; j < count[i]; ++j) { + h->size[k++] = (stbi_uc) (i+1); ++ if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); ++ } ++ } + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) +@@ -2112,6 +2115,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; ++ if(c < 0 || c >= 256) // symbol id out of bounds! ++ return -1; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol +@@ -3103,6 +3108,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m) + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } ++ if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + +From 2cdd738fd112e11bec8d7b2ee96449741a203ee2 Mon Sep 17 00:00:00 2001 +From: Neil Bickford +Date: Wed, 23 Feb 2022 23:48:49 -0800 +Subject: [PATCH 3/4] Add checks for signed integer overflow; further guard + against cases where stbi__grow_buffer_unsafe doesn't read all bits required. + +--- + stb_image.h | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/stb_image.h b/stb_image.h +index 800c83db3..9d10099bb 100644 +--- a/stb_image.h ++++ b/stb_image.h +@@ -1063,6 +1063,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) + } + #endif + ++// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. ++static int stbi__addints_valid(int a, int b) ++{ ++ if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow ++ if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. ++ return a <= INT_MAX - b; ++} ++ ++// returns 1 if the product of two signed shorts is valid, 0 on overflow. ++static int stbi__mul2shorts_valid(short a, short b) ++{ ++ if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow ++ if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid ++ if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN ++ return a >= SHRT_MIN / b; ++} ++ + // stbi__err - error + // stbi__errpf - error returning pointer to float + // stbi__errpuc - error returning pointer to unsigned char +@@ -2135,6 +2152,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) + unsigned int k; + int sgn; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); ++ if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); +@@ -2149,6 +2167,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) + { + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); ++ if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; +@@ -2160,6 +2179,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) + { + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); ++ if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; +@@ -2197,8 +2217,10 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; ++ if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; ++ if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec +@@ -2212,6 +2234,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length ++ if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location +@@ -2251,8 +2274,10 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + ++ if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; ++ if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient +@@ -2287,8 +2312,8 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length ++ if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; +- if (s > j->code_bits) return stbi__err("bad huffman code","Combined length longer than code bits available"); + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + +From 51e438b04b50eb98540f6df6057004214e9cc81c Mon Sep 17 00:00:00 2001 +From: Neil Bickford +Date: Fri, 25 Feb 2022 14:27:31 -0800 +Subject: [PATCH 4/4] Zero-initialize stbi__jpeg to avoid intermittent errors + found by fuzz-testing + +--- + stb_image.h | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/stb_image.h b/stb_image.h +index 9d10099bb..631e4e51c 100644 +--- a/stb_image.h ++++ b/stb_image.h +@@ -4008,6 +4008,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); ++ memset(j, 0, sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); +@@ -4021,6 +4022,7 @@ static int stbi__jpeg_test(stbi__context *s) + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); ++ memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); +@@ -4046,6 +4048,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); ++ memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); diff --git a/stb.spec b/stb.spec index f96f655..1aeab08 100644 --- a/stb.spec +++ b/stb.spec @@ -89,6 +89,22 @@ Patch: %{url}/pull/1223.patch # patch shows it to be correct. Patch: %{url}/pull/1236.patch +# Candidate fix for: +# https://nvd.nist.gov/vuln/detail/CVE-2022-28041 +# +# stb_image.h v2.27 was discovered to contain an integer overflow via the +# function stbi__jpeg_decode_block_prog_dc. This vulnerability allows attackers +# to cause a Denial of Service (DoS) via unspecified vectors. +# +# UBSAN: integer overflow +# https://github.com/nothings/stb/issues/1292 +# +# ---- +# +# Additional stb_image fixes for bugs from ossfuzz and issues 1289, 1291, 1292, and 1293 +# https://github.com/nothings/stb/pull/1297 +Patch: %{url}/pull/1297.patch + %global stb_c_lexer_version 0.12 %global stb_connected_components_version 0.96 %global stb_divide_version 0.94