In doabbr(), the zone line's FORMAT field (zp->z_format) was passed
directly as the format argument of sprintf():
sprintf(abbr, format, letters);
FORMAT comes from zone data, which is not always trusted: zic is also
used to compile third-party or user-supplied zone description files.
Passing such data as a printf format string is the classic
uncontrolled-format-string anti-pattern (CWE-134 / CERT FIO30-C), and
it is what makes this line the sole -Wformat-nonliteral warning in
doabbr under -Wformat=2.
inzsub() already restricts a slash-less FORMAT to at most one '%s'
conversion (a '%z' is rewritten to '%s' earlier), so expand that lone
conversion explicitly with mempcpy()/strcpy() instead of handing the
data to sprintf(). This removes the format-string sink while keeping
the output byte-for-byte identical for every valid FORMAT, so the
generated TZif files are unchanged.
* zic.c (doabbr): Expand FORMAT's single '%s' by hand rather than
using FORMAT as a printf format string.
---
diff --git a/zic.c b/zic.c
index af2bb80..7cc48ef 100644
--- a/zic.c
+++ b/zic.c
@@ -3135,7 +3135,15 @@ doabbr(char *abbr, struct zone const *zp, char const
*letters,
letters = "%s";
else if (letters == disable_percent_s)
return 0;
- sprintf(abbr, format, letters);
+ /* Expand FORMAT's lone "%s" (see inzsub) without treating
+ FORMAT, which comes from zone data, as a printf format string. */
+ cp = strchr(format, '%');
+ if (cp) {
+ char *dp = mempcpy(abbr, format, cp - format);
+ dp = mempcpy(dp, letters, strlen(letters));
+ strcpy(dp, cp + 2);
+ } else
+ strcpy(abbr, format);
} else if (isdst)
strcpy(abbr, slashp + 1);
else {
--
2.50.1 (Apple Git-155)