287 lines
13 KiB
C
287 lines
13 KiB
C
/* { dg-do compile } */
|
|
/* { dg-options "-Wformat -Wformat-overflow=2 -ftrack-macro-expansion=0" } */
|
|
|
|
/* When debugging, define LINE to the line number of the test case to exercise
|
|
and avoid exercising any of the others. The buffer and objsize macros
|
|
below make use of LINE to avoid warnings for other lines. */
|
|
#ifndef LINE
|
|
# define LINE 0
|
|
#endif
|
|
|
|
char buffer [256];
|
|
extern char *ptr;
|
|
|
|
#define buffer(size) \
|
|
(!LINE || __LINE__ == LINE ? buffer + sizeof buffer - size : ptr)
|
|
|
|
#define objsize(size) (!LINE || __LINE__ == LINE ? size : __SIZE_MAX__ / 2)
|
|
|
|
typedef __SIZE_TYPE__ size_t;
|
|
|
|
#if !__cplusplus
|
|
typedef __WCHAR_TYPE__ wchar_t;
|
|
#endif
|
|
|
|
typedef unsigned char UChar;
|
|
|
|
#define T(size, fmt, ...) \
|
|
__builtin_sprintf (buffer (size), fmt, __VA_ARGS__)
|
|
|
|
__builtin_va_list va;
|
|
|
|
/* Exercise buffer overflow detection with const string arguments. */
|
|
|
|
void test_s_const (void)
|
|
{
|
|
/* Wide string literals are handled slightly differently than
|
|
at level 1. At level 1, each wide character is assumed to
|
|
convert into a single byte. At level 2, they are assumed
|
|
to convert into at least one byte. */
|
|
T (0, "%ls", L""); /* { dg-warning "nul past the end" } */
|
|
T (1, "%ls", L"");
|
|
T (1, "%ls", L"\0");
|
|
T (1, "%1ls", L""); /* { dg-warning "nul past the end" } */
|
|
|
|
T (0, "%*ls", 0, L""); /* { dg-warning "nul past the end" } */
|
|
T (1, "%*ls", 0, L"");
|
|
T (1, "%*ls", 0, L"\0");
|
|
T (1, "%*ls", 1, L""); /* { dg-warning "nul past the end" } */
|
|
|
|
/* A wide character converts into between zero and MB_LEN_MAX bytes
|
|
(although individual ASCII characters are assumed to convert into
|
|
1 bt %lc so this could be made smarter. */
|
|
T (1, "%ls", L"1"); /* { dg-warning "directive writing up to 6 bytes into a region of size 1" } */
|
|
T (1, "%.0ls", L"1");
|
|
T (2, "%.0ls", L"1");
|
|
T (2, "%.1ls", L"1");
|
|
T (2, "%.2ls", L"1"); /* { dg-warning "nul past the end" } */
|
|
T (2, "%.3ls", L"1"); /* { dg-warning "directive writing up to 3 bytes into a region of size 2" } */
|
|
T (2, "%.7ls", L"1"); /* { dg-warning "directive writing up to 6 bytes into a region of size 2" } */
|
|
T (2, "%.2ls", L"12"); /* { dg-warning "nul past the end" } */
|
|
|
|
/* The "%.2ls" directive below will write at a minimum 1 byte (because
|
|
L"1" is known and can be assumed to convert to at least one multibyte
|
|
character), and at most 2 bytes because of the precision. Since its
|
|
output is explicitly bounded it is diagnosed. */
|
|
T (2, "%.2ls", L"1"); /* { dg-warning "nul past the end" } */
|
|
T (2, "%.*ls", 2, L"1"); /* { dg-warning "nul past the end" } */
|
|
|
|
/* The following three are constrained by the precision to at most
|
|
that many bytes of the converted wide string plus a terminating NUL. */
|
|
T (2, "%.0ls", L"1");
|
|
T (2, "%.1ls", L"1");
|
|
T (3, "%.2ls", L"1");
|
|
T (3, "%.2ls", L"12");
|
|
T (3, "%.3ls", L"12"); /* { dg-warning "nul past the end" } */
|
|
T (4, "%.3ls", L"123");
|
|
T (4, "%.4ls", L"123"); /* { dg-warning "nul past the end" } */
|
|
T (4, "%.5ls", L"123"); /* { dg-warning "directive writing up to 5 bytes into a region of size 4" } */
|
|
T (4, "%.6ls", L"123"); /* { dg-warning "directive writing up to 6 bytes into a region of size 4" } */
|
|
}
|
|
|
|
|
|
struct Arrays {
|
|
char a1 [1];
|
|
char a2 [2];
|
|
char a3 [3];
|
|
char a4 [4];
|
|
char a0 [0];
|
|
char ax [];
|
|
};
|
|
|
|
/* Exercise buffer overflow detection with non-const string arguments. */
|
|
|
|
void test_s_nonconst (int w, int p, const char *s, const wchar_t *ws,
|
|
struct Arrays *a)
|
|
{
|
|
T (0, "%s", s); /* { dg-warning "into a region" } */
|
|
T (1, "%s", s); /* { dg-warning "nul past the end" } */
|
|
T (1, "%1s", s); /* { dg-warning "writing a terminating nul" } */
|
|
T (1, "%.0s", s);
|
|
T (1, "%.1s", s); /* { dg-warning "may write a terminating nul" } */
|
|
T (1, "%*s", 0, s); /* { dg-warning "may write a terminating nul" } */
|
|
T (1, "%*s", 1, s); /* { dg-warning "writing a terminating nul" } */
|
|
T (1, "%*s", 2, s); /* { dg-warning "directive writing 2 or more bytes" } */
|
|
T (1, "%*s", 3, s); /* { dg-warning "directive writing 3 or more bytes" } */
|
|
|
|
T (1, "%.*s", 1, s); /* { dg-warning "may write a terminating nul" } */
|
|
T (1, "%.*s", 2, s); /* { dg-warning "writing up to 2 bytes" } */
|
|
T (1, "%.*s", 3, s); /* { dg-warning "writing up to 3 bytes" } */
|
|
|
|
T (1, "%.0ls", ws);
|
|
T (1, "%.1ls", ws); /* { dg-warning "may write a terminating nul" } */
|
|
T (1, "%ls", ws); /* { dg-warning "may write a terminating nul" } */
|
|
|
|
/* Verify that the size of the array is used in lieu of its length. */
|
|
T (1, "%s", a->a1);
|
|
|
|
/* In the following test, since the length of the strings isn't known,
|
|
their type (the array) is used to bound the maximum length to 1,
|
|
which means the "%s" directive would not overflow the buffer,
|
|
but it would leave no room for the terminating nul. */
|
|
T (1, "%s", a->a2); /* { dg-warning "may write a terminating nul" } */
|
|
|
|
/* Unlike in the test above, since the length of the string is bounded
|
|
by the array type to at most 2, the "%s" directive is diagnosed firts,
|
|
preventing the diagnostic about the terminatinb nul. */
|
|
T (1, "%s", a->a3); /* { dg-warning "directive writing up to 2 bytes" } */
|
|
|
|
/* The length of a zero length array and flexible array member is
|
|
unknown and at leve 2 assumed to be at least 1. */
|
|
T (1, "%s", a->a0); /* { dg-warning "may write a terminating nul" } */
|
|
T (1, "%s", a->ax); /* { dg-warning "may write a terminating nul" } */
|
|
|
|
T (2, "%s", a->a0);
|
|
T (2, "%s", a->ax);
|
|
}
|
|
|
|
/* Exercise buffer overflow detection with non-const integer arguments. */
|
|
|
|
void test_hh_nonconst (int w, int p, int x, unsigned y)
|
|
{
|
|
T (1, "%hhi", x); /* { dg-warning "into a region" } */
|
|
T (2, "%hhi", x); /* { dg-warning "into a region" } */
|
|
T (3, "%hhi", x); /* { dg-warning "into a region" } */
|
|
T (4, "%hhi", x); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
|
|
|
T (1, "%hhi", y); /* { dg-warning "between 1 and 4 bytes" } */
|
|
T (2, "%hhi", y); /* { dg-warning "into a region" } */
|
|
T (3, "%hhi", y); /* { dg-warning "into a region" } */
|
|
T (4, "%hhi", y); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
|
|
|
/* Negative precision is treated as if none were specified. */
|
|
T (1, "%.*hhi", -1, x); /* { dg-warning "between 1 and 4 bytes" } */
|
|
T (2, "%.*hhi", -1, x); /* { dg-warning "into a region" } */
|
|
T (3, "%.*hhi", -1, x); /* { dg-warning "into a region" } */
|
|
T (4, "%.*hhi", -1, x); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
|
|
|
/* Zero precision means that zero argument formats as no bytes unless
|
|
length or flags make it otherwise. */
|
|
T (1, "%.*hhi", 0, x); /* { dg-warning "writing up to 4 bytes" } */
|
|
T (2, "%.*hhi", 0, x); /* { dg-warning "writing up to 4 bytes" } */
|
|
T (3, "%.*hhi", 0, x); /* { dg-warning "writing up to 4 bytes" } */
|
|
T (4, "%.*hhi", 0, x); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
|
|
|
T (1, "%.*hhi", 0, y); /* { dg-warning "writing up to 4 bytes" } */
|
|
T (2, "%.*hhi", 0, y); /* { dg-warning "writing up to 4 bytes" } */
|
|
T (3, "%.*hhi", 0, y); /* { dg-warning "writing up to 4 bytes" } */
|
|
T (4, "%.*hhi", 0, y); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
|
|
|
T (1, "%#.*hhi", 0, y); /* { dg-warning "writing up to 4 bytes" } */
|
|
/* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
|
|
T (1, "%+.*hhi", 0, y); /* { dg-warning "between 1 and 4 bytes" } */
|
|
T (1, "%-.*hhi", 0, y); /* { dg-warning "writing up to 4 bytes" } */
|
|
T (1, "% .*hhi", 0, y); /* { dg-warning "between 1 and 4 bytes" } */
|
|
|
|
T (1, "%#.*hhi", 1, y); /* { dg-warning "between 1 and 4 bytes" } */
|
|
/* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
|
|
T (1, "%+.*hhi", 1, y); /* { dg-warning "between 2 and 4 bytes" } */
|
|
T (1, "%-.*hhi", 1, y); /* { dg-warning "between 1 and 4 bytes" } */
|
|
T (1, "% .*hhi", 1, y); /* { dg-warning "between 2 and 4 bytes" } */
|
|
|
|
T (1, "%#.*hhi", p, y); /* { dg-warning "writing up to \[0-9\]+ bytes" } */
|
|
/* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
|
|
T (1, "%+.*hhi", p, y); /* { dg-warning "writing 1 or more bytes|writing between 1 and \[0-9\]+ bytes" } */
|
|
T (1, "%-.*hhi", p, y); /* { dg-warning "writing up to \[0-9\]+ bytes" } */
|
|
T (1, "% .*hhi", p, y); /* { dg-warning "writing between 1 and \[0-9\]+ bytes|writing 1 or more bytes" } */
|
|
|
|
T (1, "%#.*hhu", 0, y); /* { dg-warning "writing up to 3 bytes" } */
|
|
/* { dg-warning ".#. flag used" "-Wformat" { target *-*-* } .-1 } */
|
|
T (1, "%+.*hhu", 0, y); /* { dg-warning "writing up to 3 bytes" } */
|
|
/* { dg-warning ".\\+. flag used" "-Wformat" { target *-*-* } .-1 } */
|
|
T (1, "%-.*hhu", 0, y); /* { dg-warning "writing up to 3 bytes" } */
|
|
T (1, "% .*hhu", 0, y); /* { dg-warning "writing up to 3 bytes" } */
|
|
/* { dg-warning ". . flag used" "-Wformat" { target *-*-* } .-1 } */
|
|
}
|
|
|
|
void test_h_nonconst (int x)
|
|
{
|
|
extern UChar uc;
|
|
|
|
T (1, "%hi", uc); /* { dg-warning "into a region" } */
|
|
T (2, "%hi", uc); /* { dg-warning "into a region" } */
|
|
/* Formatting an 8-bit unsigned char as a signed short (or any other
|
|
type with greater precision) can write at most 3 characters. */
|
|
T (3, "%hi", uc); /* { dg-warning "terminating nul past" } */
|
|
T (4, "%hi", uc);
|
|
|
|
/* Verify that the same thing works when the int argument is cast
|
|
to unsigned char. */
|
|
T (1, "%hi", (UChar)x); /* { dg-warning "into a region" } */
|
|
T (2, "%hi", (UChar)x); /* { dg-warning "into a region" } */
|
|
T (3, "%hi", (UChar)x); /* { dg-warning "may write a terminating nul past the end of the destination" } */
|
|
T (4, "%hi", (UChar)x);
|
|
}
|
|
|
|
void test_i_nonconst (int x)
|
|
{
|
|
extern UChar uc;
|
|
|
|
T (1, "%i", uc); /* { dg-warning "into a region" } */
|
|
T (2, "%i", uc); /* { dg-warning "into a region" } */
|
|
T (3, "%i", uc); /* { dg-warning "terminating nul past" } */
|
|
T (4, "%i", uc);
|
|
|
|
T (1, "%i", (UChar)x); /* { dg-warning "into a region" } */
|
|
T (2, "%i", (UChar)x); /* { dg-warning "into a region" } */
|
|
T (3, "%i", (UChar)x); /* { dg-warning "terminating nul past" } */
|
|
T (4, "%i", (UChar)x);
|
|
|
|
/* Verify the same thing using a bit-field. */
|
|
extern struct {
|
|
unsigned int b1: 1;
|
|
unsigned int b2: 2;
|
|
unsigned int b3: 3;
|
|
unsigned int b4: 4;
|
|
int sb4: 4;
|
|
unsigned int b5: 5;
|
|
unsigned int b6: 6;
|
|
unsigned int b7: 7;
|
|
unsigned int b8: 8;
|
|
} bf, abf[], *pbf;
|
|
|
|
T (1, "%i", bf.b1); /* { dg-warning "nul past the end" } */
|
|
T (1, "%i", abf [x].b1); /* { dg-warning "nul past the end" } */
|
|
T (1, "%i", pbf->b1); /* { dg-warning "nul past the end" } */
|
|
/* A one bit bit-field can only be formatted as '0' or '1'. Similarly,
|
|
two- and three-bit bit-fields can only be formatted as a single
|
|
decimal digit. */
|
|
T (2, "%i", bf.b1);
|
|
T (2, "%i", abf [x].b1);
|
|
T (2, "%i", pbf->b1);
|
|
T (2, "%i", bf.b2);
|
|
T (2, "%i", abf [x].b2);
|
|
T (2, "%i", pbf->b2);
|
|
T (2, "%i", bf.b3);
|
|
T (2, "%i", abf [x].b3);
|
|
T (2, "%i", pbf->b3);
|
|
/* A four-bit bit-field can be formatted as either one or two digits. */
|
|
T (2, "%i", bf.b4); /* { dg-warning "nul past the end" } */
|
|
T (2, "%i", abf [x].b4); /* { dg-warning "nul past the end" } */
|
|
T (2, "%i", pbf->b4); /* { dg-warning "nul past the end" } */
|
|
|
|
T (3, "%i", bf.b4);
|
|
T (3, "%i", pbf->b4);
|
|
T (3, "%i", bf.b5);
|
|
T (3, "%i", pbf->b5);
|
|
T (3, "%i", bf.b6);
|
|
T (3, "%i", pbf->b6);
|
|
T (3, "%i", bf.b7); /* { dg-warning "nul past the end" } */
|
|
T (3, "%i", pbf->b7); /* { dg-warning "nul past the end" } */
|
|
|
|
T (1, "%i", bf.b8); /* { dg-warning "into a region" } */
|
|
T (2, "%i", bf.b8); /* { dg-warning "into a region" } */
|
|
/* Formatting an 8-bit unsigned char as a signed short (or any other
|
|
type with greater precision) int can write at most 3 characters. */
|
|
T (3, "%i", bf.b8); /* { dg-warning "terminating nul past" } */
|
|
T (4, "%i", bf.b8);
|
|
|
|
T (1, "%i", bf.b8); /* { dg-warning "into a region" } */
|
|
T (2, "%i", bf.b8); /* { dg-warning "into a region" } */
|
|
T (3, "%i", bf.b8); /* { dg-warning "terminating nul past" } */
|
|
|
|
T (2, "%i", bf.sb4); /* { dg-warning "terminating nul past" } */
|
|
T (3, "%i", bf.sb4);
|
|
T (4, "%i", bf.sb4);
|
|
}
|