Ondřej Vašík 2a672b
From b3959fc691e606857a3c6e9b316ec34819972245 Mon Sep 17 00:00:00 2001
Ondřej Vašík 2a672b
From: Jim Meyering <meyering@redhat.com>
Ondřej Vašík 2a672b
Date: Sat, 28 Aug 2010 17:45:29 +0200
Ondřej Vašík 2a672b
Subject: [PATCH] tac: avoid double free
Ondřej Vašík 2a672b
Ondřej Vašík 2a672b
* src/tac.c (main): Reading a line longer than 16KiB would cause
Ondřej Vašík 2a672b
tac to realloc its primary buffer.  Then, just before exit, tac
Ondřej Vašík 2a672b
would mistakenly free the original (now free'd) buffer.
Ondřej Vašík 2a672b
This bug was introduced by commit be6c13e7, "maint: always free a
Ondřej Vašík 2a672b
buffer, to avoid even semblance of a leak".
Ondřej Vašík 2a672b
* tests/misc/tac (double-free): New test, to exercise this.
Ondřej Vašík 2a672b
Reported by Salvo Tomaselli in <http://bugs.debian.org/594666>.
Ondřej Vašík 2a672b
---
Ondřej Vašík 2a672b
 src/tac.c      |    6 ++++--
Ondřej Vašík 2a672b
 tests/misc/tac |    6 ++++++
Ondřej Vašík 28c58f
 2 files changed, 10 insertions(+), 2 deletions(-)
Ondřej Vašík 2a672b
Ondřej Vašík 2a672b
diff --git a/src/tac.c b/src/tac.c
Ondřej Vašík 2a672b
index cec9736..859e006 100644
Ondřej Vašík 2a672b
--- a/src/tac.c
Ondřej Vašík 2a672b
+++ b/src/tac.c
Ondřej Vašík 2a672b
@@ -633,7 +633,6 @@ main (int argc, char **argv)
Ondřej Vašík 2a672b
   if (! (read_size < half_buffer_size && half_buffer_size < G_buffer_size))
Ondřej Vašík 2a672b
     xalloc_die ();
Ondřej Vašík 2a672b
   G_buffer = xmalloc (G_buffer_size);
Ondřej Vašík 2a672b
-  void *buf = G_buffer;
Ondřej Vašík 2a672b
   if (sentinel_length)
Ondřej Vašík 2a672b
     {
Ondřej Vašík 2a672b
       strcpy (G_buffer, separator);
Ondřej Vašík 2a672b
@@ -666,6 +665,9 @@ main (int argc, char **argv)
Ondřej Vašík 2a672b
       error (0, errno, "-");
Ondřej Vašík 2a672b
       ok = false;
Ondřej Vašík 2a672b
     }
Ondřej Vašík 2a672b
-  free (buf);
Ondřej Vašík 2a672b
+
Ondřej Vašík 2a672b
+  size_t offset = sentinel_length ? sentinel_length : 1;
Ondřej Vašík 2a672b
+  free (G_buffer - offset);
Ondřej Vašík 2a672b
+
Ondřej Vašík 2a672b
   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
Ondřej Vašík 2a672b
 }
Ondřej Vašík 2a672b
diff --git a/tests/misc/tac b/tests/misc/tac
Ondřej Vašík 2a672b
index 7631049..4130c00 100755
Ondřej Vašík 2a672b
--- a/tests/misc/tac
Ondřej Vašík 2a672b
+++ b/tests/misc/tac
Ondřej Vašík 2a672b
@@ -24,6 +24,9 @@ my $prog = 'tac';
Ondřej Vašík 2a672b
Ondřej Vašík 2a672b
 my $bad_dir = 'no/such/dir';
Ondřej Vašík 2a672b
Ondřej Vašík 2a672b
+# This must be longer than 16KiB to trigger the double free in coreutils-8.5.
Ondřej Vašík 2a672b
+my $long_line = 'o' x (16 * 1024 + 1);
Ondřej Vašík 2a672b
+
Ondřej Vašík 2a672b
 my @Tests =
Ondřej Vašík 2a672b
 (
Ondřej Vašík 2a672b
   ['segfault', '-r', {IN=>"a\n"}, {IN=>"b\n"}, {OUT=>"a\nb\n"}],
Ondřej Vašík 2a672b
@@ -67,6 +70,9 @@ my @Tests =
Ondřej Vašík 2a672b
    {ERR_SUBST => "s,`$bad_dir': .*,...,"},
Ondřej Vašík 2a672b
    {ERR => "$prog: cannot create temporary file in ...\n"},
Ondřej Vašík 2a672b
    {EXIT => 1}],
Ondřej Vašík 2a672b
+
Ondřej Vašík 2a672b
+  # coreutils-8.5's tac would double-free its primary buffer.
Ondřej Vašík 2a672b
+  ['double-free', {IN=>$long_line}, {OUT=>$long_line}],
Ondřej Vašík 2a672b
 );
Ondřej Vašík 2a672b
Ondřej Vašík 2a672b
 @Tests = triple_test \@Tests;
Ondřej Vašík 2a672b
--
Ondřej Vašík 2a672b
1.7.2.2.510.g7180a