diff options
author | kris <kris@FreeBSD.org> | 2001-04-17 07:59:52 +0000 |
---|---|---|
committer | kris <kris@FreeBSD.org> | 2001-04-17 07:59:52 +0000 |
commit | 0f958ee7467ce3cd3eaedbef81be48790d0b6b1a (patch) | |
tree | 3a26f765580b87bb8dbbe23a52082eaf344945c5 | |
parent | b6da0fca774d229c4776e41d019373d1ec0df7ab (diff) | |
download | FreeBSD-src-0f958ee7467ce3cd3eaedbef81be48790d0b6b1a.zip FreeBSD-src-0f958ee7467ce3cd3eaedbef81be48790d0b6b1a.tar.gz |
Add fmtcheck(), a function for checking consistency of format string
arguments where the format string is obtained from user data, or
otherwise difficult to verify statically.
Example usage:
printf(fmtcheck(user_format, standard_format), arg1, arg2);
checks the format string user_format for consistency (same number/order/
type of format operators) with standard_format. If they differ,
standard_format is used instead to avoid potential crashes or security
violations.
Obtained from: NetBSD
Reviewed by: -arch
-rw-r--r-- | include/stdio.h | 2 | ||||
-rw-r--r-- | lib/libc/gen/Makefile.inc | 4 | ||||
-rw-r--r-- | lib/libc/gen/fmtcheck.3 | 100 | ||||
-rw-r--r-- | lib/libc/gen/fmtcheck.c | 253 |
4 files changed, 357 insertions, 2 deletions
diff --git a/include/stdio.h b/include/stdio.h index e8c346b..520c622 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -299,6 +299,8 @@ __END_DECLS __BEGIN_DECLS int asprintf __P((char **, const char *, ...)) __printflike(2, 3); char *ctermid_r __P((char *)); +const char *fmtcheck __P((const char *, const char *)) + __attribute__((__format_arg__(2))); char *fgetln __P((FILE *, size_t *)); int fpurge __P((FILE *)); int fseeko __P((FILE *, _BSD_OFF_T_, int)); diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index eba9beb..96ec3b9 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -9,7 +9,7 @@ SRCS+= __xuname.c _pthread_stubs.c _rand48.c _spinlock_stub.c _thread_init.c \ clock.c closedir.c confstr.c \ crypt.c ctermid.c daemon.c devname.c dirname.c disklabel.c \ dlfcn.c drand48.c erand48.c err.c errlst.c \ - exec.c fnmatch.c fstab.c ftok.c fts.c getbootfile.c getbsize.c \ + exec.c fmtcheck.c fnmatch.c fstab.c ftok.c fts.c getbootfile.c getbsize.c \ getcap.c getcwd.c getdomainname.c getgrent.c getgrouplist.c \ gethostname.c getloadavg.c getlogin.c getmntinfo.c getnetgrent.c \ getobjformat.c getosreldate.c getpagesize.c \ @@ -38,7 +38,7 @@ MAN+= alarm.3 arc4random.3 clock.3 \ basename.3 \ confstr.3 ctermid.3 daemon.3 \ devname.3 directory.3 dirname.3 dladdr.3 dllockinit.3 dlopen.3 \ - err.3 exec.3 fnmatch.3 frexp.3 ftok.3 fts.3 \ + err.3 exec.3 fmtcheck.3 fnmatch.3 frexp.3 ftok.3 fts.3 \ getbootfile.3 getbsize.3 getcap.3 getcwd.3 \ getdiskbyname.3 getdomainname.3 getfsent.3 \ getgrent.3 getgrouplist.3 gethostname.3 getloadavg.3 \ diff --git a/lib/libc/gen/fmtcheck.3 b/lib/libc/gen/fmtcheck.3 new file mode 100644 index 0000000..d52ea48 --- /dev/null +++ b/lib/libc/gen/fmtcheck.3 @@ -0,0 +1,100 @@ +.\" Copyright (c) 2000 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This file was contributed to The NetBSD Foundation by Allen Briggs. +.\" +.\" 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the NetBSD +.\" Foundation, Inc. and its contributors. +.\" 4. Neither the name of The NetBSD Foundation nor the names of its +.\" contributors may be used to endorse or promote products derived +.\" from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``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 FOUNDATION OR CONTRIBUTORS +.\" 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. +.\" +.\" $FreeBSD$ +.Dd October 17, 2000 +.Os +.Dt FMTCHECK 3 +.Sh NAME +.Nm fmtcheck +.Nd sanitizes user-supplied printf(3)-style format string +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.Fd #include <stdio.h> +.Ft const char * +.Fn fmtcheck "const char *fmt_suspect" "const char *fmt_default" +.Sh DESCRIPTION +The +.Nm +function scans +.Fa fmt_suspect +and +.Fa fmt_default +to determine if +.Fa fmt_suspect +will consume the same argument types as +.Fa fmt_default +and to ensure that +.Fa fmt_suspect +is a valid format string. +.Pp +The +.Xr printf 3 +family of functions can not verify the types of arguments that they are +passed at run-time. In some cases, like +.Xr catgets 3 , +it is useful or necessary to use a user-supplied format string with no +guarantee that the format string matches the specified parameters. +.Pp +The +.Nm +function was designed to be used in these cases, as in: +.Bd -literal -offset indent +printf(fmtcheck(user_format, standard_format), arg1, arg2); +.Ed +.Pp +In the check, field widths, fillers, precisions, etc. are ignored (unless +the field width or precision is an asterisk +.Ql * +instead of a digit string). Also, any text other than the format specifiers +is completely ignored. +.Pp +Note that the formats may be quite different as long as they accept the +same parameters. For example, ".Dq %p %o %30s %#llx %-10.*e %n" is +compatible with "This number %lu %d%% and string %s has %qd numbers +and %.*g floats (%n)." However, "%o" is not equivalent to "%lx" because +the first requires an integer and the second requires a long. +.Sh RETURN VALUES +If +.Fa fmt_suspect +is a valid format and consumes the same argument types as +.Fa fmt_default , +then the +.Nm +function will return +.Fa fmt_suspect . +Otherwise, it will return +.Fa fmt_default . +.Sh SEE ALSO +.Xr printf 3 diff --git a/lib/libc/gen/fmtcheck.c b/lib/libc/gen/fmtcheck.c new file mode 100644 index 0000000..7cc8cff --- /dev/null +++ b/lib/libc/gen/fmtcheck.c @@ -0,0 +1,253 @@ +/* $FreeBSD$ */ +/* $NetBSD: fmtcheck.c,v 1.2 2000/11/01 01:17:20 briggs Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Allen Briggs. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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/cdefs.h> +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include "namespace.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#ifdef __weak_alias +__weak_alias(fmtcheck,__fmtcheck) +#endif + +enum __e_fmtcheck_types { + FMTCHECK_START, + FMTCHECK_SHORT, + FMTCHECK_INT, + FMTCHECK_LONG, + FMTCHECK_QUAD, + FMTCHECK_SHORTPOINTER, + FMTCHECK_INTPOINTER, + FMTCHECK_LONGPOINTER, + FMTCHECK_QUADPOINTER, + FMTCHECK_DOUBLE, + FMTCHECK_LONGDOUBLE, + FMTCHECK_STRING, + FMTCHECK_WIDTH, + FMTCHECK_PRECISION, + FMTCHECK_DONE, + FMTCHECK_UNKNOWN +}; +typedef enum __e_fmtcheck_types EFT; + +#define RETURN(pf,f,r) do { \ + *(pf) = (f); \ + return r; \ + } /*NOTREACHED*/ /*CONSTCOND*/ while (0) + +static EFT +get_next_format_from_precision(const char **pf) +{ + int sh, lg, quad, longdouble; + const char *f; + + sh = lg = quad = longdouble = 0; + + f = *pf; + switch (*f) { + case 'h': + f++; + sh = 1; + break; + case 'l': + f++; + if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN); + if (*f == 'l') { + f++; + quad = 1; + } else { + lg = 1; + } + break; + case 'q': + f++; + quad = 1; + break; + case 'L': + f++; + longdouble = 1; + break; + default: + break; + } + if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN); + if (strchr("diouxX", *f)) { + if (longdouble) + RETURN(pf,f,FMTCHECK_UNKNOWN); + if (lg) + RETURN(pf,f,FMTCHECK_LONG); + if (quad) + RETURN(pf,f,FMTCHECK_QUAD); + RETURN(pf,f,FMTCHECK_INT); + } + if (*f == 'n') { + if (longdouble) + RETURN(pf,f,FMTCHECK_UNKNOWN); + if (sh) + RETURN(pf,f,FMTCHECK_SHORTPOINTER); + if (lg) + RETURN(pf,f,FMTCHECK_LONGPOINTER); + if (quad) + RETURN(pf,f,FMTCHECK_QUADPOINTER); + RETURN(pf,f,FMTCHECK_INTPOINTER); + } + if (strchr("DOU", *f)) { + if (sh + lg + quad + longdouble) + RETURN(pf,f,FMTCHECK_UNKNOWN); + RETURN(pf,f,FMTCHECK_LONG); + } + if (strchr("eEfg", *f)) { + if (longdouble) + RETURN(pf,f,FMTCHECK_LONGDOUBLE); + if (sh + lg + quad) + RETURN(pf,f,FMTCHECK_UNKNOWN); + RETURN(pf,f,FMTCHECK_DOUBLE); + } + if (*f == 'c') { + if (sh + lg + quad + longdouble) + RETURN(pf,f,FMTCHECK_UNKNOWN); + RETURN(pf,f,FMTCHECK_INT); + } + if (*f == 's') { + if (sh + lg + quad + longdouble) + RETURN(pf,f,FMTCHECK_UNKNOWN); + RETURN(pf,f,FMTCHECK_STRING); + } + if (*f == 'p') { + if (sh + lg + quad + longdouble) + RETURN(pf,f,FMTCHECK_UNKNOWN); + RETURN(pf,f,FMTCHECK_LONG); + } + RETURN(pf,f,FMTCHECK_UNKNOWN); + /*NOTREACHED*/ +} + +static EFT +get_next_format_from_width(const char **pf) +{ + const char *f; + + f = *pf; + if (*f == '.') { + f++; + if (*f == '*') { + RETURN(pf,f,FMTCHECK_PRECISION); + } + /* eat any precision (empty is allowed) */ + while (isdigit(*f)) f++; + if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN); + } + RETURN(pf,f,get_next_format_from_precision(pf)); + /*NOTREACHED*/ +} + +static EFT +get_next_format(const char **pf, EFT eft) +{ + int infmt; + const char *f; + + if (eft == FMTCHECK_WIDTH) { + (*pf)++; + return get_next_format_from_width(pf); + } else if (eft == FMTCHECK_PRECISION) { + (*pf)++; + return get_next_format_from_precision(pf); + } + + f = *pf; + infmt = 0; + while (!infmt) { + f = strchr(f, '%'); + if (f == NULL) + RETURN(pf,f,FMTCHECK_DONE); + f++; + if (!*f) + RETURN(pf,f,FMTCHECK_UNKNOWN); + if (*f != '%') + infmt = 1; + else + f++; + } + + /* Eat any of the flags */ + while (*f && (strchr("#0- +", *f))) + f++; + + if (*f == '*') { + RETURN(pf,f,FMTCHECK_WIDTH); + } + /* eat any width */ + while (isdigit(*f)) f++; + if (!*f) { + RETURN(pf,f,FMTCHECK_UNKNOWN); + } + + RETURN(pf,f,get_next_format_from_width(pf)); + /*NOTREACHED*/ +} + +__const char * +fmtcheck(const char *f1, const char *f2) +{ + const char *f1p, *f2p; + EFT f1t, f2t; + + if (!f1) return f2; + + f1p = f1; + f1t = FMTCHECK_START; + f2p = f2; + f2t = FMTCHECK_START; + while ((f1t = get_next_format(&f1p, f1t)) != FMTCHECK_DONE) { + if (f1t == FMTCHECK_UNKNOWN) + return f2; + f2t = get_next_format(&f2p, f2t); + if (f1t != f2t) + return f2; + } + return f1; +} |