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)

Reply via email to