]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
crypto/bio/bio_print.c: make %n in line with other libc implementations
authorEugene Syromiatnikov <esyr@openssl.org>
Fri, 15 Aug 2025 14:00:50 +0000 (16:00 +0200)
committerNeil Horman <nhorman@openssl.org>
Fri, 29 Aug 2025 16:18:30 +0000 (12:18 -0400)
The standard[1] is pretty vague in its definition of the %n specifier
by using "the number of bytes written to the output so far", without
actually elaborating, whether only the actually written bytes, or the bytes
that would be written (but discarded) are used;  the consensus across
implementations, however, seems to gravitate towards the latter.  Track
the virtual "write position" separately and use its value when %n format
is occurred.  That also means that we cannot finish the output early
upon reach of the end of buffer (unless we made sure that no %n specifiers
occur in the remainder of the format string).

[1] https://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html

Signed-off-by: Eugene Syromiatnikov <esyr@openssl.org>
Reviewed-by: Saša Nedvědický <sashan@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/28177)

crypto/bio/bio_print.c

index 82aecf82acf7dad01aab2a03010767f9aec474f2..186c3cdef2b8c6e0d94eaabb252086b78adf6c05 100644 (file)
@@ -37,6 +37,8 @@ struct pr_desc {
     size_t currlen;
     /** Buffer size */
     size_t maxlen;
+    /** "Write position", for proper %n support */
+    long long pos;
 };
 
 static int fmtstr(struct pr_desc *, const char *, int, int, int);
@@ -110,7 +112,7 @@ _dopr(char **sbuffer,
     int state;
     int flags;
     int cflags;
-    struct pr_desc desc = { *sbuffer, buffer, 0, *maxlen };
+    struct pr_desc desc = { *sbuffer, buffer, 0, *maxlen, 0 };
     int ret = 0;
 
     state = DP_S_DEFAULT;
@@ -119,7 +121,7 @@ _dopr(char **sbuffer,
     ch = *format++;
 
     while (state != DP_S_DONE) {
-        if (ch == '\0' || (buffer == NULL && currlen >= *maxlen))
+        if (ch == '\0')
             state = DP_S_DONE;
 
         switch (state) {
@@ -388,11 +390,7 @@ _dopr(char **sbuffer,
                     goto out;
                 break;
             case 'n':
-                {
-                    int *num;
-
-                    num = va_arg(args, int *);
-                    *num = (int)desc.currlen;
+                    *num = (int)desc.pos;
                 }
                 break;
             case '%':
@@ -941,6 +939,8 @@ doapr_outch(struct pr_desc *desc, int c)
             (*(desc->buffer))[(desc->currlen)++] = (char)c;
     }
 
+    desc->pos++;
+
     return 1;
 }