summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2016-11-27 15:09:53 +0000
committerPádraig Brady <P@draigBrady.com>2016-11-28 13:19:24 +0000
commita39641cbb8f37c5a19dd4820c6f6719c82d3e633 (patch)
treeee662cfc8add8265c1de488a9d281eba21c9d8a4
parent6f30a99fa537adb029283cf2ef03cb4419350e6c (diff)
downloadcoreutils-a39641cbb8f37c5a19dd4820c6f6719c82d3e633.tar.xz
tac: fix mem corruption when failing to read non seekable inputs
This was detected with ASAN, but can also be seen without ASAN with: $ tac - - <&- tac: standard input: read error: Bad file descriptor *** Error in `tac': malloc(): memory corruption: 0x... * src/tac.c (copy_to_temp): Don't close our output stream on (possibly transient) output error, or on input error. (temp_stream): clearerr() on the stream about to be reused, to ensure future stream use is not impacted by transient errors. * tests/misc/tac-2-nonseekable.sh: Add a test case. * NEWS: Mention the bug fix. Fixes http://bugs.gnu.org/25041
-rw-r--r--NEWS3
-rw-r--r--src/tac.c11
-rwxr-xr-xtests/misc/tac-2-nonseekable.sh3
3 files changed, 10 insertions, 7 deletions
diff --git a/NEWS b/NEWS
index 2e3096822..6f7505f82 100644
--- a/NEWS
+++ b/NEWS
@@ -50,6 +50,9 @@ GNU coreutils NEWS -*- outline -*-
seq now immediately exits upon write errors.
[This bug was present in "the beginning".]
+ tac no longer crashes when there are issues reading from non-seekable inputs.
+ [bug introduced in coreutils-8.15]
+
tail -F now continues to process initially untailable files that are replaced
by a tailable file. This was handled correctly when inotify was available,
and is now handled correctly in all cases.
diff --git a/src/tac.c b/src/tac.c
index 2e820fa07..c1b6003da 100644
--- a/src/tac.c
+++ b/src/tac.c
@@ -477,6 +477,7 @@ temp_stream (FILE **fp, char **file_name)
}
else
{
+ clearerr (tmp_fp);
if (fseeko (tmp_fp, 0, SEEK_SET) < 0
|| ftruncate (fileno (tmp_fp), 0) < 0)
{
@@ -512,13 +513,13 @@ copy_to_temp (FILE **g_tmp, char **g_tempfile, int input_fd, char const *file)
if (bytes_read == SAFE_READ_ERROR)
{
error (0, errno, _("%s: read error"), quotef (file));
- goto Fail;
+ return -1;
}
if (fwrite (G_buffer, 1, bytes_read, fp) != bytes_read)
{
error (0, errno, _("%s: write error"), quotef (file_name));
- goto Fail;
+ return -1;
}
/* Implicitly <= OFF_T_MAX due to preceding fwrite(),
@@ -530,16 +531,12 @@ copy_to_temp (FILE **g_tmp, char **g_tempfile, int input_fd, char const *file)
if (fflush (fp) != 0)
{
error (0, errno, _("%s: write error"), quotef (file_name));
- goto Fail;
+ return -1;
}
*g_tmp = fp;
*g_tempfile = file_name;
return bytes_copied;
-
- Fail:
- fclose (fp);
- return -1;
}
/* Copy INPUT_FD to a temporary, then tac that file.
diff --git a/tests/misc/tac-2-nonseekable.sh b/tests/misc/tac-2-nonseekable.sh
index 47e7849ad..d148218c1 100755
--- a/tests/misc/tac-2-nonseekable.sh
+++ b/tests/misc/tac-2-nonseekable.sh
@@ -36,4 +36,7 @@ for file in /proc/version /sys/kernel/profiling; do
fi
done
+# This failed due to heap corruption from v8.15-v8.25 inclusive.
+returns_ 1 tac - - <&- 2>err || fail=1
+
Exit $fail