--- /dev/null
+/* { dg-do compile }
+ { dg-options "-O2 -Wall -Wformat-overflow=1 -ftrack-macro-expansion=0" }
+ { dg-require-effective-target int32plus } */
+
+typedef __SIZE_TYPE__ size_t;
+typedef __WCHAR_TYPE__ wchar_t;
+
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-INT_MAX - 1)
+
+/* 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
+
+void sink (char*, char*);
+
+int dummy_sprintf (char*, const char*, ...);
+
+char buffer [256];
+extern char *ptr;
+
+int int_range (int min, int max)
+{
+ extern int int_value (void);
+ int n = int_value ();
+ return n < min || max < n ? min : n;
+}
+
+unsigned uint_range (unsigned min, unsigned max)
+{
+ extern unsigned uint_value (void);
+ unsigned n = uint_value ();
+ return n < min || max < n ? min : n;
+}
+
+/* Evaluate to an array of SIZE characters when non-negative, or to
+ a pointer to an unknown object otherwise. */
+#define buffer(size) \
+ ((0 <= size) ? buffer + sizeof buffer - (size) : ptr)
+
+/* Helper to expand function to either __builtin_f or dummy_f to
+ make debugging GCC easy. */
+#define FUNC(f) \
+ ((!LINE || LINE == __LINE__) ? __builtin_ ## f : dummy_ ## f)
+
+/* Macro to verify that calls to __builtin_sprintf (i.e., with no size
+ argument) issue diagnostics by correctly determining the size of
+ the destination buffer. */
+#define T(size, ...) \
+ (FUNC (sprintf) (buffer (size), __VA_ARGS__), \
+ sink (buffer, ptr))
+
+/* Return a signed integer in the range [MIN, MAX]. */
+#define R(min, max) int_range (min, max)
+
+/* Verify warnings and ranges for certain overflow. */
+void test_min_overflow (int i)
+{
+ T (0, "%#hho", i); /* { dg-warning "between 1 and 4 bytes" } */
+ T (0, "%#1hho", i); /* { dg-warning "between 1 and 4 bytes" } */
+ T (0, "%#2hho", i); /* { dg-warning "between 2 and 4 bytes" } */
+ T (0, "%#3hho", i); /* { dg-warning "between 3 and 4 bytes" } */
+ T (0, "%#4hho", i); /* { dg-warning "writing 4 bytes" } */
+ T (0, "%#hho", R (-1, 0)); /* { dg-warning "between 1 and 4 bytes" } */
+ T (0, "%#1hho", R (-1, 0)); /* { dg-warning "between 1 and 4 bytes" } */
+ T (0, "%#2hho", R (-1, 0)); /* { dg-warning "between 2 and 4 bytes" } */
+ T (0, "%#3hho", R (-1, 0)); /* { dg-warning "between 3 and 4 bytes" } */
+ T (0, "%#4hho", R (-1, 0)); /* { dg-warning "writing 4 bytes" } */
+ T (0, "%#hho", R (-1, 1)); /* { dg-warning "between 1 and 4 bytes" } */
+ T (0, "%#1hho", R (-1, 1)); /* { dg-warning "between 1 and 4 bytes" } */
+ T (0, "%#2hho", R (-1, 1)); /* { dg-warning "between 2 and 4 bytes" } */
+ T (0, "%#3hho", R (-1, 1)); /* { dg-warning "between 3 and 4 bytes" } */
+ T (0, "%#4hho", R (-1, 1)); /* { dg-warning "writing 4 bytes" } */
+ T (0, "%#hho", R ( 0, 1)); /* { dg-warning "between 1 and 2 bytes" } */
+ T (0, "%#1hho", R ( 0, 1)); /* { dg-warning "between 1 and 2 bytes" } */
+ T (0, "%#2hho", R ( 0, 1)); /* { dg-warning "writing 2 bytes" } */
+ T (0, "%#3hho", R ( 0, 1)); /* { dg-warning "writing 3 bytes" } */
+ T (0, "%#4hho", R ( 0, 1)); /* { dg-warning "writing 4 bytes" } */
+ T (0, "%#hho", R ( 1, 2)); /* { dg-warning "writing 2 bytes" } */
+ T (0, "%#1hho", R ( 1, 2)); /* { dg-warning "writing 2 bytes" } */
+ T (0, "%#2hho", R ( 1, 2)); /* { dg-warning "writing 2 bytes" } */
+ T (0, "%#3hho", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */
+ T (0, "%#4hho", R ( 1, 2)); /* { dg-warning "writing 4 bytes" } */
+
+ T (0, "%#ho", i); /* { dg-warning "between 1 and 7 bytes" } */
+ T (0, "%#.*ho", /* { dg-warning "between 1 and 7 bytes" } */
+ R (0, 2), i);
+ T (0, "%#.*ho", /* { dg-warning "between 1 and 7 bytes" } */
+ R (1, 2), i);
+ T (0, "%#.*ho", /* { dg-warning "between 2 and 7 bytes" } */
+ R (2, 3), i);
+ T (0, "%#.*ho", /* { dg-warning "between 3 and 7 bytes" } */
+ R (3, 4), i);
+ T (0, "%#.*ho", /* { dg-warning "between 7 and 8 bytes" } */
+ R (7, 8), i);
+
+ T (0, "%#ho", R (-1, 0)); /* { dg-warning "between 1 and 7 bytes" } */
+ T (0, "%#ho", R (-1, 1)); /* { dg-warning "between 1 and 7 bytes" } */
+ T (0, "%#ho", R ( 0, 1)); /* { dg-warning "between 1 and 2 bytes" } */
+ T (0, "%#ho", R ( 1, 2)); /* { dg-warning "writing 2 bytes" } */
+
+ T (0, "%#o", i); /* { dg-warning "between 1 and 12 bytes" } */
+ T (0, "%#o", R (-1, 0)); /* { dg-warning "between 1 and 12 bytes" } */
+ T (0, "%#o", R (-1, 1)); /* { dg-warning "between 1 and 12 bytes" } */
+ T (0, "%#o", R ( 0, 1)); /* { dg-warning "between 1 and 2 bytes" } */
+ T (0, "%#o", R ( 1, 2)); /* { dg-warning "writing 2 bytes" } */
+
+ T (0, "%#hhx", i); /* { dg-warning "between 1 and 4 bytes" } */
+ T (0, "%#.*hhx", /* { dg-warning "writing up to 4 bytes" } */
+ R (0, 2), i);
+ T (0, "%#.*hhx", /* { dg-warning "between 1 and 4 bytes" } */
+ R (1, 2), i);
+ T (0, "%#.*hhx", /* { dg-warning "between 2 and 5 bytes" } */
+ R (2, 3), i);
+ T (0, "%#.*hhx", /* { dg-warning "between 3 and 6 bytes" } */
+ R (3, 4), i);
+
+ T (0, "%#hhx", R (-1, 0)); /* { dg-warning "between 1 and 4 bytes" } */
+ T (0, "%#hhx", R (-1, 1)); /* { dg-warning "between 1 and 4 bytes" } */
+ T (0, "%#hhx", R ( 0, 1)); /* { dg-warning "between 1 and 3 bytes" } */
+ T (0, "%#hhx", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */
+
+ T (0, "%#hx", i); /* { dg-warning "between 1 and 6 bytes" } */
+ T (0, "%#hx", R (-1, 0)); /* { dg-warning "between 1 and 6 bytes" } */
+ T (0, "%#hx", R (-1, 1)); /* { dg-warning "between 1 and 6 bytes" } */
+ T (0, "%#hx", R ( 0, 1)); /* { dg-warning "between 1 and 3 bytes" } */
+ T (0, "%#hx", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */
+
+ T (0, "%#x", i); /* { dg-warning "between 1 and 10 bytes" } */
+ T (0, "%#x", R (-1, 0)); /* { dg-warning "between 1 and 10 bytes" } */
+ T (0, "%#x", R (-1, 1)); /* { dg-warning "between 1 and 10 bytes" } */
+ T (0, "%#x", R ( 0, 1)); /* { dg-warning "between 1 and 3 bytes" } */
+ T (0, "%#x", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */
+}
+
+/* Verify warnings and ranges for likely overflow. */
+void test_likely_overflow (int i)
+{
+ T (2, "%#hho", i); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#1hho", i); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#2hho", i); /* { dg-warning "writing a terminating nul" } */
+ T (2, "%#3hho", i); /* { dg-warning "between 3 and 4 bytes" } */
+ T (2, "%#4hho", i); /* { dg-warning "writing 4 bytes" } */
+ T (2, "%#hho", R (-1, 0)); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#1hho", R (-1, 0));/* { dg-warning "may write a terminating nul" } */
+ T (2, "%#2hho", R (-1, 0));/* { dg-warning "writing a terminating nul" } */
+ T (2, "%#3hho", R (-1, 0));/* { dg-warning "between 3 and 4 bytes" } */
+ T (2, "%#4hho", R (-1, 0));/* { dg-warning "writing 4 bytes" } */
+ T (2, "%#hho", R (-1, 1)); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#1hho", R (-1, 1));/* { dg-warning "may write a terminating nul" } */
+ T (2, "%#2hho", R (-1, 1));/* { dg-warning "writing a terminating nul" } */
+ T (2, "%#3hho", R (-1, 1));/* { dg-warning "between 3 and 4 bytes" } */
+ T (2, "%#4hho", R (-1, 1));/* { dg-warning "writing 4 bytes" } */
+ T (2, "%#hho", R ( 0, 1)); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#1hho", R ( 0, 1));/* { dg-warning "may write a terminating nul" } */
+ T (2, "%#2hho", R ( 0, 1));/* { dg-warning "writing a terminating nul" } */
+ T (2, "%#3hho", R ( 0, 1));/* { dg-warning "writing 3 bytes" } */
+ T (2, "%#4hho", R ( 0, 1));/* { dg-warning "writing 4 bytes" } */
+ T (2, "%#hho", R ( 1, 2)); /* { dg-warning "writing a terminating nul" } */
+ T (2, "%#1hho", R ( 1, 2));/* { dg-warning "writing a terminating nul" } */
+ T (2, "%#2hho", R ( 1, 2));/* { dg-warning "writing a terminating nul" } */
+ T (2, "%#3hho", R ( 1, 2));/* { dg-warning "writing 3 bytes" } */
+ T (2, "%#4hho", R ( 1, 2));/* { dg-warning "writing 4 bytes" } */
+
+ T (2, "%#ho", i); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#ho", R (-1, 0)); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#ho", R (-1, 1)); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#ho", R ( 0, 1)); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#ho", R ( 1, 2)); /* { dg-warning "writing a terminating nul" } */
+
+ T (2, "%#o", i); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#o", R (-1, 0)); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#o", R (-1, 1)); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#o", R ( 0, 1)); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%#o", R ( 1, 2)); /* { dg-warning "writing a terminating nul" } */
+
+ T (2, "%#hhx", i); /* { dg-warning "between 1 and 4 bytes" } */
+ T (2, "%#1hhx", i); /* { dg-warning "between 1 and 4 bytes" } */
+ T (2, "%#2hhx", i); /* { dg-warning "between 2 and 4 bytes" } */
+ T (2, "%#3hhx", i); /* { dg-warning "between 3 and 4 bytes" } */
+ T (2, "%#4hhx", i); /* { dg-warning "writing 4 bytes" } */
+ T (2, "%#1hhx", R (-1, 0));/* { dg-warning "between 1 and 4 bytes" } */
+ T (2, "%#2hhx", R (-1, 0));/* { dg-warning "between 2 and 4 bytes" } */
+ T (2, "%#3hhx", R (-1, 0));/* { dg-warning "between 3 and 4 bytes" } */
+ T (2, "%#4hhx", R (-1, 0));/* { dg-warning "writing 4 bytes" } */
+ T (2, "%#hhx", R (-1, 0)); /* { dg-warning "between 1 and 4 bytes" } */
+ T (2, "%#1hhx", R (-1, 0));/* { dg-warning "between 1 and 4 bytes" } */
+ T (2, "%#2hhx", R (-1, 0));/* { dg-warning "between 2 and 4 bytes" } */
+ T (2, "%#3hhx", R (-1, 0));/* { dg-warning "between 3 and 4 bytes" } */
+ T (2, "%#4hhx", R (-1, 0));/* { dg-warning "writing 4 bytes" } */
+ T (2, "%#hhx", R (-1, 1)); /* { dg-warning "between 1 and 4 bytes" } */
+ T (2, "%#1hhx", R (-1, 1));/* { dg-warning "between 1 and 4 bytes" } */
+ T (2, "%#2hhx", R (-1, 1));/* { dg-warning "between 2 and 4 bytes" } */
+ T (2, "%#3hhx", R (-1, 1));/* { dg-warning "between 3 and 4 bytes" } */
+ T (2, "%#4hhx", R (-1, 1));/* { dg-warning "writing 4 bytes" } */
+ T (2, "%#hhx", R ( 0, 1)); /* { dg-warning "between 1 and 3 bytes" } */
+ T (2, "%#1hhx", R ( 0, 1));/* { dg-warning "between 1 and 3 bytes" } */
+ T (2, "%#2hhx", R ( 0, 1));/* { dg-warning "between 2 and 3 bytes" } */
+ T (2, "%#3hhx", R ( 0, 1));/* { dg-warning "writing 3 bytes" } */
+ T (2, "%#4hhx", R ( 0, 1));/* { dg-warning "writing 4 bytes" } */
+ T (2, "%#hhx", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */
+ T (2, "%#1hhx", R ( 1, 2));/* { dg-warning "writing 3 bytes" } */
+ T (2, "%#2hhx", R ( 1, 2));/* { dg-warning "writing 3 bytes" } */
+ T (2, "%#3hhx", R ( 1, 2));/* { dg-warning "writing 3 bytes" } */
+ T (2, "%#4hhx", R ( 1, 2));/* { dg-warning "writing 4 bytes" } */
+
+ T (2, "%#hx", i); /* { dg-warning "between 1 and 6 bytes" } */
+ T (2, "%#hx", R (-1, 0)); /* { dg-warning "between 1 and 6 bytes" } */
+ T (2, "%#hx", R (-1, 1)); /* { dg-warning "between 1 and 6 bytes" } */
+ T (2, "%#hx", R ( 0, 1)); /* { dg-warning "between 1 and 3 bytes" } */
+ T (2, "%#hx", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */
+
+ T (2, "%#x", i); /* { dg-warning "between 1 and 10 bytes" } */
+ T (2, "%#x", R (-1, 0)); /* { dg-warning "between 1 and 10 bytes" } */
+ T (2, "%#x", R (-1, 1)); /* { dg-warning "between 1 and 10 bytes" } */
+ T (2, "%#x", R ( 0, 1)); /* { dg-warning "between 1 and 3 bytes" } */
+ T (2, "%#x", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */
+}
+
+/* Verify warnings likely overflow due to the terminating nul. */
+void test_likely_nul_overflow (int i)
+{
+ T (3, "%#hho", i);
+ T (3, "%#hho", R (-1, 0));
+ T (3, "%#hho", R (-1, 1));
+ T (3, "%#hho", R ( 0, 1));
+ T (3, "%#hho", R ( 1, 2));
+
+ T (3, "%#ho", i);
+ T (3, "%#ho", R (-1, 0));
+ T (3, "%#ho", R (-1, 1));
+ T (3, "%#ho", R ( 0, 1));
+ T (3, "%#ho", R ( 1, 2));
+
+ T (3, "%#o", i);
+ T (3, "%#o", R (-1, 0));
+ T (3, "%#o", R (-1, 1));
+ T (3, "%#o", R ( 0, 1));
+ T (3, "%#o", R ( 1, 2));
+
+ T (3, "%#hhx", i); /* { dg-warning "may write a terminating nul" } */
+ T (3, "%#hhx", R (-1, 0)); /* { dg-warning "may write a terminating nul" } */
+ T (3, "%#hhx", R (-1, 1)); /* { dg-warning "may write a terminating nul" } */
+ T (3, "%#hhx", R ( 0, 1)); /* { dg-warning "may write a terminating nul" } */
+ T (3, "%#hhx", R ( 1, 2)); /* { dg-warning "writing a terminating nul" } */
+
+ T (3, "%#hx", i); /* { dg-warning "may write a terminating nul" } */
+ T (3, "%#hx", R (-1, 0)); /* { dg-warning "may write a terminating nul" } */
+ T (3, "%#hx", R (-1, 1)); /* { dg-warning "may write a terminating nul" } */
+ T (3, "%#hx", R ( 0, 1)); /* { dg-warning "may write a terminating nul" } */
+ T (3, "%#hx", R ( 1, 2)); /* { dg-warning "writing a terminating nul" } */
+
+ T (3, "%#x", i); /* { dg-warning "may write a terminating nul" } */
+ T (3, "%#x", R (-1, 0)); /* { dg-warning "may write a terminating nul" } */
+ T (3, "%#x", R (-1, 1)); /* { dg-warning "may write a terminating nul" } */
+ T (3, "%#x", R ( 0, 1)); /* { dg-warning "may write a terminating nul" } */
+ T (3, "%#x", R ( 1, 2)); /* { dg-warning "writing a terminating nul" } */
+}