diff --git a/backport-head-fix-overflows-in-elide_tail_bytes_pipe.patch b/backport-head-fix-overflows-in-elide_tail_bytes_pipe.patch new file mode 100644 index 0000000000000000000000000000000000000000..e1e39cb9d1dfde4373f8ecddb99e53134ed9b4cb --- /dev/null +++ b/backport-head-fix-overflows-in-elide_tail_bytes_pipe.patch @@ -0,0 +1,256 @@ +From 8fe800a06e50be3c905ab1694a2d1bfd6e70be42 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Sat, 10 Aug 2024 22:19:17 -0700 +Subject: [PATCH] head: fix overflows in elide_tail_bytes_pipe +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Not clear that the overflows could be exploited, +but they made the code confusing. +* src/head.c (elide_tail_bytes_pipe): Don’t convert uintmax_t +to size_t first thing; wait until it’s known the value will fit, +and then use idx_t rather than size_t to prefer signed types. +Prefer idx_t in nearby code, too. +Rename locals n_elide_0 to n_elide (for consistency elsewhere) +and n_elide to in_elide. +Remove bogus (SIZE_MAX < n_elide + READ_BUFSIZE) test; +in the typical case where n_elide’s type was the same as +that of SIZE_MAX, the test never succeeded, and in the +less-common case where n_elide was wider than size_t, +the addition could silently overflow, causing the test +to fail when it should succeed. The test is not needed anyway now. +Add static asserts to document code assumptions. +Redo the ! (n_elide <= HEAD_TAIL_PIPE_BYTECOUNT_THRESHOLD) case +so that it works with enormous values of n_elide even on +32-bit platforms; for example, n_bufs is now uintmax_t not size_t. +Simplify by using xpalloc instead of by-hand code. +Remove bogus ‘if (rem)’ test, as rem is always nonzero. + +Reference:https://github.com/coreutils/gnulib/commit/adb76c754290c328a88438af89e491ece7e6a9c5 +Conflict:Adapt static_assert to verify. Context adaptation. Adapt "char const *filename" to "const char *filename to" + +--- + src/head.c | 129 ++++++++++++++++++++++++----------------------------- + 1 file changed, 58 insertions(+), 71 deletions(-) + +diff --git a/src/head.c b/src/head.c +index 567c2f5..b32b8a4 100644 +--- a/src/head.c ++++ b/src/head.c +@@ -238,17 +238,16 @@ elseek (int fd, off_t offset, int whence, char const *filename) + } + + /* For an input file with name FILENAME and descriptor FD, +- output all but the last N_ELIDE_0 bytes. ++ output all but the last N_ELIDE bytes. + If CURRENT_POS is nonnegative, assume that the input file is + positioned at CURRENT_POS and that it should be repositioned to + just before the elided bytes before returning. + Return true upon success. + Give a diagnostic and return false upon error. */ + static bool +-elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0, ++elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide, + off_t current_pos) + { +- size_t n_elide = n_elide_0; + uintmax_t desired_pos = current_pos; + bool ok = true; + +@@ -266,16 +265,9 @@ elide_tail_bytes_pipe (char const *filename, int fd, uintmax_t n_elide_0, + #endif + + #if HEAD_TAIL_PIPE_BYTECOUNT_THRESHOLD < 2 * READ_BUFSIZE +- "HEAD_TAIL_PIPE_BYTECOUNT_THRESHOLD must be at least 2 * READ_BUFSIZE" ++# error "HEAD_TAIL_PIPE_BYTECOUNT_THRESHOLD must be at least 2 * READ_BUFSIZE" + #endif + +- if (SIZE_MAX < n_elide_0 + READ_BUFSIZE) +- { +- char umax_buf[INT_BUFSIZE_BOUND (n_elide_0)]; +- die (EXIT_FAILURE, 0, _("%s: number of bytes is too large"), +- umaxtostr (n_elide_0, umax_buf)); +- } +- + /* Two cases to consider... + 1) n_elide is small enough that we can afford to double-buffer: + allocate 2 * (READ_BUFSIZE + n_elide) bytes +@@ -287,11 +279,14 @@ elide_tail_bytes_pipe (char const *filename, int fd, uintmax_t n_elide_0, + CAUTION: do not fail (out of memory) when asked to elide + a ridiculous amount, but when given only a small input. */ + ++ verify (READ_BUFSIZE <= IDX_MAX); ++ verify (HEAD_TAIL_PIPE_BYTECOUNT_THRESHOLD <= IDX_MAX - READ_BUFSIZE); + if (n_elide <= HEAD_TAIL_PIPE_BYTECOUNT_THRESHOLD) + { ++ idx_t in_elide = n_elide; + bool first = true; + bool eof = false; +- size_t n_to_read = READ_BUFSIZE + n_elide; ++ size_t n_to_read = READ_BUFSIZE + in_elide; + bool i; + char *b[2]; + b[0] = xnmalloc (2, n_to_read); +@@ -311,7 +306,7 @@ elide_tail_bytes_pipe (char const *filename, int fd, uintmax_t n_elide_0, + } + + /* reached EOF */ +- if (n_read <= n_elide) ++ if (n_read <= in_elide) + { + if (first) + { +@@ -321,7 +316,7 @@ elide_tail_bytes_pipe (char const *filename, int fd, uintmax_t n_elide_0, + } + else + { +- delta = n_elide - n_read; ++ delta = in_elide - n_read; + } + } + eof = true; +@@ -331,15 +326,15 @@ elide_tail_bytes_pipe (char const *filename, int fd, uintmax_t n_elide_0, + the previous round. */ + if (! first) + { +- desired_pos += n_elide - delta; +- xwrite_stdout (b[!i] + READ_BUFSIZE, n_elide - delta); ++ desired_pos += in_elide - delta; ++ xwrite_stdout (b[!i] + READ_BUFSIZE, in_elide - delta); + } + first = false; + +- if (n_elide < n_read) ++ if (in_elide < n_read) + { +- desired_pos += n_read - n_elide; +- xwrite_stdout (b[i], n_read - n_elide); ++ desired_pos += n_read - in_elide; ++ xwrite_stdout (b[i], n_read - in_elide); + } + } + +@@ -351,31 +346,24 @@ elide_tail_bytes_pipe (char const *filename, int fd, uintmax_t n_elide_0, + bytes. Then, for each new buffer we read, also write an old one. */ + + bool eof = false; +- size_t n_read; +- bool buffered_enough; +- size_t i, i_next; ++ idx_t n_read; + char **b = NULL; +- /* Round n_elide up to a multiple of READ_BUFSIZE. */ +- size_t rem = READ_BUFSIZE - (n_elide % READ_BUFSIZE); +- size_t n_elide_round = n_elide + rem; +- size_t n_bufs = n_elide_round / READ_BUFSIZE + 1; +- size_t n_alloc = 0; +- size_t n_array_alloc = 0; +- +- buffered_enough = false; ++ ++ idx_t remainder = n_elide % READ_BUFSIZE; ++ /* The number of buffers needed to hold n_elide bytes plus one ++ extra buffer. They are allocated lazily, so don't report ++ overflow now simply because the number does not fit into idx_t. */ ++ uintmax_t n_bufs = n_elide / READ_BUFSIZE + (remainder != 0) + 1; ++ idx_t n_alloc = 0; ++ idx_t n_array_alloc = 0; ++ ++ bool buffered_enough = false; ++ idx_t i, i_next; + for (i = 0, i_next = 1; !eof; i = i_next, i_next = (i_next + 1) % n_bufs) + { + if (n_array_alloc == i) +- { +- /* reallocate between 16 and n_bufs entries. */ +- if (n_array_alloc == 0) +- n_array_alloc = MIN (n_bufs, 16); +- else if (n_array_alloc <= n_bufs / 2) +- n_array_alloc *= 2; +- else +- n_array_alloc = n_bufs; +- b = xnrealloc (b, n_array_alloc, sizeof *b); +- } ++ b = xpalloc (b, &n_array_alloc, 1, MIN (n_bufs, PTRDIFF_MAX), ++ sizeof *b); + + if (! buffered_enough) + { +@@ -404,43 +392,42 @@ elide_tail_bytes_pipe (char const *filename, int fd, uintmax_t n_elide_0, + } + } + +- /* Output any remainder: rem bytes from b[i] + n_read. */ +- if (rem) ++ /* Output the remainder: rem bytes from b[i] + n_read. */ ++ idx_t rem = READ_BUFSIZE - remainder; ++ if (buffered_enough) + { +- if (buffered_enough) ++ idx_t n_bytes_left_in_b_i = READ_BUFSIZE - n_read; ++ desired_pos += rem; ++ if (rem < n_bytes_left_in_b_i) + { +- size_t n_bytes_left_in_b_i = READ_BUFSIZE - n_read; +- desired_pos += rem; +- if (rem < n_bytes_left_in_b_i) +- { +- xwrite_stdout (b[i] + n_read, rem); +- } +- else +- { +- xwrite_stdout (b[i] + n_read, n_bytes_left_in_b_i); +- xwrite_stdout (b[i_next], rem - n_bytes_left_in_b_i); +- } ++ xwrite_stdout (b[i] + n_read, rem); + } +- else if (i + 1 == n_bufs) ++ else + { +- /* This happens when n_elide < file_size < n_elide_round. +- +- |READ_BUF.| +- | | rem | +- |---------!---------!---------!---------| +- |---- n_elide ---------| +- | | x | +- | |y | +- |---- file size -----------| +- | |n_read| +- |---- n_elide_round ----------| +- */ +- size_t y = READ_BUFSIZE - rem; +- size_t x = n_read - y; +- desired_pos += x; +- xwrite_stdout (b[i_next], x); ++ xwrite_stdout (b[i] + n_read, n_bytes_left_in_b_i); ++ xwrite_stdout (b[i_next], rem - n_bytes_left_in_b_i); + } + } ++ else if (i + 1 == n_bufs) ++ { ++ /* This happens when ++ n_elide < file_size < (n_bufs - 1) * READ_BUFSIZE. ++ ++ |READ_BUF.| ++ | | rem | ++ |---------!---------!---------!---------| ++ |---- n_elide----------| ++ | | x | ++ | |y | ++ |---- file size -----------| ++ | |n_read| ++ |(n_bufs - 1) * READ_BUFSIZE--| ++ */ ++ idx_t y = READ_BUFSIZE - rem; ++ idx_t x = n_read - y; ++ desired_pos += x; ++ xwrite_stdout (b[i_next], x); ++ } + + free_mem: + for (i = 0; i < n_alloc; i++) +-- +2.33.0 + diff --git a/coreutils.spec b/coreutils.spec index b42251f82739f49298f6aadfabe241d92642b877..7465bd2a47243f009dcdbe94a26df6602529bf25 100644 --- a/coreutils.spec +++ b/coreutils.spec @@ -1,6 +1,6 @@ Name: coreutils Version: 8.32 -Release: 10 +Release: 11 License: GPLv3+ Summary: A set of basic GNU tools commonly used in shell scripts Url: https://www.gnu.org/software/coreutils/ @@ -47,6 +47,7 @@ Patch29: backport-tac-handle-short-reads-on-input.patch Patch30: backport-setenv-Don-t-crash-if-malloc-returns-NULL.patch Patch31: backport-who-don-t-crash-if-clock-gyrates.patch Patch32: backport-doc-od-strings-clarify-operation.patch +Patch33: backport-head-fix-overflows-in-elide_tail_bytes_pipe.patch Conflicts: filesystem < 3 # To avoid clobbering installs @@ -165,6 +166,10 @@ fi %{_mandir}/man*/* %changelog +* Mon Dec 2 2024 huyubiao - 8.32-11 +- sync patches from community +- add backport-head-fix-overflows-in-elide_tail_bytes_pipe.patch + * Tue Dec 5 2023 jiangchuangang - 8.32-10 - sync patches from community - add backport-who-fix-only-theoretical-overflow.patch