Fix ICE with #line directive (PR c/89410)
authorDavid Malcolm <dmalcolm@redhat.com>
Wed, 20 Feb 2019 20:07:20 +0000 (20:07 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Wed, 20 Feb 2019 20:07:20 +0000 (20:07 +0000)
PR c/89410 reports various issues with #line directives with very
large numbers; one of them is an ICE inside diagnostic-show-locus.c
when emitting a diagnostic at line 0xffffffff.

The issue is that the arithmetic in layout::calculate_line_spans to
determine if two line spans are sufficiently close to consolidate
was using the unsigned 32-bit linenum_type, which was overflowing
when comparing the line for the expanded location with those of
the location range (all on line 0xffffffff), leading to it
erroneously adding two spans for the same line, leading to an
assertion failure.

This patch fixes the ICE by generalizing the use of long long in
line-map.h's comparison function for linenum_type into a new
linenum_arith_t typedef, and using it here.

Doing so uncovered a second problem: the loop to print the lines
within the line_span for this case is infinite: looping from
0xfffffff upwards, overflowing to 0, and then never becoming
greater than 0xfffffff.  The patch fixes this by using linenum_arith_t
there also.

gcc/ChangeLog:
PR c/89410
* diagnostic-show-locus.c (layout::calculate_line_spans): Use
linenum_arith_t when determining if two adjacent line spans are
close enough to merge.
(diagnostic_show_locus): Use linenum_arith_t when iterating over
lines within each line_span.

gcc/testsuite/ChangeLog:
PR c/89410
* gcc.dg/pr89410-1.c: New test.
* gcc.dg/pr89410-2.c: New test.

libcpp/ChangeLog:
PR c/89410
* include/line-map.h (linenum_arith_t): New typedef.
(compare): Use it.

From-SVN: r269050

gcc/ChangeLog
gcc/diagnostic-show-locus.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/pr89410-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/pr89410-2.c [new file with mode: 0644]
libcpp/ChangeLog
libcpp/include/line-map.h

index 13580de7525afad7891545d4ad9fe0d1d8f652f3..75ff13427137639da90faa4e93475aae8d7837af 100644 (file)
@@ -1,3 +1,12 @@
+2019-02-20  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c/89410
+       * diagnostic-show-locus.c (layout::calculate_line_spans): Use
+       linenum_arith_t when determining if two adjacent line spans are
+       close enough to merge.
+       (diagnostic_show_locus): Use linenum_arith_t when iterating over
+       lines within each line_span.
+
 2019-02-20 Andre Vieira  <andre.simoesdiasvieira@arm.com>
 
        PR target/86487
index ddc9421ce63955975232e91968ce9522724c1526..205ee56f4099331bc33438b7fc9717e4b5de13a5 100644 (file)
@@ -1211,7 +1211,8 @@ layout::calculate_line_spans ()
       const line_span *next = &tmp_spans[i];
       gcc_assert (next->m_first_line >= current->m_first_line);
       const int merger_distance = m_show_line_numbers_p ? 1 : 0;
-      if (next->m_first_line <= current->m_last_line + 1 + merger_distance)
+      if ((linenum_arith_t)next->m_first_line
+         <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
        {
          /* We can merge them. */
          if (next->m_last_line > current->m_last_line)
@@ -2301,8 +2302,10 @@ diagnostic_show_locus (diagnostic_context * context,
              context->start_span (context, exploc);
            }
        }
-      linenum_type last_line = line_span->get_last_line ();
-      for (linenum_type row = line_span->get_first_line ();
+      /* Iterate over the lines within this span (using linenum_arith_t to
+        avoid overflow with 0xffffffff causing an infinite loop).  */
+      linenum_arith_t last_line = line_span->get_last_line ();
+      for (linenum_arith_t row = line_span->get_first_line ();
           row <= last_line; row++)
        layout.print_line (row);
     }
index 7a407cdba2082ce81d62682de6617475049b045e..9d344ba7c2d8bf880db60c3c910eacfc4eb5a6f0 100644 (file)
@@ -1,3 +1,9 @@
+2019-02-20  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c/89410
+       * gcc.dg/pr89410-1.c: New test.
+       * gcc.dg/pr89410-2.c: New test.
+
 2019-02-20  Pat Haugen  <pthaugen@us.ibm.com>
 
        * lib/target-supports.exp (check_effective_target_vect_usad_char):
diff --git a/gcc/testsuite/gcc.dg/pr89410-1.c b/gcc/testsuite/gcc.dg/pr89410-1.c
new file mode 100644 (file)
index 0000000..73dc6d4
--- /dev/null
@@ -0,0 +1,9 @@
+/* { dg-options "" } */
+
+int main(void)
+{
+  /* This is 0xffffffff.  */
+#line 4294967295
+#warning msg
+  /* { dg-warning "msg" "" { target *-*-* } -1 } */
+}
diff --git a/gcc/testsuite/gcc.dg/pr89410-2.c b/gcc/testsuite/gcc.dg/pr89410-2.c
new file mode 100644 (file)
index 0000000..76e781b
--- /dev/null
@@ -0,0 +1,13 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+int main(void)
+{
+  /* This is 0x7fffffffffffffff, which truncates to 0xffffffff.  */
+#line 9223372036854775807 /* { dg-warning "line number out of range" } */
+#warning msg
+  /* { dg-begin-multiline-output "" }
+ #line 9223372036854775807
+       ^~~~~~~~~~~~~~~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-warning "msg" "" { target *-*-* } -1 } */
+}
index fa2fa7d97e87686095debbc2f31f32ca484b7ea0..355b4cb0f739560228d14826d8298a53ad02874d 100644 (file)
@@ -1,3 +1,9 @@
+2019-02-20  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c/89410
+       * include/line-map.h (linenum_arith_t): New typedef.
+       (compare): Use it.
+
 2019-02-18  Martin Liska  <mliska@suse.cz>
 
        PR c++/89383
index a6eb87c2ec05b54323928d503e64c150c741eb0a..6c77c2888550d05ef0f270890cf5086647aafbd5 100644 (file)
@@ -49,13 +49,16 @@ along with this program; see the file COPYING3.  If not see
 /* The type of line numbers.  */
 typedef unsigned int linenum_type;
 
+/* A type for doing arithmetic on line numbers.  */
+typedef long long linenum_arith_t;
+
 /* A function for for use by qsort for comparing line numbers.  */
 
 inline int compare (linenum_type lhs, linenum_type rhs)
 {
-  /* Avoid truncation issues by using long long for the comparison,
+  /* Avoid truncation issues by using linenum_arith_t for the comparison,
      and only consider the sign of the result.  */
-  long long diff = (long long)lhs - (long long)rhs;
+  linenum_arith_t diff = (linenum_arith_t)lhs - (linenum_arith_t)rhs;
   if (diff)
     return diff > 0 ? 1 : -1;
   return 0;