]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
fold: fix out of bounds write with zero width characters
authorCollin Funk <collin.funk1@gmail.com>
Tue, 16 Sep 2025 03:53:23 +0000 (20:53 -0700)
committerCollin Funk <collin.funk1@gmail.com>
Tue, 16 Sep 2025 03:55:56 +0000 (20:55 -0700)
* src/fold.c (fold_file): Prefer putchar ('\n') to copying characters.
If we do not have room in the output buffer print it since it is not a
full line of text.
* tests/fold/fold-zero-width.sh: New test case.
* tests/local.mk (all_tests): Add it.

src/fold.c
tests/fold/fold-zero-width.sh [new file with mode: 0755]
tests/local.mk

index b90bc7d80a38e3818239e47eca8edf58933e1b3e..1908659630d16ef2f07104aa0aba040430d8abfe 100644 (file)
@@ -192,9 +192,8 @@ fold_file (char const *filename, size_t width)
             }
           if (g.ch == '\n')
             {
-              memcpy (line_out + offset_out, p, g.len);
-              offset_out += g.len;
               fwrite (line_out, sizeof (char), offset_out, stdout);
+              putchar ('\n');
               column = offset_out = 0;
               continue;
             }
@@ -249,17 +248,25 @@ fold_file (char const *filename, size_t width)
 
               if (offset_out == 0)
                 {
-                  memcpy (line_out + offset_out, p, g.len);
+                  memcpy (line_out, p, g.len);
                   offset_out += g.len;
                   continue;
                 }
 
-              line_out[offset_out++] = '\n';
               fwrite (line_out, sizeof (char), offset_out, stdout);
+              putchar ('\n');
               column = offset_out = 0;
               goto rescan;
             }
 
+          /* This can occur if we have read characters with a width of
+             zero.  */
+          if (sizeof line_out <= offset_out + g.len)
+            {
+              fwrite (line_out, sizeof (char), offset_out, stdout);
+              offset_out = 0;
+            }
+
           memcpy (line_out + offset_out, p, g.len);
           offset_out += g.len;
         }
diff --git a/tests/fold/fold-zero-width.sh b/tests/fold/fold-zero-width.sh
new file mode 100755 (executable)
index 0000000..a0d7e3f
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/sh
+# Test fold with zero width characters.
+
+# Copyright (C) 2025 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ fold printf
+getlimits_
+
+# Make sure we do not overflow the buffer.
+IO_BUFSIZE_TIMES2=$(($IO_BUFSIZE * 2))
+
+# Fold counts by columns by default.
+head -c $IO_BUFSIZE_TIMES2 /dev/zero | fold > out || fail=1
+test $(cat out | wc -l) -eq 0 || fail=1
+
+# Check that zero width characters are counted with --characters.
+head -c $IO_BUFSIZE_TIMES2 /dev/zero | fold --characters > out || fail=1
+test $(cat out | wc -l) -eq $(($IO_BUFSIZE_TIMES2 / 80)) || fail=1
+
+test "$LOCALE_FR_UTF8" != none || skip_ "French UTF-8 locale not available"
+
+LC_ALL=$LOCALE_FR_UTF8
+export LC_ALL
+
+# Same thing, but using U+200B ZERO WIDTH SPACE.
+yes $(env printf '\u200B') | head -n $IO_BUFSIZE_TIMES2 | tr -d '\n' > inp
+
+fold inp > out || fail=1
+test $(cat out | wc -l) -eq 0 || fail=1
+
+fold --characters inp > out || fail=1
+test $(cat out | wc -l) -eq $(($IO_BUFSIZE_TIMES2 / 80)) || fail=1
+
+# Ensure bounded memory operation.
+vm=$(get_min_ulimit_v_ fold /dev/null) && {
+  head -c $IO_BUFSIZE_TIMES2 /dev/zero | tr -d '\n' \
+    | (ulimit -v $(($vm+8000)) && fold 2>err) | head || fail=1
+  compare /dev/null err || fail=1
+}
+
+Exit $fail
index 67a919e841508af7a983d53b3366bd60d77d0a14..4aa199a19742f1b67852b784f07446c995ed3f6c 100644 (file)
@@ -330,6 +330,7 @@ all_tests =                                 \
   tests/fold/fold-characters.sh                        \
   tests/fold/fold-nbsp.sh                      \
   tests/fold/fold-spaces.sh                    \
+  tests/fold/fold-zero-width.sh                        \
   tests/fold/fold.pl                           \
   tests/groups/groups-dash.sh                  \
   tests/groups/groups-process-all.sh           \