summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2024-07-14 13:31:51 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2024-07-14 13:31:51 +0000
commitc829f8ea17076b7df6fff00d7300b3e9a7e05525 (patch)
treec6ebed7b49bf8c868513d585d2eaa262fafc1d96 /lib
parent4160a04015586f3ca1e8998da13a732bf09375bc (diff)
Fix printf(3) signal safety for wide character strings.
The %ls (wide char string) support in printf(3) currently uses malloc(3), which violates the promise in in sigaction(2). This makes it use mmap(2) instead. OK deraadt@
Diffstat (limited to 'lib')
-rw-r--r--lib/libc/stdio/vfprintf.c41
1 files changed, 29 insertions, 12 deletions
diff --git a/lib/libc/stdio/vfprintf.c b/lib/libc/stdio/vfprintf.c
index 759aab5225b..8011ad8c132 100644
--- a/lib/libc/stdio/vfprintf.c
+++ b/lib/libc/stdio/vfprintf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vfprintf.c,v 1.82 2023/10/06 16:41:02 millert Exp $ */
+/* $OpenBSD: vfprintf.c,v 1.83 2024/07/14 13:31:50 millert Exp $ */
/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
@@ -56,6 +56,10 @@
#include "local.h"
#include "fvwrite.h"
+#define PRINTF_PAGESIZE (1 << _MAX_PAGE_SHIFT)
+#define PRINTF_PAGEMASK (PRINTF_PAGESIZE - 1)
+#define PAGEROUND(x) (((x) + (PRINTF_PAGEMASK)) & ~PRINTF_PAGEMASK)
+
union arg {
int intarg;
unsigned int uintarg;
@@ -153,12 +157,13 @@ __sbprintf(FILE *fp, const char *fmt, va_list ap)
* string is null-terminated.
*/
static char *
-__wcsconv(wchar_t *wcsarg, int prec)
+__wcsconv(wchar_t *wcsarg, int prec, char **convbufp, size_t *convbufsizp)
{
+ char *convbuf = *convbufp;
+ size_t convbufsiz = *convbufsizp;
mbstate_t mbs;
char buf[MB_LEN_MAX];
wchar_t *p;
- char *convbuf;
size_t clen, nbytes;
/* Allocate space for the maximum number of bytes we could output. */
@@ -191,15 +196,26 @@ __wcsconv(wchar_t *wcsarg, int prec)
return (NULL);
}
}
- if ((convbuf = malloc(nbytes + 1)) == NULL)
- return (NULL);
+ if (nbytes + 1 > convbufsiz) {
+ if (convbuf != NULL)
+ munmap(convbuf, convbufsiz);
+ convbufsiz = PAGEROUND(nbytes + 1);
+ convbuf = mmap(NULL, convbufsiz, PROT_WRITE|PROT_READ,
+ MAP_ANON|MAP_PRIVATE, -1, 0);
+ if (convbuf == MAP_FAILED) {
+ *convbufp = NULL;
+ *convbufsizp = 0;
+ return (NULL);
+ }
+ *convbufp = convbuf;
+ *convbufsizp = convbufsiz;
+ }
/* Fill the output buffer. */
p = wcsarg;
memset(&mbs, 0, sizeof(mbs));
if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p,
nbytes, &mbs)) == (size_t)-1) {
- free(convbuf);
return (NULL);
}
convbuf[nbytes] = '\0';
@@ -328,6 +344,7 @@ __vfprintf(FILE *fp, const char *fmt0, __va_list ap)
va_list orgap; /* original argument pointer */
#ifdef PRINTF_WIDE_CHAR
char *convbuf; /* buffer for wide to multi-byte conversion */
+ size_t convbufsiz; /* size of convbuf, for munmap() */
#endif
/*
@@ -475,6 +492,7 @@ __vfprintf(FILE *fp, const char *fmt0, __va_list ap)
ret = 0;
#ifdef PRINTF_WIDE_CHAR
convbuf = NULL;
+ convbufsiz = 0;
#endif
/*
@@ -840,8 +858,6 @@ fp_common:
if (flags & LONGINT) {
wchar_t *wcp;
- free(convbuf);
- convbuf = NULL;
if ((wcp = GETARG(wchar_t *)) == NULL) {
struct syslog_data sdata = SYSLOG_DATA_INIT;
int save_errno = errno;
@@ -852,12 +868,12 @@ fp_common:
cp = "(null)";
} else {
- convbuf = __wcsconv(wcp, prec);
- if (convbuf == NULL) {
+ cp = __wcsconv(wcp, prec, &convbuf,
+ &convbufsiz);
+ if (cp == NULL) {
ret = -1;
goto error;
}
- cp = convbuf;
}
} else
#endif /* PRINTF_WIDE_CHAR */
@@ -1072,7 +1088,8 @@ overflow:
finish:
#ifdef PRINTF_WIDE_CHAR
- free(convbuf);
+ if (convbuf != NULL)
+ munmap(convbuf, convbufsiz);
#endif
#ifdef FLOATING_POINT
if (dtoaresult)