From 596b078da777fa1b066d57366803a13855a0c652 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 21 Mar 2016 14:45:08 -0400 Subject: sys-apps/busybox: version bump to 1.24.2 #577610 --- sys-apps/busybox/Manifest | 1 + sys-apps/busybox/busybox-1.24.2.ebuild | 305 +++++++++++++++++++++ .../files/busybox-1.24.2-CVE-2016-2147.patch | 72 +++++ .../files/busybox-1.24.2-CVE-2016-2148.patch | 55 ++++ .../busybox-1.24.2-ash-recursive-heredocs.patch | 83 ++++++ 5 files changed, 516 insertions(+) create mode 100644 sys-apps/busybox/busybox-1.24.2.ebuild create mode 100644 sys-apps/busybox/files/busybox-1.24.2-CVE-2016-2147.patch create mode 100644 sys-apps/busybox/files/busybox-1.24.2-CVE-2016-2148.patch create mode 100644 sys-apps/busybox/files/busybox-1.24.2-ash-recursive-heredocs.patch (limited to 'sys-apps/busybox') diff --git a/sys-apps/busybox/Manifest b/sys-apps/busybox/Manifest index b8590e591450..f3487c105248 100644 --- a/sys-apps/busybox/Manifest +++ b/sys-apps/busybox/Manifest @@ -2,3 +2,4 @@ DIST busybox-1.21.0.tar.bz2 2200841 SHA256 eb9d268627783297f5f459cb9bd61a94e395d DIST busybox-1.23.1.tar.bz2 2252635 SHA256 300f1db0a7ca4ecee8f8d8027aba250b903372e8339b7d9123d37c1e900473bf SHA512 60849c220dde596c4197f16dd844573b24dd46c8544345a2d5a2b1976fa0ac340d22fbc97f5a1437b7de1c04f4e16aa07b3d62bc77eb83b2467582a50ed4b362 WHIRLPOOL 7cd25e42e74663849b094df699a72deaf02b1088064a511341f76aaa419c936557bbafd54523c6818082dfd4e17605f06ee51abed238ef57a56a9be12c910f99 DIST busybox-1.23.2.tar.bz2 2252786 SHA256 05a6f9e21aad8c098e388ae77de7b2361941afa7157ef74216703395b14e319a SHA512 209c8ef26e40ccb81510f6b663202b080f9bbecac7faf386bbabf7e36a43d63b15dd6ce9f7a84c1ccc5345c524999812251da1e113ef9faadc6af1fedd24c7c9 WHIRLPOOL a0396f7f49ae702f1617e72d5d4646aceec4eba67219c7125bd8d0926d1acb1ef41ea15a7406c3cf5e5e0d8925cc75910ed5128e5fbdd257b80e2cb4f7a048a2 DIST busybox-1.24.1.tar.bz2 2068803 SHA256 37d03132cc078937360b392170b7a1d0e5b322eee9f57c0b82292a8b1f0afe3d SHA512 3afc757ebaae61ae13c2c69097ee734717434f9e658eb77093a8b7b49af3326cbca2d723483ff84a1da99544b822fd2b47d9a97c68f09962e11754e5daf124ca WHIRLPOOL 5e827d08d737caac832e7e5923da624094ff27ee2edbb46dadc339e95edba65378a8fa3db412682724476a2092eee41a804f8f36c2eec0b9f883f5ba855f3ad0 +DIST busybox-1.24.2.tar.bz2 2066822 SHA256 e71ef53ec656f31c42633918d301405d40dea1d97eca12f272217ae4a971c855 SHA512 4d20fb68ee440be2855231c7fd5f3cb9dd9bfcc1a688f0b59cd3f7a55c8819e9cc44bd15f91500713571f2a84e5e44adc0fa8ae0ae3ebf63961dfc9e1c9ef8e0 WHIRLPOOL 2d89e3fded8d61567873acdb1e1e21888a4447e19c1b893543bbe82de52e8e3cf091adb10f34aa155da573994d89ae42f40ecce65ec0d32a641d103c8d8ce053 diff --git a/sys-apps/busybox/busybox-1.24.2.ebuild b/sys-apps/busybox/busybox-1.24.2.ebuild new file mode 100644 index 000000000000..ade4b525c0ae --- /dev/null +++ b/sys-apps/busybox/busybox-1.24.2.ebuild @@ -0,0 +1,305 @@ +# Copyright 1999-2015 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id$ + +# See `man savedconfig.eclass` for info on how to use USE=savedconfig. + +EAPI="4" +inherit eutils flag-o-matic savedconfig toolchain-funcs multilib + +DESCRIPTION="Utilities for rescue and embedded systems" +HOMEPAGE="http://www.busybox.net/" +if [[ ${PV} == "9999" ]] ; then + MY_P=${PN} + EGIT_REPO_URI="git://busybox.net/busybox.git" + inherit git-2 +else + MY_P=${PN}-${PV/_/-} + SRC_URI="http://www.busybox.net/downloads/${MY_P}.tar.bz2" + KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~ia64 ~m68k ~mips ~ppc ~ppc64 ~s390 ~sh ~sparc ~x86 ~amd64-linux ~arm-linux ~x86-linux" +fi + +LICENSE="GPL-2" # GPL-2 only +SLOT="0" +IUSE="debug ipv6 livecd make-symlinks math mdev pam selinux sep-usr static syslog systemd" +RESTRICT="test" + +COMMON_DEPEND="!static? ( selinux? ( sys-libs/libselinux ) ) + pam? ( sys-libs/pam )" +DEPEND="${COMMON_DEPEND} + static? ( selinux? ( sys-libs/libselinux[static-libs(+)] ) ) + >=sys-kernel/linux-headers-2.6.39" +RDEPEND="${COMMON_DEPEND} + mdev? ( !.*set:CONFIG_$1=y:g" .config + else + sed -i -e "s:CONFIG_$1=y:# CONFIG_$1 is not set:g" .config + fi + einfo $(grep "CONFIG_$1[= ]" .config || echo Could not find CONFIG_$1 ...) + shift + done +} + +busybox_config_enabled() { + local val=$(sed -n "/^CONFIG_$1=/s:^[^=]*=::p" .config) + case ${val} in + "") return 1 ;; + y) return 0 ;; + *) echo "${val}" | sed -r 's:^"(.*)"$:\1:' ;; + esac +} + +src_prepare() { + unset KBUILD_OUTPUT #88088 + append-flags -fno-strict-aliasing #310413 + use ppc64 && append-flags -mminimal-toc #130943 + + # patches go here! + epatch "${FILESDIR}"/${PN}-1.19.0-bb.patch + epatch "${FILESDIR}"/busybox-1.24.1-trylink-ldflags.patch + epatch "${FILESDIR}"/busybox-1.24.2-ash-recursive-heredocs.patch + epatch "${FILESDIR}"/busybox-1.24.2-CVE-2016-2147.patch + epatch "${FILESDIR}"/busybox-1.24.2-CVE-2016-2148.patch + cp "${FILESDIR}"/ginit.c init/ || die + + # flag cleanup + sed -i -r \ + -e 's:[[:space:]]?-(Werror|Os|falign-(functions|jumps|loops|labels)=1|fomit-frame-pointer)\>::g' \ + Makefile.flags || die + #sed -i '/bbsh/s:^//::' include/applets.h + sed -i '/^#error Aborting compilation./d' applets/applets.c || die + use elibc_glibc && sed -i 's:-Wl,--gc-sections::' Makefile + sed -i \ + -e "/^CROSS_COMPILE/s:=.*:= ${CHOST}-:" \ + -e "/^AR\>/s:=.*:= $(tc-getAR):" \ + -e "/^CC\>/s:=.*:= $(tc-getCC):" \ + -e "/^HOSTCC/s:=.*:= $(tc-getBUILD_CC):" \ + -e "/^PKG_CONFIG\>/s:=.*:= $(tc-getPKG_CONFIG):" \ + Makefile || die + sed -i \ + -e 's:-static-libgcc::' \ + Makefile.flags || die +} + +src_configure() { + # check for a busybox config before making one of our own. + # if one exist lets return and use it. + + restore_config .config + if [ -f .config ]; then + yes "" | emake -j1 -s oldconfig >/dev/null + return 0 + else + ewarn "Could not locate user configfile, so we will save a default one" + fi + + # setup the config file + emake -j1 -s allyesconfig >/dev/null + # nommu forces a bunch of things off which we want on #387555 + busybox_config_option n NOMMU + sed -i '/^#/d' .config + yes "" | emake -j1 -s oldconfig >/dev/null + + # now turn off stuff we really don't want + busybox_config_option n DMALLOC + busybox_config_option n FEATURE_SUID_CONFIG + busybox_config_option n BUILD_AT_ONCE + busybox_config_option n BUILD_LIBBUSYBOX + busybox_config_option n FEATURE_CLEAN_UP + busybox_config_option n MONOTONIC_SYSCALL + busybox_config_option n USE_PORTABLE_CODE + busybox_config_option n WERROR + + # If these are not set and we are using a uclibc/busybox setup + # all calls to system() will fail. + busybox_config_option y ASH + busybox_config_option n HUSH + + # disable ipv6 applets + if ! use ipv6; then + busybox_config_option n FEATURE_IPV6 + busybox_config_option n TRACEROUTE6 + busybox_config_option n PING6 + busybox_config_option n UDHCPC6 + fi + + if use static && use pam ; then + ewarn "You cannot have USE='static pam'. Assuming static is more important." + fi + busybox_config_option $(usex static n pam) PAM + busybox_config_option static STATIC + busybox_config_option syslog {K,SYS}LOGD LOGGER + busybox_config_option systemd FEATURE_SYSTEMD + busybox_config_option math FEATURE_AWK_LIBM + + # all the debug options are compiler related, so punt them + busybox_config_option n DEBUG + busybox_config_option y NO_DEBUG_LIB + busybox_config_option n DMALLOC + busybox_config_option n EFENCE + busybox_config_option $(usex debug y n) TFTP_DEBUG + + busybox_config_option selinux SELINUX + + # this opt only controls mounting with /dev/null +} + +src_compile() { + unset KBUILD_OUTPUT #88088 + export SKIP_STRIP=y + + emake V=1 busybox +} + +src_install() { + unset KBUILD_OUTPUT #88088 + save_config .config + + into / + dodir /bin + if use sep-usr ; then + # install /ginit to take care of mounting stuff + exeinto / + newexe busybox_unstripped ginit + dosym /ginit /bin/bb + dosym bb /bin/busybox + else + newbin busybox_unstripped busybox + dosym busybox /bin/bb + fi + if use mdev ; then + dodir /$(get_libdir)/mdev/ + use make-symlinks || dosym /bin/bb /sbin/mdev + cp "${S}"/examples/mdev_fat.conf "${ED}"/etc/mdev.conf + + exeinto /$(get_libdir)/mdev/ + doexe "${FILESDIR}"/mdev/* + + newinitd "${FILESDIR}"/mdev.initd mdev + fi + if use livecd ; then + dosym busybox /bin/vi + fi + + # add busybox daemon's, bug #444718 + if busybox_config_enabled FEATURE_NTPD_SERVER; then + newconfd "${FILESDIR}/ntpd.confd" "busybox-ntpd" + newinitd "${FILESDIR}/ntpd.initd" "busybox-ntpd" + fi + if busybox_config_enabled SYSLOGD; then + newconfd "${FILESDIR}/syslogd.confd" "busybox-syslogd" + newinitd "${FILESDIR}/syslogd.initd" "busybox-syslogd" + fi + if busybox_config_enabled KLOGD; then + newconfd "${FILESDIR}/klogd.confd" "busybox-klogd" + newinitd "${FILESDIR}/klogd.initd" "busybox-klogd" + fi + if busybox_config_enabled WATCHDOG; then + newconfd "${FILESDIR}/watchdog.confd" "busybox-watchdog" + newinitd "${FILESDIR}/watchdog.initd" "busybox-watchdog" + fi + if busybox_config_enabled UDHCPC; then + local path=$(busybox_config_enabled UDHCPC_DEFAULT_SCRIPT) + exeinto "${path%/*}" + newexe examples/udhcp/simple.script "${path##*/}" + fi + if busybox_config_enabled UDHCPD; then + insinto /etc + doins examples/udhcp/udhcpd.conf + fi + + # bundle up the symlink files for use later + emake DESTDIR="${ED}" install + rm _install/bin/busybox + # for compatibility, provide /usr/bin/env + mkdir -p _install/usr/bin + ln -s /bin/env _install/usr/bin/env + tar cf busybox-links.tar -C _install . || : #;die + insinto /usr/share/${PN} + use make-symlinks && doins busybox-links.tar + + dodoc AUTHORS README TODO + + cd docs + docinto txt + dodoc *.txt + docinto pod + dodoc *.pod + dohtml *.html + + cd ../examples + docinto examples + dodoc inittab depmod.pl *.conf *.script undeb unrpm +} + +pkg_preinst() { + if use make-symlinks && [[ ! ${VERY_BRAVE_OR_VERY_DUMB} == "yes" ]] && [[ ${ROOT} == "/" ]] ; then + ewarn "setting USE=make-symlinks and emerging to / is very dangerous." + ewarn "it WILL overwrite lots of system programs like: ls bash awk grep (bug 60805 for full list)." + ewarn "If you are creating a binary only and not merging this is probably ok." + ewarn "set env VERY_BRAVE_OR_VERY_DUMB=yes if this is really what you want." + die "silly options will destroy your system" + fi + + if use make-symlinks ; then + mv "${ED}"/usr/share/${PN}/busybox-links.tar "${T}"/ || die + fi +} + +pkg_postinst() { + savedconfig_pkg_postinst + + if use make-symlinks ; then + cd "${T}" || die + mkdir _install + tar xf busybox-links.tar -C _install || die + cp -vpPR _install/* "${ROOT}"/ || die "copying links for ${x} failed" + fi + + if use sep-usr ; then + elog "In order to use the sep-usr support, you have to update your" + elog "kernel command line. Add the option:" + elog " init=/ginit" + elog "To launch a different init than /sbin/init, use:" + elog " init=/ginit /sbin/yourinit" + elog "To get a rescue shell, you may boot with:" + elog " init=/ginit bb" + fi +} diff --git a/sys-apps/busybox/files/busybox-1.24.2-CVE-2016-2147.patch b/sys-apps/busybox/files/busybox-1.24.2-CVE-2016-2147.patch new file mode 100644 index 000000000000..2187c9b6732c --- /dev/null +++ b/sys-apps/busybox/files/busybox-1.24.2-CVE-2016-2147.patch @@ -0,0 +1,72 @@ +From 3c4de6e36c4d387a648622e7b828a05f2b1b47e6 Mon Sep 17 00:00:00 2001 +From: Denys Vlasenko +Date: Fri, 26 Feb 2016 15:54:56 +0100 +Subject: [PATCH] udhcpc: fix OPTION_6RD parsing (could overflow its malloced + buffer) + +Signed-off-by: Denys Vlasenko +Signed-off-by: Mike Frysinger +(cherry picked from commit 352f79acbd759c14399e39baef21fc4ffe180ac2) +--- + networking/udhcp/common.c | 15 +++++++++++++-- + networking/udhcp/dhcpc.c | 4 ++-- + 2 files changed, 15 insertions(+), 4 deletions(-) + +diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c +index bc41c8d..680852c 100644 +--- a/networking/udhcp/common.c ++++ b/networking/udhcp/common.c +@@ -142,7 +142,7 @@ const char dhcp_option_strings[] ALIGN1 = + * udhcp_str2optset: to determine how many bytes to allocate. + * xmalloc_optname_optval: to estimate string length + * from binary option length: (option[LEN] / dhcp_option_lengths[opt_type]) +- * is the number of elements, multiply in by one element's string width ++ * is the number of elements, multiply it by one element's string width + * (len_of_option_as_string[opt_type]) and you know how wide string you need. + */ + const uint8_t dhcp_option_lengths[] ALIGN1 = { +@@ -162,7 +162,18 @@ const uint8_t dhcp_option_lengths[] ALIGN1 = { + [OPTION_S32] = 4, + /* Just like OPTION_STRING, we use minimum length here */ + [OPTION_STATIC_ROUTES] = 5, +- [OPTION_6RD] = 22, /* ignored by udhcp_str2optset */ ++ [OPTION_6RD] = 12, /* ignored by udhcp_str2optset */ ++ /* The above value was chosen as follows: ++ * len_of_option_as_string[] for this option is >60: it's a string of the form ++ * "32 128 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 255.255.255.255 ". ++ * Each additional ipv4 address takes 4 bytes in binary option and appends ++ * another "255.255.255.255 " 16-byte string. We can set [OPTION_6RD] = 4 ++ * but this severely overestimates string length: instead of 16 bytes, ++ * it adds >60 for every 4 bytes in binary option. ++ * We cheat and declare here that option is in units of 12 bytes. ++ * This adds more than 60 bytes for every three ipv4 addresses - more than enough. ++ * (Even 16 instead of 12 should work, but let's be paranoid). ++ */ + }; + + +diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c +index 915f659..2332b57 100644 +--- a/networking/udhcp/dhcpc.c ++++ b/networking/udhcp/dhcpc.c +@@ -113,7 +113,7 @@ static const uint8_t len_of_option_as_string[] = { + [OPTION_IP ] = sizeof("255.255.255.255 "), + [OPTION_IP_PAIR ] = sizeof("255.255.255.255 ") * 2, + [OPTION_STATIC_ROUTES ] = sizeof("255.255.255.255/32 255.255.255.255 "), +- [OPTION_6RD ] = sizeof("32 128 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 255.255.255.255 "), ++ [OPTION_6RD ] = sizeof("132 128 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 255.255.255.255 "), + [OPTION_STRING ] = 1, + [OPTION_STRING_HOST ] = 1, + #if ENABLE_FEATURE_UDHCP_RFC3397 +@@ -220,7 +220,7 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_ + type = optflag->flags & OPTION_TYPE_MASK; + optlen = dhcp_option_lengths[type]; + upper_length = len_of_option_as_string[type] +- * ((unsigned)(len + optlen - 1) / (unsigned)optlen); ++ * ((unsigned)(len + optlen) / (unsigned)optlen); + + dest = ret = xmalloc(upper_length + strlen(opt_name) + 2); + dest += sprintf(ret, "%s=", opt_name); +-- +2.7.4 + diff --git a/sys-apps/busybox/files/busybox-1.24.2-CVE-2016-2148.patch b/sys-apps/busybox/files/busybox-1.24.2-CVE-2016-2148.patch new file mode 100644 index 000000000000..08e08bec173d --- /dev/null +++ b/sys-apps/busybox/files/busybox-1.24.2-CVE-2016-2148.patch @@ -0,0 +1,55 @@ +From 3a76bb5136d05f94ee62e377aa723e63444912c7 Mon Sep 17 00:00:00 2001 +From: Denys Vlasenko +Date: Thu, 10 Mar 2016 11:47:58 +0100 +Subject: [PATCH] udhcp: fix a SEGV on malformed RFC1035-encoded domain name + +Signed-off-by: Denys Vlasenko +Signed-off-by: Mike Frysinger +(cherry picked from commit d474ffc68290e0a83651c4432eeabfa62cd51e87) +--- + networking/udhcp/domain_codec.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/networking/udhcp/domain_codec.c b/networking/udhcp/domain_codec.c +index c1325d8..8429367 100644 +--- a/networking/udhcp/domain_codec.c ++++ b/networking/udhcp/domain_codec.c +@@ -63,11 +63,10 @@ char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre) + if (crtpos + *c + 1 > clen) /* label too long? abort */ + return NULL; + if (dst) +- memcpy(dst + len, c + 1, *c); ++ /* \3com ---> "com." */ ++ ((char*)mempcpy(dst + len, c + 1, *c))[0] = '.'; + len += *c + 1; + crtpos += *c + 1; +- if (dst) +- dst[len - 1] = '.'; + } else { + /* NUL: end of current domain name */ + if (retpos == 0) { +@@ -78,7 +77,10 @@ char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre) + crtpos = retpos; + retpos = depth = 0; + } +- if (dst) ++ if (dst && len != 0) ++ /* \4host\3com\0\4host and we are at \0: ++ * \3com was converted to "com.", change dot to space. ++ */ + dst[len - 1] = ' '; + } + +@@ -228,6 +230,9 @@ int main(int argc, char **argv) + int len; + uint8_t *encoded; + ++ uint8_t str[6] = { 0x00, 0x00, 0x02, 0x65, 0x65, 0x00 }; ++ printf("NUL:'%s'\n", dname_dec(str, 6, "")); ++ + #define DNAME_DEC(encoded,pre) dname_dec((uint8_t*)(encoded), sizeof(encoded), (pre)) + printf("'%s'\n", DNAME_DEC("\4host\3com\0", "test1:")); + printf("test2:'%s'\n", DNAME_DEC("\4host\3com\0\4host\3com\0", "")); +-- +2.7.4 + diff --git a/sys-apps/busybox/files/busybox-1.24.2-ash-recursive-heredocs.patch b/sys-apps/busybox/files/busybox-1.24.2-ash-recursive-heredocs.patch new file mode 100644 index 000000000000..5405eafeaa9e --- /dev/null +++ b/sys-apps/busybox/files/busybox-1.24.2-ash-recursive-heredocs.patch @@ -0,0 +1,83 @@ +From 4194c2875310c13ee3ca2bb0e1aea6a2ae67c55a Mon Sep 17 00:00:00 2001 +From: Ron Yorston +Date: Thu, 29 Oct 2015 16:44:56 +0000 +Subject: [PATCH] ash: fix error during recursive processing of here document + +Save the value of the checkkwd flag to prevent it being clobbered +during recursion. + +Based on commit ec2c84d from git://git.kernel.org/pub/scm/utils/dash/dash.git +by Herbert Xu. + +function old new delta +readtoken 190 203 +13 +------------------------------------------------------------------------------ +(add/remove: 0/0 grow/shrink: 1/0 up/down: 13/0) Total: 13 bytes + +Signed-off-by: Ron Yorston +Signed-off-by: Denys Vlasenko +Signed-off-by: Mike Frysinger +(cherry picked from commit 713f07d906d9171953be0c12e2369869855b6ca6) +--- + shell/ash.c | 5 +++-- + shell/ash_test/ash-heredoc/heredoc3.right | 1 + + shell/ash_test/ash-heredoc/heredoc3.tests | 9 +++++++++ + 3 files changed, 13 insertions(+), 2 deletions(-) + create mode 100644 shell/ash_test/ash-heredoc/heredoc3.right + create mode 100755 shell/ash_test/ash-heredoc/heredoc3.tests + +diff --git a/shell/ash.c b/shell/ash.c +index 8a1628e..256e933 100644 +--- a/shell/ash.c ++++ b/shell/ash.c +@@ -11893,6 +11893,7 @@ static int + readtoken(void) + { + int t; ++ int kwd = checkkwd; + #if DEBUG + smallint alreadyseen = tokpushback; + #endif +@@ -11906,7 +11907,7 @@ readtoken(void) + /* + * eat newlines + */ +- if (checkkwd & CHKNL) { ++ if (kwd & CHKNL) { + while (t == TNL) { + parseheredoc(); + t = xxreadtoken(); +@@ -11920,7 +11921,7 @@ readtoken(void) + /* + * check for keywords + */ +- if (checkkwd & CHKKWD) { ++ if (kwd & CHKKWD) { + const char *const *pp; + + pp = findkwd(wordtext); +diff --git a/shell/ash_test/ash-heredoc/heredoc3.right b/shell/ash_test/ash-heredoc/heredoc3.right +new file mode 100644 +index 0000000..ce01362 +--- /dev/null ++++ b/shell/ash_test/ash-heredoc/heredoc3.right +@@ -0,0 +1 @@ ++hello +diff --git a/shell/ash_test/ash-heredoc/heredoc3.tests b/shell/ash_test/ash-heredoc/heredoc3.tests +new file mode 100755 +index 0000000..96c227c +--- /dev/null ++++ b/shell/ash_test/ash-heredoc/heredoc3.tests +@@ -0,0 +1,9 @@ ++echo hello >greeting ++cat </dev/null ++rm greeting +-- +2.7.4 + -- cgit v1.2.3-65-gdbad