Thanks for reporting that. I applied the attached patches. The first fixes the bug and adds a test case, the second fixes some unlikely and hard-to-test-for integer-overflow bugs I noticed while I was in the neighborhood.
From 15f78a1970a6d2c5cd41123864c44cb694b46d14 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Mon, 24 Feb 2014 21:38:02 -0800
Subject: [PATCH 1/2] diff: fix bug with -I and overlapping hunks

Problem reported by Vincent Lefevre in <http://bugs.gnu.org/16864>.
* src/context.c (find_hunk): Threshold is CONTEXT only if
the second change is ignorable.
* tests/ignore-matching-lines: New test.
* tests/Makefile.am (TESTS): Add it.
---
 src/context.c               |  7 +++----
 tests/Makefile.am           |  1 +
 tests/ignore-matching-lines | 47 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 51 insertions(+), 4 deletions(-)
 create mode 100755 tests/ignore-matching-lines

diff --git a/src/context.c b/src/context.c
index dd79f89..42f1eed 100644
--- a/src/context.c
+++ b/src/context.c
@@ -402,9 +402,8 @@ find_hunk (struct change *start)
   lin top0, top1;
   lin thresh;
 
-  /* Threshold distance is 2 * CONTEXT + 1 between two non-ignorable
-     changes, but only CONTEXT if one is ignorable.  Watch out for
-     integer overflow, though.  */
+  /* Threshold distance is CONTEXT if the second change is ignorable,
+     2 * CONTEXT + 1 otherwise.  Watch out for integer overflow.  */
   lin non_ignorable_threshold =
     (LIN_MAX - 1) / 2 < context ? LIN_MAX : 2 * context + 1;
   lin ignorable_threshold = context;
@@ -416,7 +415,7 @@ find_hunk (struct change *start)
       top1 = start->line1 + start->inserted;
       prev = start;
       start = start->link;
-      thresh = (prev->ignore || (start && start->ignore)
+      thresh = (start && start->ignore
                ? ignorable_threshold
                : non_ignorable_threshold);
       /* It is not supposed to matter which file we check in the end-test.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index dd2d514..005d9f0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -7,6 +7,7 @@ TESTS = \
   excess-slash \
   help-version \
   function-line-vs-leading-space \
+  ignore-matching-lines \
   label-vs-func        \
   new-file \
   no-dereference \
diff --git a/tests/ignore-matching-lines b/tests/ignore-matching-lines
new file mode 100755
index 0000000..5db9ba3
--- /dev/null
+++ b/tests/ignore-matching-lines
@@ -0,0 +1,47 @@
+#!/bin/sh
+# --ignore-matching-lines
+
+# Bug reported by Vincent Lefevre in <http://bugs.gnu.org/16864>.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+
+fail=0
+
+cat <<'EOF' >a
+1a
+2
+3a
+4
+5
+6
+EOF
+
+cat <<'EOF' >b
+1b
+2
+3b
+4
+5
+6
+7
+EOF
+
+cat <<'EOF' >exp
+@@ -1,6 +1,7 @@
+-1a
++1b
+ 2
+-3a
++3b
+ 4
+ 5
+ 6
++7
+EOF
+
+diff -u --ignore-matching-lines 3 a b >out 2>err
+test $? = 1 || fail=1
+sed 1,2d out >outtail || framework_failure+
+compare exp outtail || fail=1
+
+Exit $fail
-- 
1.8.5.3

From 3b7b9766119b8d19eec2d73d40968fd662b60aa6 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Mon, 24 Feb 2014 21:56:21 -0800
Subject: [PATCH 2/2] diff, sdiff: minor integer overflow fixes

* src/context.c (find_hunk):
Simplify, now that 2 * context + 1 cannot overflow.
* src/diff.c (main):
* src/sdiff.c (interact):
Don't rely on undefined behavior on signed integer overflow.
* src/diff.c (main): Don't let contexts exceed CONTEXT_MAX.
* src/system.h (CONTEXT_MAX): New macro.
---
 src/context.c |  6 +++---
 src/diff.c    | 15 ++++++++-------
 src/sdiff.c   | 10 ++++++----
 src/system.h  |  5 +++++
 4 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/src/context.c b/src/context.c
index 42f1eed..32053d1 100644
--- a/src/context.c
+++ b/src/context.c
@@ -403,10 +403,10 @@ find_hunk (struct change *start)
   lin thresh;
 
   /* Threshold distance is CONTEXT if the second change is ignorable,
-     2 * CONTEXT + 1 otherwise.  Watch out for integer overflow.  */
-  lin non_ignorable_threshold =
-    (LIN_MAX - 1) / 2 < context ? LIN_MAX : 2 * context + 1;
+     2 * CONTEXT + 1 otherwise.  Integer overflow can't happen, due
+     to CONTEXT_LIM.  */
   lin ignorable_threshold = context;
+  lin non_ignorable_threshold = 2 * context + 1;
 
   do
     {
diff --git a/src/diff.c b/src/diff.c
index 50d0365..c6ba5f6 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -304,11 +304,12 @@ main (int argc, char **argv)
        case '7':
        case '8':
        case '9':
-         if (! ISDIGIT (prev))
-           ocontext = c - '0';
-         else if (LIN_MAX / 10 < ocontext
-                  || ((ocontext = 10 * ocontext + c - '0') < 0))
-           ocontext = LIN_MAX;
+         ocontext = (! ISDIGIT (prev)
+                     ? c - '0'
+                     : (ocontext - (c - '0' <= CONTEXT_MAX % 10)
+                        < CONTEXT_MAX / 10)
+                     ? 10 * ocontext + (c - '0')
+                     : CONTEXT_MAX);
          break;
 
        case 'a':
@@ -337,8 +338,8 @@ main (int argc, char **argv)
                numval = strtoumax (optarg, &numend, 10);
                if (*numend)
                  try_help ("invalid context length '%s'", optarg);
-               if (LIN_MAX < numval)
-                 numval = LIN_MAX;
+               if (CONTEXT_MAX < numval)
+                 numval = CONTEXT_MAX;
              }
            else
              numval = 3;
diff --git a/src/sdiff.c b/src/sdiff.c
index e7bc657..329fa52 100644
--- a/src/sdiff.c
+++ b/src/sdiff.c
@@ -1099,12 +1099,14 @@ interact (struct line_filter *diff,
          uintmax_t val;
          lin llen, rlen, lenmax;
          errno = 0;
-         llen = val = strtoumax (diff_help + 1, &numend, 10);
-         if (llen < 0 || llen != val || errno || *numend != ',')
+         val = strtoumax (diff_help + 1, &numend, 10);
+         if (LIN_MAX < val || errno || *numend != ',')
            fatal (diff_help);
-         rlen = val = strtoumax (numend + 1, &numend, 10);
-         if (rlen < 0 || rlen != val || errno || *numend)
+         llen = val;
+         val = strtoumax (numend + 1, &numend, 10);
+         if (LIN_MAX < val || errno || *numend)
            fatal (diff_help);
+         rlen = val;
 
          lenmax = MAX (llen, rlen);
 
diff --git a/src/system.h b/src/system.h
index f39fff0..1f81a72 100644
--- a/src/system.h
+++ b/src/system.h
@@ -135,6 +135,11 @@ typedef ptrdiff_t lin;
 verify (TYPE_SIGNED (lin));
 verify (sizeof (ptrdiff_t) <= sizeof (lin));
 verify (sizeof (lin) <= sizeof (long int));
+
+/* Limit so that 2 * CONTEXT + 1 does not overflow.  */
+
+#define CONTEXT_MAX ((LIN_MAX - 1) / 2)
+
 
 /* This section contains POSIX-compliant defaults for macros
    that are meant to be overridden by hand in config.h as needed.  */
-- 
1.8.5.3

Reply via email to