Bug 1684058 - CVE-2019-9169 glibc: regular-expression match via proceed_next_node in posix/regexec.c leads to heap-based buffer over-read [fedora-all]
Summary: CVE-2019-9169 glibc: regular-expression match via proceed_next_node in posix/...
Keywords:
Status: CLOSED WONTFIX
Alias: None
Product: Fedora
Classification: Fedora
Component: glibc
Version: 29
Hardware: Unspecified
OS: Unspecified
medium
medium
Target Milestone: ---
Assignee: DJ Delorie
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks: CVE-2019-9169
TreeView+ depends on / blocked
 
Reported: 2019-02-28 10:34 UTC by msiddiqu
Modified: 2019-05-03 13:25 UTC (History)
11 users (show)

Fixed In Version:
Doc Type: No Doc Update
Doc Text:
Clone Of:
Environment:
Last Closed: 2019-05-03 13:25:46 UTC
Type: ---
Embargoed:


Attachments (Terms of Use)
coredumpctl debug / gdb output for segmentation fault running echo 0 | sed '/\(\)\(\1\(\)\1\(\)\)*/c0' (15.30 MB, text/plain)
2019-04-05 12:05 UTC, Matt Fagnani
no flags Details

Description msiddiqu 2019-02-28 10:34:28 UTC
This is an automatically created tracking bug!  It was created to ensure
that one or more security vulnerabilities are fixed in affected versions
of fedora-all.

For comments that are specific to the vulnerability please use bugs filed
against the "Security Response" product referenced in the "Blocks" field.

For more information see:
http://fedoraproject.org/wiki/Security/TrackingBugs

When submitting as an update, use the fedpkg template provided in the next
comment(s).  This will include the bug IDs of this tracking bug as well as
the relevant top-level CVE bugs.

Please also mention the CVE IDs being fixed in the RPM changelog and the
fedpkg commit message.

NOTE: this issue affects multiple supported versions of Fedora. While only
one tracking bug has been filed, please correct all affected versions at
the same time.  If you need to fix the versions independent of each other,
you may clone this bug as appropriate.

Comment 1 msiddiqu 2019-02-28 10:34:30 UTC
Use the following template to for the 'fedpkg update' request to submit an
update for this issue as it contains the top-level parent bug(s) as well as
this tracking bug.  This will ensure that all associated bugs get updated
when new packages are pushed to stable.

=====

# bugfix, security, enhancement, newpackage (required)
type=security

# low, medium, high, urgent (required)
severity=medium

# testing, stable
request=testing

# Bug numbers: 1234,9876
bugs=1684057,1684058

# Description of your update
notes=Security fix for [PUT CVEs HERE]

# Enable request automation based on the stable/unstable karma thresholds
autokarma=True
stable_karma=3
unstable_karma=-3

# Automatically close bugs when this marked as stable
close_bugs=True

# Suggest that users restart after update
suggest_reboot=False

======

Additionally, you may opt to use the bodhi web interface to submit updates:

https://bodhi.fedoraproject.org/updates/new

Comment 2 Matt Fagnani 2019-04-05 12:05:54 UTC
Created attachment 1552434 [details]
coredumpctl debug / gdb output for segmentation fault running echo 0 | sed '/\(\)\(\1\(\)\1\(\)\)*/c0'

I read in the glibc-2.28-27.fc29 bodhi change log that CVE-2019-9169 was fixed
"Record CVE-2019-9169 in NEWS and ChangeLog [BZ #24114]
regex: fix read overrun [BZ #24114]"
https://bodhi.fedoraproject.org/updates/FEDORA-2019-5fcc2049a1

I tested the reproducer given by Huzaifa S. Sidhpurwala at https://bugzilla.redhat.com/show_bug.cgi?id=1684057#c3
echo 0 | sed '/\(\)\(\1\(\)\1\(\)\)*/c0'
with glibc-2.28-26 and after upgrading to glibc-2.28-27 and rebooting. I got segmentation faults with both 2.28-27 and 2.28-26 in check_dst_limits_calc_pos_1 at regexec.c:1945. The following output and attachment are with 2.28-27.

Core was generated by `sed /\(\)\(\1\(\)\1\(\)\)*/c0'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0xb7e2609b in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0xbfb98584, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=6, 
    bkref_idx=0) at regexec.c:1945
1945                      cpos =

The trace showed that check_dst_limits_calc_pos_1 at regexec.c:1945 repeated from #0 to #87215 with the only apparent difference being an alternation of from_node=6 to from_node=3

(gdb) bt
#0  0xb7e2609b in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0xbfb98584, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=6, 
    bkref_idx=0) at regexec.c:1945
#1  0xb7e260ad in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0xbfb98584, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=3, 
    bkref_idx=0) at regexec.c:1945
#2  0xb7e260ad in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0xbfb98584, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=6, 
    bkref_idx=0) at regexec.c:1945
#3  0xb7e260ad in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0xbfb98584, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=3, 
    bkref_idx=0) at regexec.c:1945
#4  0xb7e260ad in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0xbfb98584, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=6, 
    bkref_idx=0) at regexec.c:1945
#5  0xb7e260ad in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0xbfb98584, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=3, 
    bkref_idx=0) at regexec.c:1945
#6  0xb7e260ad in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0xbfb98584, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=6, 
    bkref_idx=0) at regexec.c:1945
#7  0xb7e260ad in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0xbfb98584, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=3, 
    bkref_idx=0) at regexec.c:1945
#8  0xb7e260ad in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0xbfb98584, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=6, 
    bkref_idx=0) at regexec.c:1945
...
#87216 0xb7e260ad in check_dst_limits_calc_pos_1 (mctx=0xbfb98584, boundaries=3, subexp_idx=0, from_node=2, bkref_idx=0) at regexec.c:1945
#87217 0xb7e2619b in check_dst_limits_calc_pos (mctx=mctx@entry=0xbfb98584, limit=<optimized out>, subexp_idx=subexp_idx@entry=0, from_node=<optimized out>, str_idx=<optimized out>, bkref_idx=0) at regexec.c:2001
#87218 0xb7e27d42 in check_dst_limits (mctx=mctx@entry=0xbfb98584, dst_node=2, dst_idx=0, src_node=3, src_idx=0, limits=<optimized out>, limits=<optimized out>) at regexec.c:1879
#87219 0xb7e30982 in sift_states_bkref (candidates=0x93dc14, str_idx=0, sctx=<optimized out>, mctx=0xbfb98584) at regexec.c:2141
#87220 update_cur_sifted_state (mctx=mctx@entry=0xbfb98584, sctx=sctx@entry=0xbfb98410, str_idx=str_idx@entry=0, dest_nodes=0xbfb98390) at regexec.c:1779
#87221 0xb7e2ed03 in sift_states_backward (mctx=mctx@entry=0xbfb98584, sctx=sctx@entry=0xbfb98410) at regexec.c:1586
#87222 0xb7e309df in sift_states_bkref (candidates=0x93dc14, str_idx=0, sctx=<optimized out>, mctx=0xbfb98584) at regexec.c:2164
#87223 update_cur_sifted_state (mctx=mctx@entry=0xbfb98584, sctx=sctx@entry=0xbfb98568, str_idx=str_idx@entry=0, dest_nodes=0xbfb984a0) at regexec.c:1779
#87224 0xb7e2ed03 in sift_states_backward (mctx=mctx@entry=0xbfb98584, sctx=sctx@entry=0xbfb98568) at regexec.c:1586
#87225 0xb7e301fc in prune_impossible_nodes (mctx=0xbfb98584) at regexec.c:956
#87226 re_search_internal (preg=0x93c020, string=0x93d970 "0", length=1, start=<optimized out>, last_start=1, stop=1, nmatch=1, pmatch=0x93d910, eflags=0) at regexec.c:823
#87227 0xb7e3475d in re_search_stub (bufp=0x93c020, string=0x93d970 "0", length=length@entry=1, start=0, range=1, stop=1, regs=0x0, ret_len=false) at regexec.c:424
#87228 0xb7e350c7 in __re_search (bufp=<optimized out>, string=<optimized out>, length=1, start=<optimized out>, range=<optimized out>, regs=0x0) at regexec.c:289
#87229 0x004427b7 in match_regex (regex=0x93c020, buf=0x93d970 "0", buflen=1, buf_start_offset=<optimized out>, regarray=0x0, regsize=0) at sed/regexp.c:418
#87230 0x0044004d in match_an_address_p (addr=<optimized out>, input=input@entry=0xbfb997c8) at sed/execute.c:802
#87231 0x00440e08 in match_address_p (input=0xbfb997c8, cmd=0x93bc00) at sed/execute.c:841
#87232 execute_program (vec=vec@entry=0x93ac00, input=input@entry=0xbfb997c8) at sed/execute.c:1289
#87233 0x00441f21 in process_files (the_program=0x93ac00, argv=0xbfb9995c) at sed/execute.c:1679
#87234 0x0043caaf in main (argc=<optimized out>, argv=<optimized out>) at sed/sed.c:401

I ran echo 0 | valgrind --log-file=valgrind_sed_1.txt sed '/\(\)\(\1\(\)\1\(\)\)*/c0' , but I got a stack overflow error seemingly in valgrind itself due to the large stack size.

==2776== Stack overflow in thread #1: can't grow stack to 0xbe07e000
==2776== 
==2776== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==2776==  Access not within mapped region at address 0xBE07EFF4
==2776== Stack overflow in thread #1: can't grow stack to 0xbe07e000
==2776==    at 0x4966F7A: check_dst_limits_calc_pos_1 (regexec.c:1901)
==2776==  If you believe this happened as a result of a stack
==2776==  overflow in your program's main thread (unlikely but
==2776==  possible), you can try to increase the size of the
==2776==  main thread stack using the --main-stacksize= flag.
==2776==  The main thread stack size used in this run was 8388608.
==2776== Stack overflow in thread #1: can't grow stack to 0xbe07e000
--2776-- VALGRIND INTERNAL ERROR: Valgrind received a signal 11 (SIGSEGV) - exiting
--2776-- si_code=1;  Faulting address: 0xBE07EFE0;  sp: 0x629ccf30

valgrind: the 'impossible' happened:
   Killed by fatal signal

host stacktrace:
==2776==    at 0x580A3DFA: ??? (in /usr/lib/valgrind/memcheck-x86-linux)

sched status:
  running_tid=1

Thread 1: status = VgTs_Runnable (lwpid 2776)

echo 0 | valgrind --log-file=valgrind_sed_6.txt --main-stacksize=100000000 sed '/\(\)\(\1\(\)\1\(\)\)*/c0' gave the same stack overflow error after I increased the main thread stack size from what I think were 8.3 MB to 100 MB. I can try other methods and give further information if that would help.

Comment 4 DJ Delorie 2019-04-05 19:46:56 UTC
I'm seeing this failure in rawhide as well, and it seems to be triggered by a different part of the code:

#10 0x00007f17d56b2b1a in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0x7ffd7df806e0, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=6, bkref_idx=bkref_idx@entry=0)
    at regexec.c:1945
#11 0x00007f17d56b2b1a in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0x7ffd7df806e0, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=3, bkref_idx=bkref_idx@entry=0)
    at regexec.c:1945
#12 0x00007f17d56b2b1a in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0x7ffd7df806e0, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=6, bkref_idx=bkref_idx@entry=0)
    at regexec.c:1945
#13 0x00007f17d56b2b1a in check_dst_limits_calc_pos_1 (mctx=mctx@entry=0x7ffd7df806e0, 
    boundaries=boundaries@entry=3, subexp_idx=subexp_idx@entry=0, from_node=3, bkref_idx=bkref_idx@entry=0)
    at regexec.c:1945

Note the tic-toc of from_node between 6 and 3.  In the code in question we see:

		  /* Recurse trying to reach the OP_OPEN_SUBEXP and
		     OP_CLOSE_SUBEXP cases below.  But, if the
		     destination node is the same node as the source
		     node, don't recurse because it would cause an
		     infinite loop: a regex that exhibits this behavior
		     is ()\1*\1*  */

So the source knows that such types of recursions are (1) possible, and (2) lead to inifinite recursion, but
only tests for the same nodes each time, not a tic-toc of nodes.

So while the test case above does eventually segfault (due to out-of-stack) it does not segfault due
to buffer overflow, so I don't think it's a faithful indicator of the original CVE.

Comment 5 Matt Fagnani 2019-04-05 23:22:48 UTC
DJ, I agree with your assessment. I see now that CVE-2019-9169 was assigned to the report at https://debbugs.gnu.org/cgi/bugreport.cgi?bug=34140 with reproducer ./grep -i '\(\(\)*.\)*\(\)\(\)\1' /bin/chvt with a heap-buffer overflow in proceed_next_node at regexec.c:1296. When I ran grep -i '\(\(\)*.\)*\(\)\(\)\1' /bin/chvt, the output was Binary file /bin/chvt matches. I didn't see a crash for that grep command reproducer.

The crashes I referred to in comment 2 with reproducer echo 0 | ./sed '/\(\)\(\1\(\)\1\(\)\)*/c0' appear to have been reported at https://debbugs.gnu.org/cgi/bugreport.cgi?bug=34141 with a stack overflow in check_dst_limits_calc_pos_1 at regexec.c:1912. So those crashes are a different problem from CVE-2019-9169. I could make another report if that would be appropriate. Thanks.

Comment 6 Carlos O'Donell 2019-05-03 13:25:46 UTC
The regular expression compiler in glibc is only supposed to be exposed to trusted content, so this is not a security vulnerability:

“resource exhaustion issues which can be triggered only with crafted patterns (either during compilation or execution) are not treated as security bugs”

<https://sourceware.org/glibc/wiki/Security%20Exceptions>


Note You need to log in before you can comment on or make changes to this bug.