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)
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);
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;
ch = *format++;
while (state != DP_S_DONE) {
- if (ch == '\0' || (buffer == NULL && currlen >= *maxlen))
+ if (ch == '\0')
state = DP_S_DONE;
switch (state) {
goto out;
break;
case 'n':
- {
- int *num;
-
- num = va_arg(args, int *);
- *num = (int)desc.currlen;
+ *num = (int)desc.pos;
}
break;
case '%':
(*(desc->buffer))[(desc->currlen)++] = (char)c;
}
+ desc->pos++;
+
return 1;
}