diff -Nru busybox-1.27.2/debian/changelog busybox-1.27.2/debian/changelog --- busybox-1.27.2/debian/changelog 2020-09-18 14:26:16.000000000 +0000 +++ busybox-1.27.2/debian/changelog 2021-11-24 19:05:22.000000000 +0000 @@ -1,3 +1,21 @@ +busybox (1:1.27.2-2ubuntu3.4) bionic-security; urgency=medium + + * SECURITY UPDATE: invalid free or segfault via gzip data + - debian/patches/CVE-2021-28831.patch: fix DoS if gzip is corrupt in + archival/libarchive/decompress_gunzip.c. + - CVE-2021-28831 + * SECURITY UPDATE: OOB read in unlzma + - debian/patches/CVE-2021-42374.patch: fix a case where we could read + before beginning of buffer in archival/libarchive/decompress_unlzma.c. + - CVE-2021-42374 + * SECURITY UPDATE: multiple security issues in awk + - debian/patches/CVE-2021-423xx-awk.patch: backport awk.c from + busybox 1.34.1. + - CVE-2021-42378, CVE-2021-42379, CVE-2021-42380, CVE-2021-42381, + CVE-2021-42382, CVE-2021-42384, CVE-2021-42385, CVE-2021-42386 + + -- Marc Deslauriers Wed, 24 Nov 2021 14:05:22 -0500 + busybox (1:1.27.2-2ubuntu3.3) bionic-security; urgency=medium * SECURITY UPDATE: missing ssl cert validation in wget applet diff -Nru busybox-1.27.2/debian/patches/CVE-2021-28831.patch busybox-1.27.2/debian/patches/CVE-2021-28831.patch --- busybox-1.27.2/debian/patches/CVE-2021-28831.patch 1970-01-01 00:00:00.000000000 +0000 +++ busybox-1.27.2/debian/patches/CVE-2021-28831.patch 2021-11-24 19:03:21.000000000 +0000 @@ -0,0 +1,45 @@ +Backport of: + +From f25d254dfd4243698c31a4f3153d4ac72aa9e9bd Mon Sep 17 00:00:00 2001 +From: Samuel Sapalski +Date: Wed, 3 Mar 2021 16:31:22 +0100 +Subject: decompress_gunzip: Fix DoS if gzip is corrupt + +On certain corrupt gzip files, huft_build will set the error bit on +the result pointer. If afterwards abort_unzip is called huft_free +might run into a segmentation fault or an invalid pointer to +free(p). + +In order to mitigate this, we check in huft_free if the error bit +is set and clear it before the linked list is freed. + +Signed-off-by: Samuel Sapalski +Signed-off-by: Peter Kaestle +Signed-off-by: Denys Vlasenko +--- + archival/libarchive/decompress_gunzip.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +--- a/archival/libarchive/decompress_gunzip.c ++++ b/archival/libarchive/decompress_gunzip.c +@@ -224,10 +224,20 @@ static const uint8_t border[] ALIGN1 = { + * each table. + * t: table to free + */ ++#define BAD_HUFT(p) ((uintptr_t)(p) & 1) ++#define ERR_RET ((huft_t*)(uintptr_t)1) + static void huft_free(huft_t *p) + { + huft_t *q; + ++ /* ++ * If 'p' has the error bit set we have to clear it, otherwise we might run ++ * into a segmentation fault or an invalid pointer to free(p) ++ */ ++ if (BAD_HUFT(p)) { ++ p = (huft_t*)((uintptr_t)(p) ^ (uintptr_t)(ERR_RET)); ++ } ++ + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + while (p) { + q = (--p)->v.t; diff -Nru busybox-1.27.2/debian/patches/CVE-2021-42374.patch busybox-1.27.2/debian/patches/CVE-2021-42374.patch --- busybox-1.27.2/debian/patches/CVE-2021-42374.patch 1970-01-01 00:00:00.000000000 +0000 +++ busybox-1.27.2/debian/patches/CVE-2021-42374.patch 2021-11-24 19:03:35.000000000 +0000 @@ -0,0 +1,41 @@ +Backport of: + +From 04f052c56ded5ab6a904e3a264a73dc0412b2e78 Mon Sep 17 00:00:00 2001 +From: Denys Vlasenko +Date: Tue, 15 Jun 2021 15:07:57 +0200 +Subject: unlzma: fix a case where we could read before beginning of buffer + +Testcase: + + 21 01 01 00 00 00 00 00 e7 01 01 01 ef 00 df b6 + 00 17 02 10 11 0f ff 00 16 00 00 + +Unfortunately, the bug is not reliably causing a segfault, +the behavior depends on what's in memory before the buffer. + +function old new delta +unpack_lzma_stream 2762 2768 +6 + +Signed-off-by: Denys Vlasenko +--- + archival/libarchive/decompress_unlzma.c | 5 ++++- + testsuite/unlzma.tests | 17 +++++++++++++---- + testsuite/unlzma_issue_3.lzma | Bin 0 -> 27 bytes + 3 files changed, 17 insertions(+), 5 deletions(-) + create mode 100644 testsuite/unlzma_issue_3.lzma + +--- a/archival/libarchive/decompress_unlzma.c ++++ b/archival/libarchive/decompress_unlzma.c +@@ -281,8 +281,11 @@ unpack_lzma_stream(transformer_state_t * + uint32_t pos; + + pos = buffer_pos - rep0; +- if ((int32_t)pos < 0) ++ if ((int32_t)pos < 0) { + pos += header.dict_size; ++ if ((int32_t)pos < 0) ++ goto bad; ++ } + match_byte = buffer[pos]; + do { + int bit; diff -Nru busybox-1.27.2/debian/patches/CVE-2021-423xx-awk.patch busybox-1.27.2/debian/patches/CVE-2021-423xx-awk.patch --- busybox-1.27.2/debian/patches/CVE-2021-423xx-awk.patch 1970-01-01 00:00:00.000000000 +0000 +++ busybox-1.27.2/debian/patches/CVE-2021-423xx-awk.patch 2021-11-24 19:05:14.000000000 +0000 @@ -0,0 +1,495 @@ +Description: fix multiple security issues in awk +Origin: backported awk.c from busybox 1.34.1 + +--- a/editors/awk.c ++++ b/editors/awk.c +@@ -6,32 +6,30 @@ + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +- + //config:config AWK +-//config: bool "awk" ++//config: bool "awk (23 kb)" + //config: default y + //config: help +-//config: Awk is used as a pattern scanning and processing language. This is +-//config: the BusyBox implementation of that programming language. ++//config: Awk is used as a pattern scanning and processing language. + //config: + //config:config FEATURE_AWK_LIBM + //config: bool "Enable math functions (requires libm)" + //config: default y + //config: depends on AWK + //config: help +-//config: Enable math functions of the Awk programming language. +-//config: NOTE: This will require libm to be present for linking. ++//config: Enable math functions of the Awk programming language. ++//config: NOTE: This requires libm to be present for linking. + //config: + //config:config FEATURE_AWK_GNU_EXTENSIONS + //config: bool "Enable a few GNU extensions" + //config: default y + //config: depends on AWK + //config: help +-//config: Enable a few features from gawk: +-//config: * command line option -e AWK_PROGRAM +-//config: * simultaneous use of -f and -e on the command line. +-//config: This enables the use of awk library files. +-//config: Ex: awk -f mylib.awk -e '{print myfunction($1);}' ... ++//config: Enable a few features from gawk: ++//config: * command line option -e AWK_PROGRAM ++//config: * simultaneous use of -f and -e on the command line. ++//config: This enables the use of awk library files. ++//config: Example: awk -f mylib.awk -e '{print myfunction($1);}' ... + + //applet:IF_AWK(APPLET_NOEXEC(awk, awk, BB_DIR_USR_BIN, BB_SUID_DROP, awk)) + +@@ -71,7 +69,11 @@ + #endif + + +-#define OPTSTR_AWK \ ++/* "+": stop on first non-option: ++ * $ awk 'BEGIN { for(i=1; iinfo) && ((t_info & OPCLSMASK) == OC_COLON)) + ) { + vn = vn->a.n; ++ if (!vn->a.n) syntax_error(EMSG_UNEXP_TOKEN); + } + if ((t_info & OPCLSMASK) == OC_TERNARY) + t_info += P(6); +@@ -1343,8 +1359,10 @@ static node *parse_expr(uint32_t iexp) + v = cn->l.v = xzalloc(sizeof(var)); + if (tc & TC_NUMBER) + setvar_i(v, t_double); +- else ++ else { + setvar_s(v, t_string); ++ xtc &= ~TC_UOPPOST; /* "str"++ is not allowed */ ++ } + break; + + case TC_REGEXP: +@@ -1380,7 +1398,12 @@ static node *parse_expr(uint32_t iexp) + + case TC_LENGTH: + debug_printf_parse("%s: TC_LENGTH\n", __func__); +- next_token(TC_SEQSTART | TC_OPTERM | TC_GRPTERM); ++ next_token(TC_SEQSTART /* length(...) */ ++ | TC_OPTERM /* length; (or newline)*/ ++ | TC_GRPTERM /* length } */ ++ | TC_BINOPX /* length NUM */ ++ | TC_COMMA /* print length, 1 */ ++ ); + rollback_token(); + if (t_tclass & TC_SEQSTART) { + /* It was a "(" token. Handle just like TC_BUILTIN */ +@@ -1422,7 +1445,11 @@ static void chain_expr(uint32_t info) + node *n; + + n = chain_node(info); ++ + n->l.n = parse_expr(TC_OPTERM | TC_GRPTERM); ++ if ((info & OF_REQUIRED) && !n->l.n) ++ syntax_error(EMSG_TOO_FEW_ARGS); ++ + if (t_tclass & TC_GRPTERM) + rollback_token(); + } +@@ -1602,12 +1629,25 @@ static void parse_program(char *p) + f = newfunc(t_string); + f->body.first = NULL; + f->nargs = 0; +- while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) { ++ /* Match func arg list: a comma sep list of >= 0 args, and a close paren */ ++ while (next_token(TC_VARIABLE | TC_SEQTERM | TC_COMMA)) { ++ /* Either an empty arg list, or trailing comma from prev iter ++ * must be followed by an arg */ ++ if (f->nargs == 0 && t_tclass == TC_SEQTERM) ++ break; ++ ++ /* TC_SEQSTART/TC_COMMA must be followed by TC_VARIABLE */ ++ if (t_tclass != TC_VARIABLE) ++ syntax_error(EMSG_UNEXP_TOKEN); ++ + v = findvar(ahash, t_string); + v->x.aidx = f->nargs++; + ++ /* Arg followed either by end of arg list or 1 comma */ + if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM) + break; ++ if (t_tclass != TC_COMMA) ++ syntax_error(EMSG_UNEXP_TOKEN); + } + seq = &f->body; + chain_group(); +@@ -1723,12 +1763,34 @@ static void fsrealloc(int size) + nfields = size; + } + ++static int regexec1_nonempty(const regex_t *preg, const char *s, regmatch_t pmatch[]) ++{ ++ int r = regexec(preg, s, 1, pmatch, 0); ++ if (r == 0 && pmatch[0].rm_eo == 0) { ++ /* For example, happens when FS can match ++ * an empty string (awk -F ' *'). Logically, ++ * this should split into one-char fields. ++ * However, gawk 5.0.1 searches for first ++ * _non-empty_ separator string match: ++ */ ++ size_t ofs = 0; ++ do { ++ ofs++; ++ if (!s[ofs]) ++ return REG_NOMATCH; ++ regexec(preg, s + ofs, 1, pmatch, 0); ++ } while (pmatch[0].rm_eo == 0); ++ pmatch[0].rm_so += ofs; ++ pmatch[0].rm_eo += ofs; ++ } ++ return r; ++} ++ + static int awk_split(const char *s, node *spl, char **slist) + { +- int l, n; ++ int n; + char c[4]; + char *s1; +- regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough... + + /* in worst case, each char would be a separate field */ + *slist = s1 = xzalloc(strlen(s) * 2 + 3); +@@ -1745,29 +1807,31 @@ static int awk_split(const char *s, node + return n; /* "": zero fields */ + n++; /* at least one field will be there */ + do { ++ int l; ++ regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough... ++ + l = strcspn(s, c+2); /* len till next NUL or \n */ +- if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0 ++ if (regexec1_nonempty(icase ? spl->r.ire : spl->l.re, s, pmatch) == 0 + && pmatch[0].rm_so <= l + ) { ++ /* if (pmatch[0].rm_eo == 0) ... - impossible */ + l = pmatch[0].rm_so; +- if (pmatch[0].rm_eo == 0) { +- l++; +- pmatch[0].rm_eo++; +- } + n++; /* we saw yet another delimiter */ + } else { + pmatch[0].rm_eo = l; + if (s[l]) + pmatch[0].rm_eo++; + } +- memcpy(s1, s, l); +- /* make sure we remove *all* of the separator chars */ +- do { +- s1[l] = '\0'; +- } while (++l < pmatch[0].rm_eo); +- nextword(&s1); ++ s1 = mempcpy(s1, s, l); ++ *s1++ = '\0'; + s += pmatch[0].rm_eo; + } while (*s); ++ ++ /* echo a-- | awk -F-- '{ print NF, length($NF), $NF }' ++ * should print "2 0 ": ++ */ ++ *s1 = '\0'; ++ + return n; + } + if (c[0] == '\0') { /* null split */ +@@ -1846,6 +1910,8 @@ static void handle_special(var *v) + + if (v == intvar[NF]) { + n = (int)getvar_i(v); ++ if (n < 0) ++ syntax_error("NF set to negative value"); + fsrealloc(n); + + /* recalculate $0 */ +@@ -1969,7 +2035,7 @@ static int ptest(node *pattern) + static int awk_getline(rstream *rsm, var *v) + { + char *b; +- regmatch_t pmatch[2]; ++ regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough... + int size, a, p, pp = 0; + int fd, so, eo, r, rp; + char c, *m, *s; +@@ -2512,6 +2578,32 @@ static var *evaluate(node *op, var *res) + op1 = op->l.n; + debug_printf_eval("opinfo:%08x opn:%08x\n", opinfo, opn); + ++ /* "delete" is special: ++ * "delete array[var--]" must evaluate index expr only once, ++ * must not evaluate it in "execute inevitable things" part. ++ */ ++ if (XC(opinfo & OPCLSMASK) == XC(OC_DELETE)) { ++ uint32_t info = op1->info & OPCLSMASK; ++ var *v; ++ ++ debug_printf_eval("DELETE\n"); ++ if (info == OC_VAR) { ++ v = op1->l.v; ++ } else if (info == OC_FNARG) { ++ v = &fnargs[op1->l.aidx]; ++ } else { ++ syntax_error(EMSG_NOT_ARRAY); ++ } ++ if (op1->r.n) { /* array ref? */ ++ const char *s; ++ s = getvar_s(evaluate(op1->r.n, v1)); ++ hash_remove(iamarray(v), s); ++ } else { ++ clear_array(iamarray(v)); ++ } ++ goto next; ++ } ++ + /* execute inevitable things */ + if (opinfo & OF_RES1) + L.v = evaluate(op1, v1); +@@ -2619,28 +2711,7 @@ static var *evaluate(node *op, var *res) + break; + } + +- case XC( OC_DELETE ): { +- uint32_t info = op1->info & OPCLSMASK; +- var *v; +- +- if (info == OC_VAR) { +- v = op1->l.v; +- } else if (info == OC_FNARG) { +- v = &fnargs[op1->l.aidx]; +- } else { +- syntax_error(EMSG_NOT_ARRAY); +- } +- +- if (op1->r.n) { +- const char *s; +- clrvar(L.v); +- s = getvar_s(evaluate(op1->r.n, v1)); +- hash_remove(iamarray(v), s); +- } else { +- clear_array(iamarray(v)); +- } +- break; +- } ++ /* case XC( OC_DELETE ): - moved to happen before arg evaluation */ + + case XC( OC_NEWSOURCE ): + g_progname = op->l.new_progname; +@@ -2664,12 +2735,14 @@ static var *evaluate(node *op, var *res) + /* -- recursive node type -- */ + + case XC( OC_VAR ): ++ debug_printf_eval("VAR\n"); + L.v = op->l.v; + if (L.v == intvar[NF]) + split_f0(); + goto v_cont; + + case XC( OC_FNARG ): ++ debug_printf_eval("FNARG[%d]\n", op->l.aidx); + L.v = &fnargs[op->l.aidx]; + v_cont: + res = op->r.n ? findvar(iamarray(L.v), R.s) : L.v; +@@ -2940,6 +3013,8 @@ static var *evaluate(node *op, var *res) + + case XC( OC_FIELD ): { + int i = (int)getvar_i(R.v); ++ if (i < 0) ++ syntax_error(EMSG_NEGATIVE_FIELD); + if (i == 0) { + res = intvar[F0]; + } else { +@@ -3033,7 +3108,8 @@ static var *evaluate(node *op, var *res) + + default: + syntax_error(EMSG_POSSIBLE_ERROR); +- } ++ } /* switch */ ++ next: + if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS) + op = op->a.n; + if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS) +@@ -3139,7 +3215,7 @@ static rstream *next_input_file(void) + } + + int awk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +-int awk_main(int argc, char **argv) ++int awk_main(int argc UNUSED_PARAM, char **argv) + { + unsigned opt; + char *opt_F; +@@ -3208,7 +3284,7 @@ int awk_main(int argc, char **argv) + } + opt = getopt32(argv, OPTSTR_AWK, &opt_F, &list_v, &list_f, IF_FEATURE_AWK_GNU_EXTENSIONS(&list_e,) NULL); + argv += optind; +- argc -= optind; ++ //argc -= optind; + if (opt & OPT_W) + bb_error_msg("warning: option -W is ignored"); + if (opt & OPT_F) { +@@ -3245,15 +3321,14 @@ int awk_main(int argc, char **argv) + if (!*argv) + bb_show_usage(); + parse_program(*argv++); +- argc--; + } + + /* fill in ARGV array */ +- setvar_i(intvar[ARGC], argc + 1); + setari_u(intvar[ARGV], 0, "awk"); + i = 0; + while (*argv) + setari_u(intvar[ARGV], ++i, *argv++); ++ setvar_i(intvar[ARGC], i + 1); + + evaluate(beginseq.first, &tv); + if (!mainseq.first && !endseq.first) diff -Nru busybox-1.27.2/debian/patches/series busybox-1.27.2/debian/patches/series --- busybox-1.27.2/debian/patches/series 2020-09-18 14:26:10.000000000 +0000 +++ busybox-1.27.2/debian/patches/series 2021-11-24 19:03:51.000000000 +0000 @@ -31,3 +31,6 @@ CVE-2018-1000500-pre2.patch CVE-2018-1000500-1.patch CVE-2018-1000500-2.patch +CVE-2021-28831.patch +CVE-2021-42374.patch +CVE-2021-423xx-awk.patch