]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Guard against unsafe conditions in usage of pg_strftime().
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 11 May 2026 12:13:46 +0000 (05:13 -0700)
committerNoah Misch <noah@leadboat.com>
Mon, 11 May 2026 12:13:46 +0000 (05:13 -0700)
Although pg_strftime() has defined error conditions, no callers bother
to check for errors.  This is problematic because the output string is
very likely not null-terminated if an error occurs, so that blindly
using it is unsafe.  Rather than trusting that we can find and fix all
the callers, let's alter the function's API spec slightly: make it
guarantee a null-terminated result so long as maxsize > 0.

Furthermore, if we do get an error, let's make that null-terminated
result be an empty string.  We could instead truncate at the buffer
length, but that risks producing mis-encoded output if the tz_name
string contains multibyte characters.  It doesn't seem reasonable for
src/timezone/ to make use of our encoding-aware truncation logic.
Also, the only really likely source of a failure is a user-supplied
timezone name that is intentionally trying to overrun our buffers.
I don't feel a need to be particularly friendly about that case.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: John Naylor <johncnaylorls@gmail.com>
Backpatch-through: 14
Security: CVE-2026-6474

src/timezone/strftime.c

index 9247a34157f78eae7332ec45ab004237b91aff2a..3da0b4d765842326ddcdb733e6e7e3a7fa75db89 100644 (file)
@@ -122,6 +122,13 @@ static char *_yconv(int a, int b, bool convert_top, bool convert_yy, char *pt, c
  * Convert timestamp t to string s, a caller-allocated buffer of size maxsize,
  * using the given format pattern.
  *
+ * Unlike standard strftime(), we guarantee to provide a null-terminated
+ * result even on failure, so long as maxsize > 0.  If we overrun the buffer,
+ * return an empty string rather than risking mis-encoded multibyte output.
+ * (Since this module only supports C locale, you might think multibyte
+ * characters are impossible --- but the time zone name printed by %Z comes
+ * from outside and could contain such.)
+ *
  * See also timestamptz_to_str.
  */
 size_t
@@ -135,11 +142,15 @@ pg_strftime(char *s, size_t maxsize, const char *format, const struct pg_tm *t)
        if (!p)
        {
                errno = EOVERFLOW;
+               if (maxsize > 0)
+                       *s = '\0';
                return 0;
        }
        if (p == s + maxsize)
        {
                errno = ERANGE;
+               if (maxsize > 0)
+                       *s = '\0';
                return 0;
        }
        *p = '\0';