From: Todd C. Miller Date: Tue, 10 Dec 2013 21:38:52 +0000 (-0700) Subject: Add strtonum.c to compat for simpler number parsing. X-Git-Tag: SUDO_1_8_9^2~45 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f83eac40eb3b36c35796686536df937b99c92766;p=sudo Add strtonum.c to compat for simpler number parsing. --- diff --git a/MANIFEST b/MANIFEST index 193a9868c..bc93d7c0e 100644 --- a/MANIFEST +++ b/MANIFEST @@ -94,6 +94,7 @@ compat/stdbool.h compat/strlcat.c compat/strlcpy.c compat/strsignal.c +compat/strtonum.c compat/timespec.h compat/utime.h compat/utimes.c diff --git a/compat/Makefile.in b/compat/Makefile.in index ef11f307e..874854b1e 100644 --- a/compat/Makefile.in +++ b/compat/Makefile.in @@ -222,6 +222,8 @@ strlcpy.lo: $(srcdir)/strlcpy.c $(incdir)/missing.h $(top_builddir)/config.h strsignal.lo: $(srcdir)/strsignal.c $(incdir)/gettext.h $(incdir)/missing.h \ $(top_builddir)/config.h $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/strsignal.c +strtonum.lo: $(srcdir)/strtonum.c $(incdir)/missing.h $(top_builddir)/config.h + $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/strtonum.c utimes.lo: $(srcdir)/utimes.c $(incdir)/missing.h $(top_builddir)/config.h \ $(top_srcdir)/compat/utime.h $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/utimes.c diff --git a/compat/strtonum.c b/compat/strtonum.c new file mode 100644 index 000000000..d1b5e9180 --- /dev/null +++ b/compat/strtonum.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2013 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include +#include +#include +#include +#ifdef HAVE_STDLIB_H +# include +#endif + +#include "missing.h" + +enum strtonum_err { + STN_VALID, + STN_INVALID, + STN_TOOSMALL, + STN_TOOBIG +}; + +/* + * Convert a string to a number in the range [minval, maxval] + */ +long long +strtonum(const char *str, long long minval, long long maxval, + const char **errstrp) +{ + const unsigned char *ustr = (const unsigned char *)str; + enum strtonum_err errval = STN_VALID; + long long lastval, result = 0; + unsigned char dig, sign; + int remainder; + + if (minval > maxval) { + errval = STN_INVALID; + goto done; + } + + /* Trim leading space and check sign, if any. */ + while (isspace(*ustr)) { + ustr++; + } + switch (*ustr) { + case '-': + sign = '-'; + ustr++; + break; + case '+': + ustr++; + /* FALLTHROUGH */ + default: + sign = '+'; + break; + } + + /* + * To prevent overflow we determine the highest (or lowest in + * the case of negative numbers) value result can have *before* + * if its multiplied (divided) by 10 as well as the remainder. + * If result matches this value and the next digit is larger than + * the remainder, we know the result is out of range. + * The remainder is always positive since it is compared against + * an unsigned digit. + */ + if (sign == '-') { + lastval = minval / 10; + remainder = -(minval % 10); + if (remainder < 0) { + lastval += 1; + remainder += 10; + } + while ((dig = *ustr++) != '\0') { + if (!isdigit(dig)) { + errval = STN_INVALID; + break; + } + dig -= '0'; + if (result < lastval || (result == lastval && dig > remainder)) { + errval = STN_TOOSMALL; + break; + } else { + result *= 10; + result -= dig; + } + } + if (result > maxval) + errval = STN_TOOBIG; + } else { + lastval = maxval / 10; + remainder = maxval % 10; + while ((dig = *ustr++) != '\0') { + if (!isdigit(dig)) { + errval = STN_INVALID; + break; + } + dig -= '0'; + if (result > lastval || (result == lastval && dig > remainder)) { + errval = STN_TOOBIG; + break; + } else { + result *= 10; + result += dig; + } + } + if (result < minval) + errval = STN_TOOSMALL; + } + +done: + switch (errval) { + case STN_VALID: + if (errstrp != NULL) + *errstrp = NULL; + break; + case STN_INVALID: + result = 0; + errno = EINVAL; + if (errstrp != NULL) + *errstrp = "invalid"; + break; + case STN_TOOSMALL: + result = 0; + errno = ERANGE; + if (errstrp != NULL) + *errstrp = "too small"; + break; + case STN_TOOBIG: + result = 0; + errno = ERANGE; + if (errstrp != NULL) + *errstrp = "too large"; + break; + } + return result; +} diff --git a/config.h.in b/config.h.in index 908852e2f..87b2c6122 100644 --- a/config.h.in +++ b/config.h.in @@ -581,6 +581,9 @@ /* Define to 1 if you have the `strtoll' function. */ #undef HAVE_STRTOLL +/* Define to 1 if you have the `strtonum' function. */ +#undef HAVE_STRTONUM + /* Define to 1 if `d_type' is a member of `struct dirent'. */ #undef HAVE_STRUCT_DIRENT_D_TYPE diff --git a/configure b/configure index 13c656a4a..f37737f86 100755 --- a/configure +++ b/configure @@ -17751,6 +17751,19 @@ esac fi +ac_fn_c_check_func "$LINENO" "strtonum" "ac_cv_func_strtonum" +if test "x$ac_cv_func_strtonum" = xyes; then : + $as_echo "#define HAVE_STRTONUM 1" >>confdefs.h + +else + case " $LIBOBJS " in + *" strtonum.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS strtonum.$ac_objext" + ;; +esac + +fi + for ac_func in getopt_long do : diff --git a/configure.ac b/configure.ac index f972209e1..c5bf464e8 100644 --- a/configure.ac +++ b/configure.ac @@ -2388,7 +2388,7 @@ SUDO_FUNC_FNMATCH([AC_DEFINE(HAVE_FNMATCH)], [AC_LIBOBJ(fnmatch) COMPAT_TEST_PROGS="${COMPAT_TEST_PROGS}${COMPAT_TEST_PROGS+ }fnm_test" ]) SUDO_FUNC_ISBLANK -AC_REPLACE_FUNCS(memrchr memset_s pw_dup strlcpy strlcat) +AC_REPLACE_FUNCS(memrchr memset_s pw_dup strlcpy strlcat strtonum) AC_CHECK_FUNCS(getopt_long, [], [AC_LIBOBJ(getopt_long) AC_MSG_CHECKING([for optreset]) AC_CACHE_VAL(sudo_cv_optreset, [ diff --git a/include/missing.h b/include/missing.h index 0c89f5e5a..a4051b3ad 100644 --- a/include/missing.h +++ b/include/missing.h @@ -446,6 +446,9 @@ char *strsignal(int); #ifndef HAVE_SIG2STR int sig2str(int, char *); #endif +#ifndef HAVE_STRTONUM +long long strtonum(const char *, long long, long long, const char **); +#endif void initprogname(const char *); #endif /* _SUDO_MISSING_H */ diff --git a/mkdep.pl b/mkdep.pl index 6b108f783..e6c1fa588 100755 --- a/mkdep.pl +++ b/mkdep.pl @@ -70,7 +70,7 @@ sub mkdep { $makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo sssd.lo:; # XXX - fill in AUTH_OBJS from contents of the auth dir instead $makefile =~ s:\@AUTH_OBJS\@:afs.lo aix_auth.lo bsdauth.lo dce.lo fwtk.lo getspwuid.lo kerb5.lo pam.lo passwd.lo rfc1938.lo secureware.lo securid5.lo sia.lo:; - $makefile =~ s:\@LTLIBOBJS\@:closefrom.lo fnmatch.lo getaddrinfo.lo getcwd.lo getgrouplist.lo getline.lo getopt_long.lo glob.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo pw_dup.lo sig2str.lo siglist.lo signame.lo snprintf.lo strlcat.lo strlcpy.lo strsignal.lo utimes.lo globtest.o fnm_test.o:; + $makefile =~ s:\@LTLIBOBJS\@:closefrom.lo fnmatch.lo getaddrinfo.lo getcwd.lo getgrouplist.lo getline.lo getopt_long.lo glob.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo pw_dup.lo sig2str.lo siglist.lo signame.lo snprintf.lo strlcat.lo strlcpy.lo strsignal.lo strtonum.lo utimes.lo globtest.o fnm_test.o:; # Parse OBJS lines my %objs;