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
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.
Fixed in git, will be in 1.18.x