preprocessor: Fix #line overflow check [PR97602]

Joseph Myers joseph@codesourcery.com
Fri Nov 27 22:41:05 GMT 2020


The preprocessor check for overflow (of linenum_type = unsigned int)
when reading the line number in a #line directive is incomplete; it
checks "reg < reg_prev" which doesn't cover all cases where
multiplying by 10 overflowed.  Fix this by checking for overflow
before rather than after it occurs (using essentially the same logic
as used by e.g. glibc printf when reading width and precision values
from strings).

Bootstrapped with no regressions for x86_64-pc-linux-gnu.  Applied to 
mainline.

libcpp/
2020-11-27  Joseph Myers  <joseph@codesourcery.com>

	PR preprocessor/97602
	* directives.c (strtolinenum): Check for overflow before it
	occurs.  Correct comment.

gcc/testsuite/
2020-11-27  Joseph Myers  <joseph@codesourcery.com>

	PR preprocessor/97602
	* gcc.dg/cpp/line9.c, gcc.dg/cpp/line10.c: New tests.

diff --git a/gcc/testsuite/gcc.dg/cpp/line10.c b/gcc/testsuite/gcc.dg/cpp/line10.c
new file mode 100644
index 00000000000..9f5f0799847
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/line10.c
@@ -0,0 +1,5 @@
+/* Test #line overflow checks: bug 97602.  */
+/* { dg-do preprocess } */
+/* { dg-options "-pedantic" } */
+
+#line 4294967296 /* { dg-warning "line number out of range" } */
diff --git a/gcc/testsuite/gcc.dg/cpp/line9.c b/gcc/testsuite/gcc.dg/cpp/line9.c
new file mode 100644
index 00000000000..8060aff204d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/cpp/line9.c
@@ -0,0 +1,5 @@
+/* Test #line overflow checks: bug 97602.  */
+/* { dg-do preprocess } */
+/* { dg-options "-pedantic" } */
+
+#line 5000000000 /* { dg-warning "line number out of range" } */
diff --git a/libcpp/directives.c b/libcpp/directives.c
index fa66b5c5f71..75115600e3a 100644
--- a/libcpp/directives.c
+++ b/libcpp/directives.c
@@ -915,12 +915,11 @@ read_flag (cpp_reader *pfile, unsigned int last)
 /* Subroutine of do_line and do_linemarker.  Convert a number in STR,
    of length LEN, to binary; store it in NUMP, and return false if the
    number was well-formed, true if not. WRAPPED is set to true if the
-   number did not fit into 'unsigned long'.  */
+   number did not fit into 'linenum_type'.  */
 static bool
 strtolinenum (const uchar *str, size_t len, linenum_type *nump, bool *wrapped)
 {
   linenum_type reg = 0;
-  linenum_type reg_prev = 0;
 
   uchar c;
   *wrapped = false;
@@ -929,11 +928,12 @@ strtolinenum (const uchar *str, size_t len, linenum_type *nump, bool *wrapped)
       c = *str++;
       if (!ISDIGIT (c))
 	return true;
+      if (reg > ((linenum_type) -1) / 10)
+	*wrapped = true;
       reg *= 10;
-      reg += c - '0';
-      if (reg < reg_prev) 
+      if (reg > ((linenum_type) -1) - (c - '0'))
 	*wrapped = true;
-      reg_prev = reg;
+      reg += c - '0';
     }
   *nump = reg;
   return false;

-- 
Joseph S. Myers
joseph@codesourcery.com


More information about the Gcc-patches mailing list