From 776ede1a0c2fb5cc963fb489c4afd9ee34945384 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mar 21 2017 16:26:52 +0000 Subject: CVE-2016-10253 Signed-off-by: Peter Lemenkov --- diff --git a/erlang.spec b/erlang.spec index 4dd0e41..5d66ef8 100644 --- a/erlang.spec +++ b/erlang.spec @@ -66,7 +66,7 @@ Name: erlang Version: 19.3 -Release: 1%{?dist} +Release: 2%{?dist} Summary: General-purpose programming language and runtime environment Group: Development/Languages @@ -116,6 +116,9 @@ Patch5: otp-0005-Do-not-install-nteventlog-and-related-doc-files-on-n.patch # Fedora specific patch # Do not install erlang sources Patch6: otp-0006-Do-not-install-erlang-sources.patch +# Fedora specific patch +# Fix CVE-2016-10253 +Patch7: otp-0007-Fix-CVE-2016-10253.patch # end of autogenerated patch tag list BuildRequires: flex @@ -926,6 +929,7 @@ Provides support for XML 1.0. %patch4 -p1 -b .Do_not_install_Java_sources %patch5 -p1 -b .Do_not_install_nteventlog_and_related_doc_files_on_n %patch6 -p1 -b .Do_not_install_erlang_sources +%patch7 -p1 -b .Fix_CVE_2016_10253 # end of autogenerated prep patch list # FIXME we should come up with a better solution @@ -2312,6 +2316,9 @@ useradd -r -g epmd -d /tmp -s /sbin/nologin \ %changelog +* Tue Mar 21 2017 Peter Lemenkov - 19.3-2 +- Fix for CVE-2016-10253 + * Wed Mar 15 2017 Peter Lemenkov - 19.3-1 - Ver. 19.3 diff --git a/otp-0007-Fix-CVE-2016-10253.patch b/otp-0007-Fix-CVE-2016-10253.patch new file mode 100644 index 0000000..bee9afb --- /dev/null +++ b/otp-0007-Fix-CVE-2016-10253.patch @@ -0,0 +1,318 @@ +From: Zephyr Pellerin +Date: Mon, 20 Mar 2017 15:36:41 -0700 +Subject: [PATCH] Fix CVE-2016-10253 + + +diff --git a/erts/emulator/pcre/pcre_compile.c b/erts/emulator/pcre/pcre_compile.c +index d48126a..15a81fa 100644 +--- a/erts/emulator/pcre/pcre_compile.c ++++ b/erts/emulator/pcre/pcre_compile.c +@@ -2335,34 +2335,36 @@ for (;;) + } + } + +- +- + /************************************************* +-* Scan compiled branch for non-emptiness * +-*************************************************/ ++ * Scan compiled branch for non-emptiness * ++ *************************************************/ + + /* This function scans through a branch of a compiled pattern to see whether it +-can match the empty string or not. It is called from could_be_empty() +-below and from compile_branch() when checking for an unlimited repeat of a +-group that can match nothing. Note that first_significant_code() skips over +-backward and negative forward assertions when its final argument is TRUE. If we +-hit an unclosed bracket, we return "empty" - this means we've struck an inner +-bracket whose current branch will already have been scanned. +- +-Arguments: +- code points to start of search +- endcode points to where to stop +- utf TRUE if in UTF-8 / UTF-16 / UTF-32 mode +- cd contains pointers to tables etc. +- +-Returns: TRUE if what is matched could be empty ++ can match the empty string or not. It is called from could_be_empty() ++ below and from compile_branch() when checking for an unlimited repeat of a ++ group that can match nothing. Note that first_significant_code() skips over ++ backward and negative forward assertions when its final argument is TRUE. If we ++ hit an unclosed bracket, we return "empty" - this means we've struck an inner ++ bracket whose current branch will already have been scanned. ++ ++ Arguments: ++ code points to start of search ++ endcode points to where to stop ++ utf TRUE if in UTF-8 / UTF-16 / UTF-32 mode ++ cd contains pointers to tables etc. ++ recurses chain of recurse_check to catch mutual recursion ++ ++ Returns: TRUE if what is matched could be empty + */ + ++ + static BOOL + could_be_empty_branch(const pcre_uchar *code, const pcre_uchar *endcode, +- BOOL utf, compile_data *cd) ++ BOOL utf, compile_data *cd, recurse_check *recurses) + { + register pcre_uchar c; ++recurse_check this_recurse; ++ + for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); + code < endcode; + code = first_significant_code(code + PRIV(OP_lengths)[c], TRUE)) +@@ -2390,25 +2392,47 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); + + if (c == OP_RECURSE) + { +- const pcre_uchar *scode; ++ const pcre_uchar *scode = cd->start_code + GET(code, 1); ++ const pcre_uchar *endgroup = scode; + BOOL empty_branch; + +- /* Test for forward reference */ ++ /* Test for forward reference or uncompleted reference. This is disabled ++ when called to scan a completed pattern by setting cd->start_workspace to ++ NULL. */ + +- for (scode = cd->start_workspace; scode < cd->hwm; scode += LINK_SIZE) +- if ((int)GET(scode, 0) == (int)(code + 1 - cd->start_code)) return TRUE; ++ if (cd->start_workspace != NULL) ++ { ++ const pcre_uchar *tcode; ++ for (tcode = cd->start_workspace; tcode < cd->hwm; tcode += LINK_SIZE) ++ if ((int)GET(tcode, 0) == (int)(code + 1 - cd->start_code)) return TRUE; ++ if (GET(scode, 1) == 0) return TRUE; /* Unclosed */ ++ } + +- /* Not a forward reference, test for completed backward reference */ ++ /* If the reference is to a completed group, we need to detect whether this ++ is a recursive call, as otherwise there will be an infinite loop. If it is ++ a recursion, just skip over it. Simple recursions are easily detected. For ++ mutual recursions we keep a chain on the stack. */ + +- empty_branch = FALSE; +- scode = cd->start_code + GET(code, 1); +- if (GET(scode, 1) == 0) return TRUE; /* Unclosed */ ++ do endgroup += GET(endgroup, 1); while (*endgroup == OP_ALT); ++ if (code >= scode && code <= endgroup) continue; /* Simple recursion */ ++ else ++ { ++ recurse_check *r = recurses; ++ for (r = recurses; r != NULL; r = r->prev) ++ if (r->group == scode) break; ++ if (r != NULL) continue; /* Mutual recursion */ ++ } ++ ++ /* Completed reference; scan the referenced group, remembering it on the ++ stack chain to detect mutual recursions. */ + +- /* Completed backwards reference */ ++ empty_branch = FALSE; ++ this_recurse.prev = recurses; ++ this_recurse.group = scode; + + do + { +- if (could_be_empty_branch(scode, endcode, utf, cd)) ++ if (could_be_empty_branch(scode, endcode, utf, cd, &this_recurse)) + { + empty_branch = TRUE; + break; +@@ -2448,7 +2472,7 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); + if (c == OP_BRA || c == OP_BRAPOS || + c == OP_CBRA || c == OP_CBRAPOS || + c == OP_ONCE || c == OP_ONCE_NC || +- c == OP_COND) ++ c == OP_COND || c == OP_SCOND) + { + BOOL empty_branch; + if (GET(code, 1) == 0) return TRUE; /* Hit unclosed bracket */ +@@ -2464,8 +2488,8 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); + empty_branch = FALSE; + do + { +- if (!empty_branch && could_be_empty_branch(code, endcode, utf, cd)) +- empty_branch = TRUE; ++ if (!empty_branch && could_be_empty_branch(code, endcode, utf, cd, ++ recurses)) empty_branch = TRUE; + code += GET(code, 1); + } + while (*code == OP_ALT); +@@ -2522,34 +2546,57 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); + + /* Opcodes that must match a character */ + ++ case OP_ANY: ++ case OP_ALLANY: ++ case OP_ANYBYTE: ++ + case OP_PROP: + case OP_NOTPROP: ++ case OP_ANYNL: ++ ++ case OP_NOT_HSPACE: ++ case OP_HSPACE: ++ case OP_NOT_VSPACE: ++ case OP_VSPACE: + case OP_EXTUNI: ++ + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: +- case OP_ANY: +- case OP_ALLANY: +- case OP_ANYBYTE: ++ + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: ++ + case OP_PLUS: ++ case OP_PLUSI: + case OP_MINPLUS: +- case OP_POSPLUS: +- case OP_EXACT: ++ case OP_MINPLUSI: ++ + case OP_NOTPLUS: ++ case OP_NOTPLUSI: + case OP_NOTMINPLUS: ++ case OP_NOTMINPLUSI: ++ ++ case OP_POSPLUS: ++ case OP_POSPLUSI: + case OP_NOTPOSPLUS: ++ case OP_NOTPOSPLUSI: ++ ++ case OP_EXACT: ++ case OP_EXACTI: + case OP_NOTEXACT: ++ case OP_NOTEXACTI: ++ + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEPOSPLUS: + case OP_TYPEEXACT: ++ + return FALSE; + + /* These are going to continue, as they may be empty, but we have to +@@ -2583,30 +2630,58 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); + return TRUE; + + /* In UTF-8 mode, STAR, MINSTAR, POSSTAR, QUERY, MINQUERY, POSQUERY, UPTO, +- MINUPTO, and POSUPTO may be followed by a multibyte character */ ++ MINUPTO, and POSUPTO and their caseless and negative versions may be ++ followed by a multibyte character. */ + + #if defined SUPPORT_UTF && !defined COMPILE_PCRE32 + case OP_STAR: + case OP_STARI: ++ case OP_NOTSTAR: ++ case OP_NOTSTARI: ++ + case OP_MINSTAR: + case OP_MINSTARI: ++ case OP_NOTMINSTAR: ++ case OP_NOTMINSTARI: ++ + case OP_POSSTAR: + case OP_POSSTARI: ++ case OP_NOTPOSSTAR: ++ case OP_NOTPOSSTARI: ++ + case OP_QUERY: + case OP_QUERYI: ++ case OP_NOTQUERY: ++ case OP_NOTQUERYI: ++ + case OP_MINQUERY: + case OP_MINQUERYI: ++ case OP_NOTMINQUERY: ++ case OP_NOTMINQUERYI: ++ + case OP_POSQUERY: + case OP_POSQUERYI: ++ case OP_NOTPOSQUERY: ++ case OP_NOTPOSQUERYI: ++ + if (utf && HAS_EXTRALEN(code[1])) code += GET_EXTRALEN(code[1]); + break; + + case OP_UPTO: + case OP_UPTOI: ++ case OP_NOTUPTO: ++ case OP_NOTUPTOI: ++ + case OP_MINUPTO: + case OP_MINUPTOI: ++ case OP_NOTMINUPTO: ++ case OP_NOTMINUPTOI: ++ + case OP_POSUPTO: + case OP_POSUPTOI: ++ case OP_NOTPOSUPTO: ++ case OP_NOTPOSUPTOI: ++ + if (utf && HAS_EXTRALEN(code[1 + IMM2_SIZE])) code += GET_EXTRALEN(code[1 + IMM2_SIZE]); + break; + #endif +@@ -2632,7 +2707,6 @@ return TRUE; + } + + +- + /************************************************* + * Scan compiled regex for non-emptiness * + *************************************************/ +@@ -2660,7 +2734,7 @@ could_be_empty(const pcre_uchar *code, const pcre_uchar *endcode, + { + while (bcptr != NULL && bcptr->current_branch >= code) + { +- if (!could_be_empty_branch(bcptr->current_branch, endcode, utf, cd)) ++ if (!could_be_empty_branch(bcptr->current_branch, endcode, utf, cd, NULL)) + return FALSE; + bcptr = bcptr->outer; + } +@@ -2668,7 +2742,6 @@ return TRUE; + } + + +- + /************************************************* + * Check for POSIX class syntax * + *************************************************/ +@@ -5392,7 +5465,7 @@ for (;; ptr++) + pcre_uchar *scode = bracode; + do + { +- if (could_be_empty_branch(scode, ketcode, utf, cd)) ++ if (could_be_empty_branch(scode, ketcode, utf, cd, NULL)) + { + *bracode += OP_SBRA - OP_BRA; + break; +diff --git a/erts/emulator/pcre/pcre_internal.h b/erts/emulator/pcre/pcre_internal.h +index af436bd..eb0db89 100644 +--- a/erts/emulator/pcre/pcre_internal.h ++++ b/erts/emulator/pcre/pcre_internal.h +@@ -2455,6 +2455,13 @@ typedef struct branch_chain { + pcre_uchar *current_branch; + } branch_chain; + ++/* Structure for mutual recursion detection. */ ++ ++typedef struct recurse_check { ++ struct recurse_check *prev; ++ const pcre_uchar *group; ++} recurse_check; ++ + /* Structure for items in a linked list that represents an explicit recursive + call within the pattern; used by pcre_exec(). */ +