Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

heap-buffer-overflow in Perl_uvoffuni_to_utf8_flags_msgs #17643

Closed
dur-randir opened this issue Mar 13, 2020 · 3 comments
Closed

heap-buffer-overflow in Perl_uvoffuni_to_utf8_flags_msgs #17643

dur-randir opened this issue Mar 13, 2020 · 3 comments
Milestone

Comments

@dur-randir
Copy link
Member

This is a bug report for perl from [email protected],
generated with the help of perlbug 1.41 running under perl 5.31.10.


[Please describe your issue here]

While fuzzing perl v5.31.9-70-g0c96aa4b7b built with afl and run
under libdislocator, I found the following program

s--d.-%y-00.d-0\x{d0000}-

to cause heap-buffer-overflow. ASAN diagnostics are:

WRITE of size 1 at 0x602000001576 thread T0
#0 0xd5c006 in Perl_uvoffuni_to_utf8_flags_msgs /home/afl/afl-asan/utf8.c:374:7
#1 0xc71ca7 in S_do_trans_invmap /home/afl/afl-asan/doop.c:542:21
#2 0xc71ca7 in Perl_do_trans /home/afl/afl-asan/doop.c:623
#3 0xae0341 in Perl_pp_trans /home/afl/afl-asan/pp.c:692:13
#4 0x8efa3e in Perl_runops_debug /home/afl/afl-asan/dump.c:2571:23
#5 0x61fd94 in S_run_body /home/afl/afl-asan/perl.c
#6 0x61f1f6 in perl_run /home/afl/afl-asan/perl.c:2687:2
#7 0x5352f3 in main /home/afl/afl-asan/perlmain.c:134:9
#8 0x7f1c428be09a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a)
#9 0x43ccb9 in _start (/home/afl/afl-asan/perl+0x43ccb9)

0x602000001576 is located 0 bytes to the right of 6-byte region [0x602000001570,0x602000001576)
allocated by thread T0 here:
#0 0x501a90 in malloc (/home/afl/afl-asan/perl+0x501a90)
#1 0x8f59e6 in Perl_safesysmalloc /home/afl/afl-asan/util.c:155:21
#2 0xc70596 in S_do_trans_invmap /home/afl/afl-asan/doop.c:472:2
#3 0xc70596 in Perl_do_trans /home/afl/afl-asan/doop.c:623
#4 0xae0341 in Perl_pp_trans /home/afl/afl-asan/pp.c:692:13
#5 0x8efa3e in Perl_runops_debug /home/afl/afl-asan/dump.c:2571:23
#6 0x61fd94 in S_run_body /home/afl/afl-asan/perl.c
#7 0x61f1f6 in perl_run /home/afl/afl-asan/perl.c:2687:2
#8 0x5352f3 in main /home/afl/afl-asan/perlmain.c:134:9
#9 0x7f1c428be09a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a)

This is regression in blead, bisect points to the following range:

The first bad commit could be any of:

8c90d3a

Author: Karl Williamson [email protected]
AuthorDate: Wed Oct 2 22:34:37 2019 -0600
Commit: Karl Williamson [email protected]
CommitDate: Wed Nov 6 21:22:24 2019 -0700

intrpvar.h: Add variable for use in tr///

f34acfe

Author: Karl Williamson [email protected]
AuthorDate: Mon Nov 4 21:30:48 2019 -0700
Commit: Karl Williamson [email protected]
CommitDate: Wed Nov 6 21:22:24 2019 -0700

Reimplement tr/// without swashes

We cannot bisect more!

[Please do not change anything below this line]


Flags:
category=core
severity=high

Site configuration information for perl 5.31.10:

Configured by root at Fri Mar 13 17:15:02 MSK 2020.

Summary of my perl5 (revision 5 version 31 subversion 10) configuration:
Commit id: 0c96aa4
Platform:
osname=linux
osvers=4.19.0-8-amd64
archname=x86_64-linux
uname='linux dorothy 4.19.0-8-amd64 #1 smp debian 4.19.98-1 (2020-01-26) x86_64 gnulinux '
config_args='-de -Dusedevel -Doptimize=-O2'
hint=recommended
useposix=true
d_sigaction=define
useithreads=undef
usemultiplicity=undef
use64bitint=define
use64bitall=define
uselongdouble=undef
usemymalloc=n
default_inc_excludes_dot=define
bincompat5005=undef
Compiler:
cc='cc'
ccflags ='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2'
optimize='-O2'
cppflags='-fwrapv -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
ccversion=''
gccversion='8.3.0'
gccosandvers=''
intsize=4
longsize=8
ptrsize=8
doublesize=8
byteorder=12345678
doublekind=3
d_longlong=define
longlongsize=8
d_longdbl=define
longdblsize=16
longdblkind=3
ivtype='long'
ivsize=8
nvtype='double'
nvsize=8
Off_t='off_t'
lseeksize=8
alignbytes=8
prototype=define
Linker and Libraries:
ld='cc'
ldflags =' -fstack-protector-strong -L/usr/local/lib'
libpth=/usr/local/lib /usr/lib/gcc/x86_64-linux-gnu/8/include-fixed /usr/include/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib/../lib /usr/lib/x86_64-linux-gnu /usr/lib/../lib /lib
libs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
libc=libc-2.28.so
so=so
useshrplib=false
libperl=libperl.a
gnulibc_version='2.28'
Dynamic Linking:
dlsrc=dl_dlopen.xs
dlext=so
d_dlsymun=undef
ccdlflags='-Wl,-E'
cccdlflags='-fPIC'
lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong'


@inc for perl 5.31.10:
lib
/usr/local/lib/perl5/site_perl/5.31.10/x86_64-linux
/usr/local/lib/perl5/site_perl/5.31.10
/usr/local/lib/perl5/5.31.10/x86_64-linux
/usr/local/lib/perl5/5.31.10


Environment for perl 5.31.10:
HOME=/home/afl
LANG=en_US.UTF-8
LANGUAGE=en_US:en
LC_CTYPE=en_US.UTF-8
LC_TIME=C
LD_LIBRARY_PATH (unset)
LOGDIR (unset)
PATH=/home/afl/perlbrew/bin:/home/afl/perlbrew/perls/perl-5.30.0-dbg/bin:/opt/local/bin:/usr/texbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PERLBREW_HOME=/home/afl/.perlbrew
PERLBREW_MANPATH=/home/afl/perlbrew/perls/perl-5.30.0-dbg/man
PERLBREW_PATH=/home/afl/perlbrew/bin:/home/afl/perlbrew/perls/perl-5.30.0-dbg/bin
PERLBREW_PERL=perl-5.30.0-dbg
PERLBREW_ROOT=/home/afl/perlbrew
PERLBREW_SHELLRC_VERSION=0.88
PERLBREW_VERSION=0.88
PERL_BADLANG (unset)

@hvds
Copy link
Contributor

hvds commented Mar 13, 2020

FWIW, the reproducer simplifies slightly to: ./miniperl -e '"cb" =~ tr{aabc}{d\x{d0000}}r'.

@hvds
Copy link
Contributor

hvds commented Mar 13, 2020

@khwilliamson within pmtrans(), max_expansion is only set after tests on ranges that are not marked TR_SPECIAL_HANDLING (which can, among other things, mean "replace with last replacement character seen"), so it needs an additional test for that case.

@khwilliamson khwilliamson added this to the 5.32.0 milestone Mar 20, 2020
@khwilliamson khwilliamson reopened this Apr 1, 2020
khwilliamson added a commit that referenced this issue Apr 1, 2020
A tr/// can be done in-place if the target string doesn't contain a
character whose transliterated representation is longer than the
original.  Otherwise, writing the new value would destroy the next
character we need to read.

In general, we can't know if a particular string contains such a
character without keeping a list of the problematic characters, and
scanning it ahead of time for occurrences of those.  Instead, we
determine at compilation time if, for a given transliteration, if there
exists any possible target string that could have an overwriting
problem.  If none exist, we edit in place.  Otherwise, we first make a
copy.

Prior to this commit, the code failed to account for the case where the
rhs is shorter than the left, so that any unmatched lhs characters map
to the final rhs one.  The reason the code didn't consider this is that
I didn't think of this possibility when writing it.

This fixes #17654 and #17643
@khwilliamson
Copy link
Contributor

Duplicate of #17654

@khwilliamson khwilliamson marked this as a duplicate of #17654 Apr 1, 2020
khwilliamson added a commit that referenced this issue Apr 1, 2020
A tr/// can be done in-place if the target string doesn't contain a
character whose transliterated representation is longer than the
original.  Otherwise, writing the new value would destroy the next
character we need to read.

In general, we can't know if a particular string contains such a
character without keeping a list of the problematic characters, and
scanning it ahead of time for occurrences of those.  Instead, we
determine at compilation time if, for a given transliteration, if there
exists any possible target string that could have an overwriting
problem.  If none exist, we edit in place.  Otherwise, we first make a
copy.

Prior to this commit, the code failed to account for the case where the
rhs is shorter than the left, so that any unmatched lhs characters map
to the final rhs one.  The reason the code didn't consider this is that
I didn't think of this possibility when writing it.

This fixes #17654 and #17643
khwilliamson added a commit that referenced this issue Apr 2, 2020
A tr/// can be done in-place if the target string doesn't contain a
character whose transliterated representation is longer than the
original.  Otherwise, writing the new value would destroy the next
character we need to read.

In general, we can't know if a particular string contains such a
character without keeping a list of the problematic characters, and
scanning it ahead of time for occurrences of those.  Instead, we
determine at compilation time if, for a given transliteration, if there
exists any possible target string that could have an overwriting
problem.  If none exist, we edit in place.  Otherwise, we first make a
copy.

Prior to this commit, the code failed to account for the case where the
rhs is shorter than the left, so that any unmatched lhs characters map
to the final rhs one.  The reason the code didn't consider this is that
I didn't think of this possibility when writing it.

This fixes #17654 and #17643
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants