From cb9434bf5db409773dd66372c52f344b8dc21d76 Mon Sep 17 00:00:00 2001 From: Peter Breitenlohner Date: Wed, 1 Aug 2012 14:34:03 +0000 Subject: kpathsea: Make sure snprintf() is not badly broken if necessary, declare (v)snprintf wrapper functions git-svn-id: svn://tug.org/texlive/trunk@27271 c570f23f-e606-0410-a88d-b1316a301751 --- Build/source/texk/kpathsea/ChangeLog | 8 +++ Build/source/texk/kpathsea/c-auto.in | 3 + Build/source/texk/kpathsea/config.h | 69 +++++++++++++++++++ Build/source/texk/kpathsea/configure | 116 +++++++++++++++++++++++++++++++- Build/source/texk/kpathsea/configure.ac | 49 +++++++++++++- Build/source/texk/kpathsea/progname.c | 7 ++ 6 files changed, 248 insertions(+), 4 deletions(-) (limited to 'Build') diff --git a/Build/source/texk/kpathsea/ChangeLog b/Build/source/texk/kpathsea/ChangeLog index ab35720f3ec..19cc2cb9631 100644 --- a/Build/source/texk/kpathsea/ChangeLog +++ b/Build/source/texk/kpathsea/ChangeLog @@ -1,3 +1,11 @@ +2012-08-01 Peter Breitenlohner + + * configure.ac: Bail out for a badly broken snprintf() + (hopefully this will not happen). + Check if we need (v)snprintf wrapper functions. + * config.h: If needed, declare (v)snprintf wrapper functions. + * progname.c (kpathsea_set_program_name): Check snprintf sanity. + 2012-07-25 Peter Breitenlohner * configure.ac: Add AC_CHECK_SIZEOF([long]). diff --git a/Build/source/texk/kpathsea/c-auto.in b/Build/source/texk/kpathsea/c-auto.in index d4fcb84ea3c..4c585126be0 100644 --- a/Build/source/texk/kpathsea/c-auto.in +++ b/Build/source/texk/kpathsea/c-auto.in @@ -219,6 +219,9 @@ /* Version number of package */ #undef VERSION +/* Define to 1 if we need (v)snprintf wrapper functions. */ +#undef WRAP_SNPRINTF + /* Enable large inode numbers on Mac OS X 10.5. */ #ifndef _DARWIN_USE_64_BIT_INODE # define _DARWIN_USE_64_BIT_INODE 1 diff --git a/Build/source/texk/kpathsea/config.h b/Build/source/texk/kpathsea/config.h index eef952d7113..49f1b41b443 100644 --- a/Build/source/texk/kpathsea/config.h +++ b/Build/source/texk/kpathsea/config.h @@ -103,6 +103,75 @@ #endif #endif +#if defined(WIN32) || defined(WRAP_SNPRINTF) +/* All Unix systems known to us have snprintf() and vsnprintf(), + while all known Windows systems have _snprintf() and _vsnprintf(). + + Consider a call + RET = snprintf(BUF, SIZE, FMT, ...) + and let LEN be the number that would be written to BUF if SIZE were + sufficiently large (not counting the trailing null byte). + + C99 requires that snprintf + (A) modifies at most the first SIZE bytes of BUF, + (B) writes a trailing null byte except when SIZE=0, and + (C) always returns LEN. + + All known implementations (except some ancient buggy ones, e.g., for + 64-bit Solaris 7 from Oct. 1998) satisfy (A). As long as LEN=SIZE by some older + implementations (SUSv2, glibc <= 2.0.6, etc.) and for Windows even + (B) is violated.. + + TeX Live does not require the full C99 semantics, but will need that + (1) there is always a trailing null byte, and + (2) for LEN>=SIZE the return value is either >=SIZE or <0, i.e., + (unsigned)RET >= (unsigned)SIZE. + + A violation of (2) is detected by configure (except when cross + compiling) and by a runtime check in the initialization routine + kpathsea_set_program_name. + + A violation of (1) is handled here through static inline wrapper + functions. */ + +#include + +#undef snprintf +#undef vsnprintf + +static inline int +kpse_vsnprintf (char *str, size_t size, const char *format, va_list ap) +{ +#ifdef WIN32 + int ret = _vsnprintf (str, size, format, ap); +#else + int ret = vsnprintf (str, size, format, ap); +#endif + if (size > 0 && (unsigned)ret >= (unsigned)size) + str [size - 1] = '\0'; + return ret; +} + +static inline int +kpse_snprintf (char *str, size_t size, const char *format, ...) +{ + int ret; + va_list ap; + + va_start (ap, format); + ret = kpse_vsnprintf (str, size, format, ap); + va_end (ap); + return ret; +} + +#define snprintf kpse_snprintf +#define vsnprintf kpse_vsnprintf + +#endif /* WIN32 || WRAP_SNPRINTF */ + /* Transform filename characters for use in hash tables. */ #if defined(MONOCASE_FILENAMES) #if defined(WIN32) && !defined(__i386_pc_gnu__) diff --git a/Build/source/texk/kpathsea/configure b/Build/source/texk/kpathsea/configure index 8641402a3ba..0e2137d3993 100755 --- a/Build/source/texk/kpathsea/configure +++ b/Build/source/texk/kpathsea/configure @@ -778,6 +778,7 @@ enable_mkocp_default enable_mkofm_default enable_mktexfmt_default enable_mktextex_default +with_snprintf_wrapper ' ac_precious_vars='build_alias host_alias @@ -1440,6 +1441,8 @@ Optional Packages: --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot=DIR Search for dependent libraries within DIR (or the compiler's sysroot if not specified). + --with-snprintf-wrapper use (v)snprintf wrapper functions [automatic for + native compilation or Windows] Some influential environment variables: CC C compiler command @@ -13298,7 +13301,7 @@ fi -if test $ac_cv_func_getcwd = yes; then +if test "x$ac_cv_func_getcwd" = xyes; then # We only need to run this if we have getcwd. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether getcwd uses fork or vfork" >&5 $as_echo_n "checking whether getcwd uses fork or vfork... " >&6; } @@ -13334,7 +13337,7 @@ fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $kb_cv_func_getcwd_forks" >&5 $as_echo "$kb_cv_func_getcwd_forks" >&6; } -if test $kb_cv_func_getcwd_forks = yes; then +if test "x$kb_cv_func_getcwd_forks" = xyes; then $as_echo "#define GETCWD_FORKS 1" >>confdefs.h @@ -13407,6 +13410,115 @@ fi LT_OBJDIR="$lt_cv_objdir" +# Checking snprintf and vsnprintf + +# Check whether --with-snprintf-wrapper was given. +if test "${with_snprintf_wrapper+set}" = set; then : + withval=$with_snprintf_wrapper; kpse_cv_wrap_snprintf=$withval +else + test "x$kpse_cv_have_win32" != xno && kpse_cv_wrap_snprintf=yes +fi +if test "x$kpse_cv_have_win32" = xno; then + snfunc=snprintf vsnfunc=vsnprintf +else + snfunc=_snprintf vsnfunc=_vsnprintf +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $snfunc and $vsnfunc" >&5 +$as_echo_n "checking for $snfunc and $vsnfunc... " >&6; } +if ${kpse_cv_have_snfuncs+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + char buf[16]; + va_list ap; +int +main () +{ +$snfunc (buf, 16, "%s", "abc"); + $vsnfunc (buf, 16, "%s", ap); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + kpse_cv_have_snfuncs=yes +else + kpse_cv_have_snfuncs=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $kpse_cv_have_snfuncs" >&5 +$as_echo "$kpse_cv_have_snfuncs" >&6; } +if test "x$kpse_cv_have_snfuncs" != xyes; then + as_fn_error $? "Sorry, you need $snfunc and $vsnfunc." "$LINENO" 5 +fi +if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +char buf[4] = "abc"; +int +main () +{ +if ($snfunc (buf, 1, "%s", "x") == 0 || buf[1] != 'b') + return 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + as_fn_error $? "Sorry, your $snfunc is badly broken." "$LINENO" 5 +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if we need (v)snprintf wrapper functions" >&5 +$as_echo_n "checking if we need (v)snprintf wrapper functions... " >&6; } +if ${kpse_cv_wrap_snprintf+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + kpse_cv_wrap_snprintf=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +char buf[4] = "abc"; +int +main () +{ +$snfunc (buf, 3, "xyz"); + if (buf[2] != 0) return 1; + $snfunc (buf, 2, "xyz"); + if (buf[1] != 0) return 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + kpse_cv_wrap_snprintf=no +else + kpse_cv_wrap_snprintf=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $kpse_cv_wrap_snprintf" >&5 +$as_echo "$kpse_cv_wrap_snprintf" >&6; } +if test "x$kpse_cv_wrap_snprintf" = xyes; then + +$as_echo "#define WRAP_SNPRINTF 1" >>confdefs.h + +fi + ac_config_headers="$ac_config_headers c-auto.h:c-auto.in" diff --git a/Build/source/texk/kpathsea/configure.ac b/Build/source/texk/kpathsea/configure.ac index 3f305c6f356..aa7f373253b 100644 --- a/Build/source/texk/kpathsea/configure.ac +++ b/Build/source/texk/kpathsea/configure.ac @@ -40,7 +40,7 @@ dnl Replacement functions that may be required on ancient broken system. AC_CHECK_DECLS([putenv, strstr]) AC_REPLACE_FUNCS([putenv strcasecmp strtol strstr]) -if test $ac_cv_func_getcwd = yes; then +if test "x$ac_cv_func_getcwd" = xyes; then # We only need to run this if we have getcwd. AC_CACHE_CHECK([whether getcwd uses fork or vfork], [kb_cv_func_getcwd_forks], @@ -52,7 +52,7 @@ AC_CACHE_CHECK([whether getcwd uses fork or vfork], [kb_cv_func_getcwd_forks=no], [kb_cv_func_getcwd_forks=yes], [kb_cv_func_getcwd_forks=no])]) -if test $kb_cv_func_getcwd_forks = yes; then +if test "x$kb_cv_func_getcwd_forks" = xyes; then AC_DEFINE([GETCWD_FORKS], 1, [Define to 1 if getcwd is implemented using fork or vfork. Let me know if you have to add this by hand because configure @@ -66,6 +66,51 @@ AM_CONDITIONAL([MINGW32], [test "x$kpse_cv_have_win32" = xmingw32]) AC_SUBST([LT_OBJDIR], ["$lt_cv_objdir"]) +# Checking snprintf and vsnprintf +AC_ARG_WITH([snprintf-wrapper], + AS_HELP_STRING([--with-snprintf-wrapper], + [use (v)snprintf wrapper functions @<:@automatic + for native compilation or Windows@:>@]), + [kpse_cv_wrap_snprintf=$withval], + [test "x$kpse_cv_have_win32" != xno && kpse_cv_wrap_snprintf=yes])[]dnl +if test "x$kpse_cv_have_win32" = xno; then + snfunc=snprintf vsnfunc=vsnprintf +else + snfunc=_snprintf vsnfunc=_vsnprintf +fi +AC_CACHE_CHECK([for $snfunc and $vsnfunc], + [kpse_cv_have_snfuncs], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include + char buf[16]; + va_list ap;]], + [[$snfunc (buf, 16, "%s", "abc"); + $vsnfunc (buf, 16, "%s", ap);]])], + [kpse_cv_have_snfuncs=yes], + [kpse_cv_have_snfuncs=no])]) +if test "x$kpse_cv_have_snfuncs" != xyes; then + AC_MSG_ERROR([Sorry, you need $snfunc and $vsnfunc.]) +fi +AC_RUN_IFELSE([AC_LANG_PROGRAM([[char buf[4] = "abc";]], + [[if ($snfunc (buf, 1, "%s", "x") == 0 || buf[1] != 'b') + return 1;]])], + [], + [AC_MSG_ERROR([Sorry, your $snfunc is badly broken.])], + [:]) +AC_CACHE_CHECK([if we need (v)snprintf wrapper functions], + [kpse_cv_wrap_snprintf], + [AC_RUN_IFELSE([AC_LANG_PROGRAM([[char buf[4] = "abc";]], + [[$snfunc (buf, 3, "xyz"); + if (buf[2] != 0) return 1; + $snfunc (buf, 2, "xyz"); + if (buf[1] != 0) return 1;]])], + [kpse_cv_wrap_snprintf=no], + [kpse_cv_wrap_snprintf=yes], + [kpse_cv_wrap_snprintf=no])]) +if test "x$kpse_cv_wrap_snprintf" = xyes; then + AC_DEFINE([WRAP_SNPRINTF], 1, + [Define to 1 if we need (v)snprintf wrapper functions.]) +fi + dnl Write output here, instead of putting a zillion -D's on the command line. AC_CONFIG_HEADERS([c-auto.h:c-auto.in], [sed -e 's/^#define PACKAGE/#define KPSE_PACKAGE/' \ diff --git a/Build/source/texk/kpathsea/progname.c b/Build/source/texk/kpathsea/progname.c index ee5cfaff659..0bb403b0ae5 100644 --- a/Build/source/texk/kpathsea/progname.c +++ b/Build/source/texk/kpathsea/progname.c @@ -696,6 +696,13 @@ kpathsea_set_program_name (kpathsea kpse, const_string argv0, } } + /* Runtime check that snprintf always writes a trailing NUL byte. */ + { + char buf[4] = "old"; + assert (snprintf (buf, 2, "a") == 1 && buf[1] == '\0'); + assert ((unsigned)snprintf (buf, 2, "ab") >= 2 && buf[1] == '\0'); + assert ((unsigned)snprintf (buf, 2, "abc") >= 2 && buf[1] == '\0'); + } /* Some of the utility routines (like atou() and xfopen()) will use FATAL and variations thereof (see lib.h) if there is a problem. -- cgit v1.2.3