summaryrefslogtreecommitdiffstats
path: root/sys/kern/subr_sbuf.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>2003-05-25 19:03:08 +0000
committerpeter <peter@FreeBSD.org>2003-05-25 19:03:08 +0000
commit54155a49a5a2b84f07b579c8e6af7e5f9c8e1d88 (patch)
tree3b4a1e1ad4c12a1a6e7c82cd29ea612f2c6723b7 /sys/kern/subr_sbuf.c
parent4c8aa154ff7fbfb6beeb7489a9418f8b93e91b12 (diff)
downloadFreeBSD-src-54155a49a5a2b84f07b579c8e6af7e5f9c8e1d88.zip
FreeBSD-src-54155a49a5a2b84f07b579c8e6af7e5f9c8e1d88.tar.gz
Copy the va_list in sbuf_vprintf() before passing it to vsnprintf(),
because we could fail due to a small buffer and loop and rerun. If this happens, then the vsnprintf() will have already taken the arguments off the va_list. For i386 and others, this doesn't matter because the va_list type is a passed as a copy. But on powerpc and amd64, this is fatal because the va_list is a reference to an external structure that keeps the vararg state due to the more complicated argument passing system. On amd64, arguments can be passed as follows: First 6 int/pointer type arguments go in registers, the rest go on the memory stack. Float and double are similar, except using SSE registers. long double (80 bit precision) are similar except using the x87 stack. Where the 'next argument' comes from depends on how many have been processed so far and what type it is. For amd64, gcc keeps this state somewhere that is referenced by the va_list. I found a description that showed the va_copy was required here: http://mirrors.ccs.neu.edu/cgi-bin/unixhelp/man-cgi?va_end+9 The single unix spec doesn't mention va_copy() at all. Anyway, the problem was that the sysctl kern.geom.conf* nodes would panic due to walking off the end of the va_arg lists in vsnprintf. A better fix would be to have sbuf_vprintf() use a single pass and call kvprintf() with a callback function that stored the results and grew the buffer as needed. Approved by: re (scottl)
Diffstat (limited to 'sys/kern/subr_sbuf.c')
-rw-r--r--sys/kern/subr_sbuf.c5
1 files changed, 4 insertions, 1 deletions
diff --git a/sys/kern/subr_sbuf.c b/sys/kern/subr_sbuf.c
index 9e63e8e..80947a4 100644
--- a/sys/kern/subr_sbuf.c
+++ b/sys/kern/subr_sbuf.c
@@ -399,6 +399,7 @@ sbuf_cpy(struct sbuf *s, const char *str)
int
sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
{
+ va_list ap_copy;
int len;
assert_sbuf_integrity(s);
@@ -411,8 +412,10 @@ sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
return (-1);
do {
+ va_copy(ap_copy, ap);
len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
- fmt, ap);
+ fmt, ap_copy);
+ va_end(ap_copy);
} while (len > SBUF_FREESPACE(s) &&
sbuf_extend(s, len - SBUF_FREESPACE(s)) == 0);
OpenPOWER on IntegriCloud