Bug 2653

Summary: busybox grep with option -B can cause segmentation fault
Product: Busybox Reporter: Yunho Kim <kimyunho>
Component: OtherAssignee: unassigned
Status: RESOLVED FIXED    
Severity: major CC: busybox-cvs
Priority: P5    
Version: 1.17.x   
Target Milestone: ---   
Hardware: PC   
OS: Linux   
Host: Target:
Build:

Description Yunho Kim 2010-10-02 06:35:09 UTC
I report an integer overflow bug in a busybox grep applet, which causes an memory corruption. 

**** findutils/grep.c ****
634     if (option_mask32 & OPT_C) {
635         /* -C unsets prev -A and -B, but following -A or -B
636            may override it */
637         if (!(option_mask32 & OPT_A)) /* not overridden */
638             lines_after = Copt;
639         if (!(option_mask32 & OPT_B)) /* not overridden */
640             lines_before = Copt;
641     }
642     /* sanity checks */
643     if (option_mask32 & (OPT_c|OPT_q|OPT_l|OPT_L)) {
644         option_mask32 &= ~OPT_n;
645         lines_before = 0;
646         lines_after = 0;
647     } else if (lines_before > 0) {
648         before_buf = xzalloc(lines_before * sizeof(before_buf[0]));
**** findutils/grep.c ****

When grep is compiled with GREP_CONTEXT feature, option -A, -B, and -C are enabled. The option argument of the three options indicates # of lines of trailing, leading, and both, respectively. In line no 640 in findutils/grep.c, # of leading lines is stored in lines_before and lines_before is used in line 648 
to specify the size of allocated memory to store the lines where before_buf[0]'s type is a pointer to char (char*). So, if lines_before is greater than 2^32 / 4, lines_before * sizeof(before_buf[0]) will be very small number due to integer overflow. Because remaining grep parts assume that before_buf is large enough to store 'lines_before' lines unless before_buf is NULL, memory corruption occurs. 

The following transcript demonstrates how to reproduce the failure.

$ uname -an
Linux pswlab.kaist.ac.kr 2.6.22.14-72.fc6 #1 SMP Wed Nov 21 13:44:07 EST 2007 i686 i686 i386 GNU/Linux

$ cat test.dat
abc
def

$ ./busybox grep -B 1073741824 "de" test.dat
Segmentation fault

$ ./busybox grep -B 1173741824 "de" test.dat
*** glibc detected *** ./busybox: free(): invalid pointer: 0x00320380 ***
======= Backtrace: =========
/lib/libc.so.6[0x24aa96]
/lib/libc.so.6(cfree+0x90)[0x24dfb0]
./busybox[0x80cab34]
======= Memory map: ========
00110000-00111000 r-xp 00110000 00:00 0          [vdso]
00111000-0011b000 r-xp 00000000 fd:00 18964855   /usr/local/lib/libgcc_s.so.1
0011b000-0011c000 rwxp 00009000 fd:00 18964855   /usr/local/lib/libgcc_s.so.1
001c7000-001e0000 r-xp 00000000 fd:00 26247252   /lib/ld-2.5.so
001e0000-001e1000 r-xp 00019000 fd:00 26247252   /lib/ld-2.5.so
001e1000-001e2000 rwxp 0001a000 fd:00 26247252   /lib/ld-2.5.so
001e4000-0031e000 r-xp 00000000 fd:00 26248509   /lib/libc-2.5.so
0031e000-00320000 r-xp 0013a000 fd:00 26248509   /lib/libc-2.5.so
00320000-00321000 rwxp 0013c000 fd:00 26248509   /lib/libc-2.5.so
00321000-00324000 rwxp 00321000 00:00 0
00326000-0034b000 r-xp 00000000 fd:00 26248518   /lib/libm-2.5.so
0034b000-0034c000 r-xp 00024000 fd:00 26248518   /lib/libm-2.5.so
0034c000-0034d000 rwxp 00025000 fd:00 26248518   /lib/libm-2.5.so
08048000-080f9000 r-xp 00000000 fd:00 62390317   /home/yhkim/research/CREST/busybox/busybox-1.17.2/busybox
080f9000-080fa000 rw-p 000b1000 fd:00 62390317   /home/yhkim/research/CREST/busybox/busybox-1.17.2/busybox
080fa000-080fc000 rw-p 080fa000 00:00 0
082a0000-082c1000 rw-p 082a0000 00:00 0
a0000000-a0021000 rw-p a0000000 00:00 0
a0021000-a0100000 ---p a0021000 00:00 0
a01d1000-b7f4c000 rw-p a01d1000 00:00 0
b7f5c000-b7f5d000 rw-p b7f5c000 00:00 0
bfaea000-bfb00000 rw-p bfaea000 00:00 0          [stack]
Aborted

$ grep -B 1073741824 "de" test.dat
abc
def

$ grep -B 1173741824 "de" test.dat
abc
def

$ grep --version
grep (GNU grep) 2.5.1

Copyright 1988, 1992-1999, 2000, 2001 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ ./busybox --help
BusyBox v1.17.2 (2010-09-16 03:21:14 KST) multi-call binary.
Copyright (C) 1998-2009 Erik Andersen, Rob Landley, Denys Vlasenko
and others. Licensed under GPLv2.
See source distribution for full notice.

Usage: busybox [function] [arguments]...
   or: function [arguments]...

        BusyBox is a multi-call binary that combines many common Unix
        utilities into a single executable.  Most people will create a
        link to busybox for each function they wish to use and BusyBox
        will act like whatever it was invoked as.

Currently defined functions:
        [, [[, acpid, addgroup, adduser, adjtimex, arp, arping, ash, awk, basename, beep, blkid, bootchartd, brctl, bunzip2, bzcat, bzip2, cal, cat, catv, chat, chattr, chgrp, chmod, chown, chpasswd, chpst, chroot, chrt,
        chvt, cksum, clear, cmp, comm, cp, cpio, crond, crontab, cryptpw, cttyhack, cut, date, dc, dd, deallocvt,
        delgroup, deluser, depmod, devmem, df, dhcprelay, diff, dirname, dmesg, dnsd, dnsdomainname, dos2unix, du,
        dumpkmap, dumpleases, echo, ed, egrep, eject, env, envdir, envuidgid, ether-wake, expand, expr, fakeidentd,
        false, fbset, fbsplash, fdflush, fdformat, fdisk, fgconsole, fgrep, find, findfs, flock, fold, free,
        freeramdisk, fsck, fsck.minix, fsync, ftpd, ftpget, ftpput, fuser, getopt, getty, grep, gunzip, gzip, halt,
        hd, hdparm, head, hexdump, hostid, hostname, httpd, hush, hwclock, id, ifconfig, ifdown, ifenslave, ifplugd,
        ifup, inetd, init, insmod, install, ionice, ip, ipaddr, ipcalc, ipcrm, ipcs, iplink, iproute, iprule,
        iptunnel, kbd_mode, kill, killall, killall5, klogd, last, length, less, linux32, linux64, linuxrc, ln,
        loadfont, loadkmap, logger, login, logname, logread, losetup, lpd, lpq, lpr, ls, lsattr, lsmod, lspci, lsusb,
        lzcat, lzma, lzop, lzopcat, makedevs, makemime, man, md5sum, mdev, mesg, microcom, mkdir, mkdosfs, mke2fs,
        mkfifo, mkfs.ext2, mkfs.minix, mkfs.vfat, mknod, mkpasswd, mkswap, mktemp, modinfo, modprobe, more, mount,
        mountpoint, mt, mv, nameif, nc, netstat, nice, nmeter, nohup, nslookup, ntpd, od, openvt, passwd, patch,
        pgrep, pidof, ping, ping6, pipe_progress, pivot_root, pkill, popmaildir, poweroff, printenv, printf, ps,
        pscan, pwd, raidautorun, rdate, rdev, readahead, readlink, readprofile, realpath, reboot, reformime, renice,
        reset, resize, rev, rm, rmdir, rmmod, route, rpm, rpm2cpio, rtcwake, run-parts, runlevel, runsv, runsvdir,
        rx, script, scriptreplay, sed, sendmail, seq, setarch, setconsole, setfont, setkeycodes, setlogcons, setsid,
        setuidgid, sh, sha1sum, sha256sum, sha512sum, showkey, slattach, sleep, smemcap, softlimit, sort, split,
        start-stop-daemon, stat, strings, stty, su, sulogin, sum, sv, svlogd, swapoff, swapon, switch_root, sync,
        sysctl, syslogd, tac, tail, tar, tcpsvd, tee, telnet, telnetd, test, tftp, tftpd, time, timeout, top, touch,
        tr, traceroute, traceroute6, true, tty, ttysize, tunctl, udhcpc, udhcpd, udpsvd, umount, uname, unexpand,
        uniq, unix2dos, unlzma, unlzop, unxz, unzip, uptime, usleep, uudecode, uuencode, vconfig, vi, vlock, volname,
        wall, watch, watchdog, wc, wget, which, who, whoami, xargs, xz, xzcat, yes, zcat, zcip
Comment 1 Denys Vlasenko 2010-10-02 10:41:48 UTC
Proposed fix:

        } else if (lines_before > 0) {
+               if (lines_before > INT_MAX / sizeof(long long))
+                       lines_before = INT_MAX / sizeof(long long);
+               /* overflow in (lines_before * sizeof(x)) is prevented (above) */
                before_buf = xzalloc(lines_before * sizeof(before_buf[0]));
                IF_EXTRA_COMPAT(before_buf_size = xzalloc(lines_before * sizeof(before_buf_size[0]));)
        }

I am committing it to git.
Comment 2 Denys Vlasenko 2010-10-03 21:50:47 UTC
Fixed in git, will be in 1.18.x