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.
This commit is contained in:
Frolov Daniil 2022-11-28 12:35:13 -05:00 committed by Jeff Law
parent 75e562d2c4
commit aa353bd9a0
2 changed files with 53 additions and 16 deletions

View File

@ -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

View File

@ -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);
}