summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkris <kris@FreeBSD.org>2001-04-17 07:59:52 +0000
committerkris <kris@FreeBSD.org>2001-04-17 07:59:52 +0000
commit0f958ee7467ce3cd3eaedbef81be48790d0b6b1a (patch)
tree3a26f765580b87bb8dbbe23a52082eaf344945c5
parentb6da0fca774d229c4776e41d019373d1ec0df7ab (diff)
downloadFreeBSD-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.h2
-rw-r--r--lib/libc/gen/Makefile.inc4
-rw-r--r--lib/libc/gen/fmtcheck.3100
-rw-r--r--lib/libc/gen/fmtcheck.c253
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;
+}
OpenPOWER on IntegriCloud