Bugtraq mailing list archives
better snprintf replacement, anyone?
From: deraadt () CVS OPENBSD ORG (Theo de Raadt)
Date: Sat, 19 Jul 1997 20:03:46 -0600
Quite often I find people saying to me "Why do you use snprintf() all over the place to avoid buffer overflows, and not try to use other techniques. Using snprintf() makes it hard for us to port the code to legacy systems." It is a rather annoying problem to not have snprintf() on these legacy systems, because over the last year or so snprintf() calls have started popping up nearly everywhere where security is an issue. There's a couple thousand snprintf() calls in the OpenBSD source tree. Many packages now come with their own snprintf() variants, sendmail, who knows what else... A common complaint and worry is that those snprintf() and vsnprintf() variants may not emulate the _exact_ behaviour of the native system's sprintf call. This can be an issue. Here's something I threw together yesterday, to see if this technique will help solve the problem. The code has only received a small amount of testing (thanks to those who helped me test it, and pointed out problems). I'd like to hear from people if they come up with any further improvements. Hopefully this can drop into any piece of code and work exactly as expected. Newer versions will show up at http://theos.com/~deraadt/snprintf.c /* * Copyright (c) 1997 Theo de Raadt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <sys/param.h> #include <sys/types.h> #include <sys/mman.h> #include <signal.h> #include <stdio.h> #if __STDC__ #include <stdarg.h> #include <stdlib.h> #else #include <varargs.h> #endif #include <setjmp.h> static int pgsize; static char *curobj; static int caught; static jmp_buf bail; static char * msetup(str, n) char *str; size_t n; { char *e; if (pgsize == 0) pgsize = getpagesize(); if (n == 0) { *str = '\0'; return 0; } curobj = (char *)malloc(n + pgsize * 2); if (curobj == NULL) return NULL; e = curobj + n; e = (char *)roundup((unsigned long)e, pgsize); if (mprotect(e, pgsize, PROT_NONE) == -1) { free(curobj); curobj = NULL; return NULL; } return (e - n - 2); /* XXX: why 2? you don't want to know */ } static void mcatch() { longjmp(bail, 1); } static void mcleanup(str, n, p) char *str; size_t n; char *p; { strncpy(str, p, n-1); str[n-1] = '\0'; free(curobj); if (mprotect((caddr_t)(p + n), pgsize, PROT_READ|PROT_WRITE|PROT_EXEC) == 0) return; mprotect((caddr_t)(p + n), pgsize, PROT_READ|PROT_WRITE); } int #if __STDC__ snprintf(char *str, size_t n, char const *fmt, ...) #else snprintf(str, n, fmt, va_alist) char *str; size_t n; char *fmt; va_dcl #endif { va_list ap; int ret; char *p; void (*sigsegv)() = NULL; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif p = msetup(str, n); if (p == NULL) return 0; if (setjmp(bail) == 0) { sigsegv = signal(SIGSEGV, mcatch); ret = vsprintf(p, fmt, ap); } va_end(ap); mcleanup(str, n, p); (void) signal(SIGSEGV, sigsegv); return (ret); } int vsnprintf(str, n, fmt, ap) char *str; size_t n; char *fmt; char *ap; { int ret; char *p; void (*sigsegv)() = NULL; p = msetup(str, n); if (p == NULL) return 0; if (setjmp(bail) == 0) { sigsegv = signal(SIGSEGV, mcatch); ret = vsprintf(p, fmt, ap); } mcleanup(str, n, p); (void) signal(SIGSEGV, sigsegv); return (ret); }
Current thread:
- better snprintf replacement, anyone? Theo de Raadt (Jul 19)
- Re: better snprintf replacement, anyone? Steve \ (Jul 21)
- Re: better snprintf replacement, anyone? Manoj Kasichainula (Jul 21)
- Re: better snprintf replacement, anyone? Theo de Raadt (Jul 21)
- Re: better snprintf replacement, anyone? Alan Cox (Jul 22)
- Re: better snprintf replacement, anyone? James Bonfield (Jul 22)
- ld.so vulnerability Aleph One (Jul 22)
- Security hole in exim 1.62: local root exploit Aleph One (Jul 22)
- Re: Security hole in exim 1.62: local root exploit Warner Losh (Jul 22)
- Named Config Files Gus Huber (Jul 22)
- Re: Named Config Files Aveek Datta (Jul 22)
- Re: better snprintf replacement, anyone? Steve \ (Jul 21)