On 2026-09-27 16:35, Collin Funk wrote:

Thanks for the report. I attached a patch which should fix it. Limiting
the value to LIN_MAX - 1 seems simpler than trying to handle the
overflow when we see it. I also doubt anyone will complain about the
possible output context being lowered by 1 in this case.
Yes, the latter's not a problem, however, I'm more inclined to put the fix near where the problem occurs, so I installed the attached instead. Most of this is just your patch.

Thanks to both of you. Closing the bug report.

From 9d50430461bc6820eda31739adef641665c8a603 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Mon, 28 Sep 2026 16:56:50 -0700
Subject: [PATCH 1/2] diff: fix integer overflow

Problem reported by Daniel Koh Ji Yang (Bug#81937).
* src/io.c (find_identical_ends): Avoid undefined behavior
on signed integer overflow.
---
 src/io.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/io.c b/src/io.c
index 4a06c78..ac217b8 100644
--- a/src/io.c
+++ b/src/io.c
@@ -1203,9 +1203,11 @@ find_identical_ends (struct file_data filevec[])
          this line to the main body.  Discard up to HORIZON_LINES lines from
          the identical suffix.  Also, discard one extra line,
          because shift_boundaries may need it.  */
-      lin i = horizon_lines + !((buffer0 == p0 || p0[-1] == '\n')
-				&&
-				(buffer1 == p1 || p1[-1] == '\n'));
+      bool at_line_beginning = ((buffer0 == p0 || p0[-1] == '\n')
+				&& (buffer1 == p1 || p1[-1] == '\n'));
+      lin i;
+      if (ckd_add (&i, horizon_lines, !at_line_beginning))
+	i = LIN_MAX;
       while (i-- && p0 != end0)
         while (*p0++ != '\n')
           continue;
-- 
2.55.0

From c2f7cb42b2b5ce90b1306ba01799538ef1cf1b02 Mon Sep 17 00:00:00 2001
From: Collin Funk <[email protected]>
Date: Sun, 27 Sep 2026 16:21:44 -0700
Subject: [PATCH 2/2] diff: test integer overflow fix

* NEWS: Mention the bug fix.
* tests/basic: Add a test case that triggers UBSAN without the fix.
* THANKS: Add Daniel Koh Ji Yang for reporting the bug.
Fixes https://bugs.gnu.org/81937
---
 NEWS        |  3 +++
 THANKS      |  1 +
 tests/basic | 14 ++++++++++++++
 3 files changed, 18 insertions(+)

diff --git a/NEWS b/NEWS
index f92bed9..a8288bc 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,9 @@ GNU diffutils NEWS                                    -*- outline -*-
   or unexpectedly truncated data.
   [bugs present since "the beginning"]
 
+  diff -u no longer overflows a signed integer when PTRDIFF_MAX lines of
+  output context are requested.
+  [bug#81937 introduced in 3.11]
 
 * Noteworthy changes in release 3.12 (2025-04-08) [stable]
 
diff --git a/THANKS b/THANKS
index a372954..61b95c8 100644
--- a/THANKS
+++ b/THANKS
@@ -21,4 +21,5 @@ Randy Smith  <[email protected]>
 Richard Stallman  <[email protected]>
 Leonard H. Tower Jr.  <[email protected]>
 Larry Wall  <[email protected]>
+Daniel Koh Ji Yang  <[email protected]>
 Eli Zaretskii  <[email protected]>
diff --git a/tests/basic b/tests/basic
index 161262d..519d165 100755
--- a/tests/basic
+++ b/tests/basic
@@ -39,4 +39,18 @@ for opt in '' -u -c; do
   compare exp-$(echo $opt|tr ' ' _) out || fail=1
 done
 
+# This would overflow a signed integer in diffutils-3.12 and earlier.
+returns_ 1 diff -u -9999999999999999999999999999 a b >out-t 2>err || fail=1
+# Remove date and time.
+sed -e 's/^\([-+*][-+*][-+*] [^	]*\)	.*/\1/' out-t >out || framework_failure_
+cat <<\EOF >exp || framework_failure
+--- a
++++ b
+@@ -1 +1 @@
+-a
++b
+EOF
+compare exp out || fail=1
+compare /dev/null err || fail=1
+
 Exit $fail
-- 
2.55.0

Reply via email to