From eb9e8241d99145020ec5c050c918c1ad3abc2701 Mon Sep 17 00:00:00 2001
From: Frolov Daniil <frolov...@phystech.edu>
Date: Thu, 1 Sep 2022 10:55:01 +0300
Subject: [PATCH] Support %b, %B for -Wformat-overflow (sprintf, snprintf)

gcc/ChangeLog:

	* gimple-ssa-sprintf.cc (fmtresult::type_max_digits): Handle
	base == 2.
	(tree_digits): Likewise.
	(format_integer): Likewise.
	(parse_directive): Add cases for %b and %B directives.

gcc/testsuite/ChangeLog:

	* gcc.dg/Wformat-overflow1.c: New test.
---
 gcc/gimple-ssa-sprintf.cc                | 41 +++++++++++++++---------
 gcc/testsuite/gcc.dg/Wformat-overflow1.c | 28 ++++++++++++++++
 2 files changed, 53 insertions(+), 16 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/Wformat-overflow1.c

diff --git a/gcc/gimple-ssa-sprintf.cc b/gcc/gimple-ssa-sprintf.cc
index a888b5ac7d5..1dd9b0dc46b 100644
--- a/gcc/gimple-ssa-sprintf.cc
+++ b/gcc/gimple-ssa-sprintf.cc
@@ -535,6 +535,8 @@ fmtresult::type_max_digits (tree type, int base)
   unsigned prec = TYPE_PRECISION (type);
   switch (base)
     {
+    case 2:
+      return prec;
     case 8:
       return (prec + 2) / 3;
     case 10:
@@ -804,9 +806,9 @@ ilog (unsigned HOST_WIDE_INT x, int base)
 /* Return the number of bytes resulting from converting into a string
    the INTEGER_CST tree node X in BASE with a minimum of PREC digits.
    PLUS indicates whether 1 for a plus sign should be added for positive
-   numbers, and PREFIX whether the length of an octal ('O') or hexadecimal
-   ('0x') prefix should be added for nonzero numbers.  Return -1 if X cannot
-   be represented.  */
+   numbers, and PREFIX whether the length of an octal ('0') or hexadecimal
+   ('0x') or binary ('0b') prefix should be added for nonzero numbers.
+   Return -1 if X cannot be represented.  */
 
 static HOST_WIDE_INT
 tree_digits (tree x, int base, HOST_WIDE_INT prec, bool plus, bool prefix)
@@ -857,11 +859,11 @@ tree_digits (tree x, int base, HOST_WIDE_INT prec, bool plus, bool prefix)
 
   /* Adjust a non-zero value for the base prefix, either hexadecimal,
      or, unless precision has resulted in a leading zero, also octal.  */
-  if (prefix && absval && (base == 16 || prec <= ndigs))
+  if (prefix && absval)
     {
-      if (base == 8)
+      if (base == 8 && prec <= ndigs)
 	res += 1;
-      else if (base == 16)
+      else if (base == 16 || base == 2) /* 0x...(0X...) or 0b...(0B...).  */
 	res += 2;
     }
 
@@ -1209,7 +1211,7 @@ format_integer (const directive &dir, tree arg, pointer_query &ptr_qry)
 
   /* True when a conversion is preceded by a prefix indicating the base
      of the argument (octal or hexadecimal).  */
-  bool maybebase = dir.get_flag ('#');
+  const bool maybebase = dir.get_flag ('#');
 
   /* True when a signed conversion is preceded by a sign or space.  */
   bool maybesign = false;
@@ -1229,6 +1231,10 @@ format_integer (const directive &dir, tree arg, pointer_query &ptr_qry)
     case 'u':
       base = 10;
       break;
+    case 'b':
+    case 'B':
+      base = 2;
+      break;
     case 'o':
       base = 8;
       break;
@@ -1240,6 +1246,8 @@ format_integer (const directive &dir, tree arg, pointer_query &ptr_qry)
       gcc_unreachable ();
     }
 
+  const unsigned adj = (sign | maybebase) + (base == 2 || base == 16);
+
   /* The type of the "formal" argument expected by the directive.  */
   tree dirtype = NULL_TREE;
 
@@ -1350,11 +1358,9 @@ format_integer (const directive &dir, tree arg, pointer_query &ptr_qry)
       res.range.unlikely = res.range.max;
 
       /* Bump up the counters if WIDTH is greater than LEN.  */
-      res.adjust_for_width_or_precision (dir.width, dirtype, base,
-					 (sign | maybebase) + (base == 16));
+      res.adjust_for_width_or_precision (dir.width, dirtype, base, adj);
       /* Bump up the counters again if PRECision is greater still.  */
-      res.adjust_for_width_or_precision (dir.prec, dirtype, base,
-					 (sign | maybebase) + (base == 16));
+      res.adjust_for_width_or_precision (dir.prec, dirtype, base, adj);
 
       return res;
     }
@@ -1503,17 +1509,15 @@ format_integer (const directive &dir, tree arg, pointer_query &ptr_qry)
 	  if (res.range.min == 1)
 	    res.range.likely += base == 8 ? 1 : 2;
 	  else if (res.range.min == 2
-		   && base == 16
+		   && (base == 16 || base == 2)
 		   && (dir.width[0] == 2 || dir.prec[0] == 2))
 	    ++res.range.likely;
 	}
     }
 
   res.range.unlikely = res.range.max;
-  res.adjust_for_width_or_precision (dir.width, dirtype, base,
-				     (sign | maybebase) + (base == 16));
-  res.adjust_for_width_or_precision (dir.prec, dirtype, base,
-				     (sign | maybebase) + (base == 16));
+  res.adjust_for_width_or_precision (dir.width, dirtype, base, adj);
+  res.adjust_for_width_or_precision (dir.prec, dirtype, base, adj);
 
   return res;
 }
@@ -3725,6 +3729,11 @@ parse_directive (call_info &info,
       dir.fmtfunc = format_integer;
       break;
 
+    case 'b':
+    case 'B':
+      dir.fmtfunc = format_integer;
+      break;
+
     case 'p':
       /* The %p output is implementation-defined.  It's possible
 	 to determine this format but due to extensions (especially
diff --git a/gcc/testsuite/gcc.dg/Wformat-overflow1.c b/gcc/testsuite/gcc.dg/Wformat-overflow1.c
new file mode 100644
index 00000000000..cf9766fae14
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wformat-overflow1.c
@@ -0,0 +1,28 @@
+/*
+    { dg-do compile }
+    { dg-options "-Wformat-overflow -std=c2x" }
+*/
+
+extern int sprintf (char* restrict, const char* restrict, ...);
+
+void test_warn () {
+
+    int n = __INT_MAX__;
+    char dst [5] = {0};
+    sprintf (dst, "%b", n);  /* { dg-warning "-Wformat-overflow" } */
+
+    sprintf (dst, "%#b", n); /* { dg-warning "-Wformat-overflow" } */
+
+}
+
+void test_no_warn () {
+
+    char dst [5] = {0};
+    int n = 8;
+    sprintf (dst, "%b", n);
+
+    char another_dst [34] = {0};
+    n = __INT_MAX__;
+    sprintf (another_dst, "%#b", n);
+
+}
-- 
2.25.1

  • Re: -... Frolov Daniil via Gcc-patches
    • ... Marek Polacek via Gcc-patches
      • ... Даниил Александрович Фролов via Gcc-patches

Reply via email to