diff --git a/CVE-2019-9192-add-backtrack-limit-to-avoid-infinite-loop.patch b/CVE-2019-9192-add-backtrack-limit-to-avoid-infinite-loop.patch new file mode 100644 index 0000000000000000000000000000000000000000..8388627d602f7fe3965f1ca6aa3cfac3a1f1777c --- /dev/null +++ b/CVE-2019-9192-add-backtrack-limit-to-avoid-infinite-loop.patch @@ -0,0 +1,372 @@ +From 9da19e62daede18e280b405600c56b32b6e82c62 Mon Sep 17 00:00:00 2001 +From: liqingqing +Date: Mon, 22 Jul 2019 03:10:28 -0400 +Subject: [PATCH] add backtrack limit to avoid infinite loop + +Signed-off-by: liqingqing +--- + posix/regcomp.c | 61 ++++++++++++++++++++++++++++++++++++++++--- + posix/regexec.c | 69 +++++++++++++++++++++++++++++++++++++------------ + 2 files changed, 109 insertions(+), 21 deletions(-) + +diff --git a/posix/regcomp.c b/posix/regcomp.c +index 90a2ab9f..ef4c6e2a 100644 +--- a/posix/regcomp.c ++++ b/posix/regcomp.c +@@ -21,6 +21,9 @@ + # include + #endif + ++#define DEFAULT_BKTRACK_LIMIT 10000 ++static unsigned int bktrack_limit; ++ + static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern, + size_t length, reg_syntax_t syntax); + static void re_compile_fastmap_iter (regex_t *bufp, +@@ -54,7 +57,8 @@ static Idx search_duplicated_node (const re_dfa_t *dfa, Idx org_node, + unsigned int constraint); + static reg_errcode_t calc_eclosure (re_dfa_t *dfa); + static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, +- Idx node, bool root); ++ Idx node, bool root, ++ unsigned int *bktrack_cnt); + static reg_errcode_t calc_inveclosure (re_dfa_t *dfa); + static Idx fetch_number (re_string_t *input, re_token_t *token, + reg_syntax_t syntax); +@@ -203,6 +207,49 @@ static const size_t __re_error_msgid_idx[] = + REG_ESIZE_IDX, + REG_ERPAREN_IDX + }; ++ ++/* finish the initial of bktrack_limit before main start, ++ * and regcomp/regexec just use the value. ++ */ ++static void __attribute__ ((constructor)) init_bktrack_limit(void) ++{ ++ int value; ++ char *limit; ++ ++ limit = getenv("GLIBC_REGEXEC_BACKTRACK_LIMIT"); ++ if ((limit == NULL) || (limit[0] == '\0')) ++ { ++ bktrack_limit = DEFAULT_BKTRACK_LIMIT; ++ return; ++ } ++ ++ /* the user confirm the input string */ ++ value = atoi(limit); ++ if (value > 0) ++ bktrack_limit = value; ++ else if (value == 0) ++ bktrack_limit = 0xffffffff; ++ else ++ bktrack_limit = DEFAULT_BKTRACK_LIMIT; ++ ++ return; ++} ++ ++/* just do increment when we in the begining of the backtrack function, ++ * and check if we have excessed the bktrack_limit ++ */ ++bool chk_bktrack_exhaust(unsigned int *cnt) ++{ ++ unsigned int limit = bktrack_limit; ++ ++ if (*cnt <= limit) ++ { ++ (*cnt)++; ++ return false; ++ } ++ ++ return true; ++} + + /* Entry points for GNU code. */ + +@@ -1664,6 +1711,7 @@ static reg_errcode_t + calc_eclosure (re_dfa_t *dfa) + { + Idx node_idx; ++ unsigned int bktrack_cnt = 0; + bool incomplete; + #ifdef DEBUG + assert (dfa->nodes_len > 0); +@@ -1690,7 +1738,7 @@ calc_eclosure (re_dfa_t *dfa) + if (dfa->eclosures[node_idx].nelem != 0) + continue; + /* Calculate epsilon closure of 'node_idx'. */ +- err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, true); ++ err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, true, &bktrack_cnt); + if (__glibc_unlikely (err != REG_NOERROR)) + return err; + +@@ -1706,13 +1754,18 @@ calc_eclosure (re_dfa_t *dfa) + /* Calculate epsilon closure of NODE. */ + + static reg_errcode_t +-calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, Idx node, bool root) ++calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, Idx node, bool root, ++ unsigned int *bktrack_cnt) + { + reg_errcode_t err; + Idx i; + re_node_set eclosure; + bool ok; + bool incomplete = false; ++ ++ if (__glibc_unlikely (chk_bktrack_exhaust(bktrack_cnt))) ++ return REG_ESPACE; ++ + err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1); + if (__glibc_unlikely (err != REG_NOERROR)) + return err; +@@ -1750,7 +1803,7 @@ calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, Idx node, bool root) + calculate now. Otherwise use calculated epsilon closure. */ + if (dfa->eclosures[edest].nelem == 0) + { +- err = calc_eclosure_iter (&eclosure_elem, dfa, edest, false); ++ err = calc_eclosure_iter (&eclosure_elem, dfa, edest, false, bktrack_cnt); + if (__glibc_unlikely (err != REG_NOERROR)) + return err; + } +diff --git a/posix/regexec.c b/posix/regexec.c +index ff6ab120..024a9fe9 100644 +--- a/posix/regexec.c ++++ b/posix/regexec.c +@@ -17,6 +17,12 @@ + License along with the GNU C Library; if not, see + . */ + ++#define DST_LIMIT_BAD_POS 0x5a ++ ++/* this is not a godd way to include the chk_bktrack_exhaust function directly. ++ * it's only a temporary solution. ++ */ ++extern bool chk_bktrack_exhaust(unsigned int *cnt); + static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags, + Idx n); + static void match_ctx_clean (re_match_context_t *mctx); +@@ -73,14 +79,16 @@ static int sift_states_iter_mb (const re_match_context_t *mctx, + Idx node_idx, Idx str_idx, Idx max_str_idx); + #endif /* RE_ENABLE_I18N */ + static reg_errcode_t sift_states_backward (const re_match_context_t *mctx, +- re_sift_context_t *sctx); ++ re_sift_context_t *sctx, ++ unsigned int *bktrack_cnt); + static reg_errcode_t build_sifted_states (const re_match_context_t *mctx, + re_sift_context_t *sctx, Idx str_idx, + re_node_set *cur_dest); + static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx, + re_sift_context_t *sctx, + Idx str_idx, +- re_node_set *dest_nodes); ++ re_node_set *dest_nodes, ++ unsigned int *bktrack_cnt); + static reg_errcode_t add_epsilon_src_nodes (const re_dfa_t *dfa, + re_node_set *dest_nodes, + const re_node_set *candidates); +@@ -90,7 +98,8 @@ static bool check_dst_limits (const re_match_context_t *mctx, + Idx src_idx); + static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, + int boundaries, Idx subexp_idx, +- Idx from_node, Idx bkref_idx); ++ Idx from_node, Idx bkref_idx, ++ unsigned int *bktrack_cnt); + static int check_dst_limits_calc_pos (const re_match_context_t *mctx, + Idx limit, Idx subexp_idx, + Idx node, Idx str_idx, +@@ -103,7 +112,8 @@ static reg_errcode_t check_subexp_limits (const re_dfa_t *dfa, + Idx str_idx); + static reg_errcode_t sift_states_bkref (const re_match_context_t *mctx, + re_sift_context_t *sctx, +- Idx str_idx, const re_node_set *candidates); ++ Idx str_idx, const re_node_set *candidates, ++ unsigned int *bktrack_cnt); + static reg_errcode_t merge_state_array (const re_dfa_t *dfa, + re_dfastate_t **dst, + re_dfastate_t **src, Idx num); +@@ -923,6 +933,7 @@ prune_impossible_nodes (re_match_context_t *mctx) + const re_dfa_t *const dfa = mctx->dfa; + Idx halt_node, match_last; + reg_errcode_t ret; ++ unsigned int bktrack_cnt = 0; + re_dfastate_t **sifted_states; + re_dfastate_t **lim_states = NULL; + re_sift_context_t sctx; +@@ -943,6 +954,7 @@ prune_impossible_nodes (re_match_context_t *mctx) + ret = REG_ESPACE; + goto free_return; + } ++ + if (dfa->nbackref) + { + lim_states = re_malloc (re_dfastate_t *, match_last + 1); +@@ -957,7 +969,7 @@ prune_impossible_nodes (re_match_context_t *mctx) + sizeof (re_dfastate_t *) * (match_last + 1)); + sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, + match_last); +- ret = sift_states_backward (mctx, &sctx); ++ ret = sift_states_backward (mctx, &sctx, &bktrack_cnt); + re_node_set_free (&sctx.limits); + if (__glibc_unlikely (ret != REG_NOERROR)) + goto free_return; +@@ -987,7 +999,7 @@ prune_impossible_nodes (re_match_context_t *mctx) + else + { + sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last); +- ret = sift_states_backward (mctx, &sctx); ++ ret = sift_states_backward (mctx, &sctx, &bktrack_cnt); + re_node_set_free (&sctx.limits); + if (__glibc_unlikely (ret != REG_NOERROR)) + goto free_return; +@@ -1571,7 +1583,8 @@ update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, + ((state) != NULL && re_node_set_contains (&(state)->nodes, node)) + + static reg_errcode_t +-sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx) ++sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx, ++ unsigned int *bktrack_cnt) + { + reg_errcode_t err; + int null_cnt = 0; +@@ -1587,7 +1600,7 @@ sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx) + err = re_node_set_init_1 (&cur_dest, sctx->last_node); + if (__glibc_unlikely (err != REG_NOERROR)) + return err; +- err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); ++ err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest, bktrack_cnt); + if (__glibc_unlikely (err != REG_NOERROR)) + goto free_return; + +@@ -1617,7 +1630,7 @@ sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx) + - It can epsilon transit to a node in CUR_DEST. + - It is in CUR_SRC. + And update state_log. */ +- err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); ++ err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest, bktrack_cnt); + if (__glibc_unlikely (err != REG_NOERROR)) + goto free_return; + } +@@ -1743,7 +1756,7 @@ merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst, + static reg_errcode_t + update_cur_sifted_state (const re_match_context_t *mctx, + re_sift_context_t *sctx, Idx str_idx, +- re_node_set *dest_nodes) ++ re_node_set *dest_nodes, unsigned int *bktrack_cnt) + { + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err = REG_NOERROR; +@@ -1780,7 +1793,7 @@ update_cur_sifted_state (const re_match_context_t *mctx, + + if (candidates && mctx->state_log[str_idx]->has_backref) + { +- err = sift_states_bkref (mctx, sctx, str_idx, candidates); ++ err = sift_states_bkref (mctx, sctx, str_idx, candidates, bktrack_cnt); + if (__glibc_unlikely (err != REG_NOERROR)) + return err; + } +@@ -1883,10 +1896,17 @@ check_dst_limits (const re_match_context_t *mctx, const re_node_set *limits, + dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], + subexp_idx, dst_node, dst_idx, + dst_bkref_idx); ++ ++ if (__glibc_unlikely (dst_pos == DST_LIMIT_BAD_POS)) ++ return true; ++ + src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], + subexp_idx, src_node, src_idx, + src_bkref_idx); + ++ if (__glibc_unlikely (src_pos == DST_LIMIT_BAD_POS)) ++ return true; ++ + /* In case of: + ( ) + ( ) +@@ -1901,12 +1921,16 @@ check_dst_limits (const re_match_context_t *mctx, const re_node_set *limits, + + static int + check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries, +- Idx subexp_idx, Idx from_node, Idx bkref_idx) ++ Idx subexp_idx, Idx from_node, Idx bkref_idx, ++ unsigned int *bktrack_cnt) + { + const re_dfa_t *const dfa = mctx->dfa; + const re_node_set *eclosures = dfa->eclosures + from_node; + Idx node_idx; + ++ if (__glibc_unlikely (chk_bktrack_exhaust(bktrack_cnt))) ++ return DST_LIMIT_BAD_POS; ++ + /* Else, we are on the boundary: examine the nodes on the epsilon + closure. */ + for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx) +@@ -1948,12 +1972,14 @@ check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries, + + cpos = + check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, +- dst, bkref_idx); ++ dst, bkref_idx, bktrack_cnt); + if (cpos == -1 /* && (boundaries & 1) */) + return -1; + if (cpos == 0 && (boundaries & 2)) + return 0; +- ++ if (__glibc_unlikely (cpos == DST_LIMIT_BAD_POS)) ++ return DST_LIMIT_BAD_POS; ++ + if (subexp_idx < BITSET_WORD_BITS) + ent->eps_reachable_subexps_map + &= ~((bitset_word_t) 1 << subexp_idx); +@@ -1987,6 +2013,7 @@ check_dst_limits_calc_pos (const re_match_context_t *mctx, Idx limit, + { + struct re_backref_cache_entry *lim = mctx->bkref_ents + limit; + int boundaries; ++ unsigned int bktrack_cnt = 0; + + /* If we are outside the range of the subexpression, return -1 or 1. */ + if (str_idx < lim->subexp_from) +@@ -2003,7 +2030,7 @@ check_dst_limits_calc_pos (const re_match_context_t *mctx, Idx limit, + + /* Else, examine epsilon closure. */ + return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, +- from_node, bkref_idx); ++ from_node, bkref_idx, &bktrack_cnt); + } + + /* Check the limitations of sub expressions LIMITS, and remove the nodes +@@ -2099,12 +2126,20 @@ check_subexp_limits (const re_dfa_t *dfa, re_node_set *dest_nodes, + static reg_errcode_t + __attribute_warn_unused_result__ + sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx, +- Idx str_idx, const re_node_set *candidates) ++ Idx str_idx, const re_node_set *candidates, ++ unsigned int *bktrack_cnt) + { + const re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + Idx node_idx, node; + re_sift_context_t local_sctx; ++ ++ if (__glibc_unlikely (chk_bktrack_exhaust(bktrack_cnt))) ++ { ++ err = REG_ESPACE; ++ goto free_return; ++ } ++ + Idx first_idx = search_cur_bkref_entry (mctx, str_idx); + + if (first_idx == -1) +@@ -2165,7 +2200,7 @@ sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx, + goto free_return; + } + cur_state = local_sctx.sifted_states[str_idx]; +- err = sift_states_backward (mctx, &local_sctx); ++ err = sift_states_backward (mctx, &local_sctx, bktrack_cnt); + if (__glibc_unlikely (err != REG_NOERROR)) + goto free_return; + if (sctx->limited_states != NULL) +-- +2.19.1 + + diff --git a/glibc.spec b/glibc.spec index 34af3f8f5b0fec85c63dbb97f1cf7c0d6b73e19b..913d286153fb857471ae8546d81c0dfb18485d18 100644 --- a/glibc.spec +++ b/glibc.spec @@ -59,7 +59,7 @@ ############################################################################## Name: glibc Version: 2.28 -Release: 31 +Release: 32 Summary: The GNU libc libraries License: %{all_license} URL: http://www.gnu.org/software/glibc/ @@ -73,6 +73,9 @@ Source5: glibc-bench-compare Source6: LicenseList Source7: LanguageList +Patch6000: not-use-gettimeofday-in-ramdom-id.patch +Patch9000: CVE-2019-9192-add-backtrack-limit-to-avoid-infinite-loop.patch + Provides: ldconfig rtld(GNU_HASH) bundled(gnulib) BuildRequires: audit-libs-devel >= 1.1.3, sed >= 3.95, libcap-devel, gettext @@ -919,6 +922,10 @@ fi %changelog +* Mon Jan 20 2020 liqingqing -2.28-32 +- fix CVE-2019-9192, CVE-2018-20796, regex add backtrack limit to avoid infinite loop. + backport upstream commit 359653aaacad, note that we just use parts of them. + * Tue Jan 7 2020 Wang Shuo - 2.28-31 - Fix compile macro diff --git a/not-use-gettimeofday-in-ramdom-id.patch b/not-use-gettimeofday-in-ramdom-id.patch new file mode 100644 index 0000000000000000000000000000000000000000..9b1765ea00edf7c3b848a109f457c9d2809f5ccf --- /dev/null +++ b/not-use-gettimeofday-in-ramdom-id.patch @@ -0,0 +1,68 @@ +From b2d23fe92cceee2464639e146a57a857b90d7dec Mon Sep 17 00:00:00 2001 +From: Adhemerval Zanella +Date: Tue, 17 Dec 2019 17:29:16 +0800 +Subject: [PATCH] backport commit 359653aaacad463d916323f03c0ac3c47405aafa + +note that we just use parts of this commit. + +From 359653aaacad463d916323f03c0ac3c47405aafa +Author: Adhemerval Zanella +Date: Wed Jan 16 18:10:56 2019 +0000 + + Do not use HP_TIMING_NOW for random bits + + This patch removes the HP_TIMING_BITS usage for fast random bits and replace + with clock_gettime (CLOCK_MONOTONIC). It has unspecified starting time and + nano-second accuracy, so its randomness is significantly better than + gettimeofday. + + Althoug it should incur in more overhead (specially for architecture that + support hp-timing), the symbol is also common implemented as a vDSO. + + Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu. I also + checked on a i686-gnu build. + + * include/random-bits.h: New file. + * resolv/res_mkquery.c [HP_TIMING_AVAIL] (RANDOM_BITS, + (__res_context_mkquery): Remove usage hp-timing usage and replace with + random_bits. + * resolv/res_send.c [HP_TIMING_AVAIL] (nameserver_offset): Likewise. + * sysdeps/posix/tempname.c [HP_TIMING_AVAIL] (__gen_tempname): + Likewise. + +--- + resolv/res_mkquery.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/resolv/res_mkquery.c b/resolv/res_mkquery.c +index 213abeef..7ba40640 100644 +--- a/resolv/res_mkquery.c ++++ b/resolv/res_mkquery.c +@@ -95,6 +95,7 @@ + + #include + #include ++#include + #if HP_TIMING_AVAIL + # define RANDOM_BITS(Var) { uint64_t v64; HP_TIMING_NOW (v64); Var = v64; } + #endif +@@ -124,9 +125,12 @@ __res_context_mkquery (struct resolv_context *ctx, int op, const char *dname, + #ifdef RANDOM_BITS + RANDOM_BITS (randombits); + #else +- struct timeval tv; +- __gettimeofday (&tv, NULL); +- randombits = (tv.tv_sec << 8) ^ tv.tv_usec; ++ struct timespec tv; ++ clock_gettime (CLOCK_MONOTONIC, &tv); ++ /* Shuffle the lower bits to minimize the clock bias. */ ++ uint32_t ret = tv.tv_nsec ^ tv.tv_sec; ++ ret ^= (ret << 24) | (ret >> 8); ++ randombits = ret; + #endif + + hp->id = randombits; +-- +2.19.1 + +