From c378da0b47eb8c26fc8da4d89e128ee3c73537de Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Wed, 4 May 2011 00:19:21 -0700 Subject: [PATCH] Use C99's va_copy to avoid undefined behavior on x86-64 GNU/Linux. --- ChangeLog | 4 +++ Makefile.in | 2 +- lib/gnulib.mk | 29 +++++++++++++++++- lib/stdarg.in.h | 36 +++++++++++++++++++++++ m4/gl-comp.m4 | 9 ++++++ m4/stdarg.m4 | 78 +++++++++++++++++++++++++++++++++++++++++++++++++ src/ChangeLog | 3 ++ src/eval.c | 5 +++- 8 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 lib/stdarg.in.h create mode 100644 m4/stdarg.m4 diff --git a/ChangeLog b/ChangeLog index a9446476bc6..c1e774c2924 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2011-05-04 Paul Eggert + Use C99's va_copy to avoid undefined behavior on x86-64 GNU/Linux. + * Makefile.in (GNULIB_MODULES): Add stdarg, for va_copy. + * lib/stdarg.in.h, m4/stdarg.m4: New files, from gnulib. + * Makefile.in (GNULIB_TOOL_FLAG): Add --conditional-dependencies. This new gnulib-tool option saves 'configure' the trouble of checking for strtoull when strtoumax exists. diff --git a/Makefile.in b/Makefile.in index 180f7e5be16..ba2926d2853 100644 --- a/Makefile.in +++ b/Makefile.in @@ -333,7 +333,7 @@ DOS_gnulib_comp.m4 = gl-comp.m4 GNULIB_MODULES = \ careadlinkat crypto/md5 dtoastr filemode getloadavg getopt-gnu \ ignore-value intprops lstat mktime readlink \ - socklen stdio strftime strtoumax symlink sys_stat + socklen stdarg stdio strftime strtoumax symlink sys_stat GNULIB_TOOL_FLAGS = \ --conditional-dependencies --import --no-changelog --no-vc-files \ --makefile-name=gnulib.mk diff --git a/lib/gnulib.mk b/lib/gnulib.mk index faf89aaa0e6..1466e430a4c 100644 --- a/lib/gnulib.mk +++ b/lib/gnulib.mk @@ -9,7 +9,7 @@ # the same distribution terms as the rest of that program. # # Generated by gnulib-tool. -# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --makefile-name=gnulib.mk --no-libtool --macro-prefix=gl --no-vc-files careadlinkat crypto/md5 dtoastr filemode getloadavg getopt-gnu ignore-value intprops lstat mktime readlink socklen stdio strftime strtoumax symlink sys_stat +# Reproduce by: gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --makefile-name=gnulib.mk --no-libtool --macro-prefix=gl --no-vc-files careadlinkat crypto/md5 dtoastr filemode getloadavg getopt-gnu ignore-value intprops lstat mktime readlink socklen stdarg stdio strftime strtoumax symlink sys_stat MOSTLYCLEANFILES += core *.stackdump @@ -258,6 +258,33 @@ EXTRA_libgnu_a_SOURCES += stat.c ## end gnulib module stat +## begin gnulib module stdarg + +BUILT_SOURCES += $(STDARG_H) + +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +if GL_GENERATE_STDARG_H +stdarg.h: stdarg.in.h $(top_builddir)/config.status + $(AM_V_GEN)rm -f $@-t $@ && \ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \ + sed -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ + -e 's|@''NEXT_STDARG_H''@|$(NEXT_STDARG_H)|g' \ + < $(srcdir)/stdarg.in.h; \ + } > $@-t && \ + mv $@-t $@ +else +stdarg.h: $(top_builddir)/config.status + rm -f $@ +endif +MOSTLYCLEANFILES += stdarg.h stdarg.h-t + +EXTRA_DIST += stdarg.in.h + +## end gnulib module stdarg + ## begin gnulib module stdbool BUILT_SOURCES += $(STDBOOL_H) diff --git a/lib/stdarg.in.h b/lib/stdarg.in.h new file mode 100644 index 00000000000..4469d54e4f4 --- /dev/null +++ b/lib/stdarg.in.h @@ -0,0 +1,36 @@ +/* Substitute for and wrapper around . + Copyright (C) 2008-2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GL_STDARG_H + +#if __GNUC__ >= 3 +@PRAGMA_SYSTEM_HEADER@ +#endif +@PRAGMA_COLUMNS@ + +/* The include_next requires a split double-inclusion guard. */ +#@INCLUDE_NEXT@ @NEXT_STDARG_H@ + +#ifndef _GL_STDARG_H +#define _GL_STDARG_H + +#ifndef va_copy +# define va_copy(a,b) ((a) = (b)) +#endif + +#endif /* _GL_STDARG_H */ +#endif /* _GL_STDARG_H */ diff --git a/m4/gl-comp.m4 b/m4/gl-comp.m4 index 4338f2036b1..87d7616f8bb 100644 --- a/m4/gl-comp.m4 +++ b/m4/gl-comp.m4 @@ -51,6 +51,12 @@ AC_DEFUN([gl_EARLY], # Code from module socklen: # Code from module ssize_t: # Code from module stat: + # Code from module stdarg: + dnl Some compilers (e.g., AIX 5.3 cc) need to be in c99 mode + dnl for the builtin va_copy to work. With Autoconf 2.60 or later, + dnl AC_PROG_CC_STDC arranges for this. With older Autoconf AC_PROG_CC_STDC + dnl shouldn't hurt, though installers are on their own to set c99 mode. + AC_REQUIRE([AC_PROG_CC_STDC]) # Code from module stdbool: # Code from module stddef: # Code from module stdint: @@ -104,6 +110,7 @@ gl_FUNC_READLINK gl_UNISTD_MODULE_INDICATOR([readlink]) gl_TYPE_SOCKLEN_T gt_TYPE_SSIZE_T +gl_STDARG_H AM_STDBOOL_H gl_STDDEF_H gl_STDINT_H @@ -358,6 +365,7 @@ AC_DEFUN([gl_FILE_LIST], [ lib/mktime.c lib/readlink.c lib/stat.c + lib/stdarg.in.h lib/stdbool.in.h lib/stddef.in.h lib/stdint.in.h @@ -395,6 +403,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/ssize_t.m4 m4/st_dm_mode.m4 m4/stat.m4 + m4/stdarg.m4 m4/stdbool.m4 m4/stddef_h.m4 m4/stdint.m4 diff --git a/m4/stdarg.m4 b/m4/stdarg.m4 new file mode 100644 index 00000000000..5705de9ecaa --- /dev/null +++ b/m4/stdarg.m4 @@ -0,0 +1,78 @@ +# stdarg.m4 serial 6 +dnl Copyright (C) 2006, 2008-2011 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. +dnl Provide a working va_copy in combination with . + +AC_DEFUN([gl_STDARG_H], +[ + STDARG_H='' + NEXT_STDARG_H='' + AC_MSG_CHECKING([for va_copy]) + AC_CACHE_VAL([gl_cv_func_va_copy], [ + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[ +#ifndef va_copy +void (*func) (va_list, va_list) = va_copy; +#endif + ]])], + [gl_cv_func_va_copy=yes], + [gl_cv_func_va_copy=no])]) + AC_MSG_RESULT([$gl_cv_func_va_copy]) + if test $gl_cv_func_va_copy = no; then + dnl Provide a substitute. + dnl Usually a simple definition in is enough. Not so on AIX 5 + dnl with some versions of the /usr/vac/bin/cc compiler. It has an + dnl which does '#undef va_copy', leading to a missing va_copy symbol. For + dnl this platform, we use an substitute. But we cannot use this + dnl approach on other platforms, because often defines only + dnl preprocessor macros and gl_ABSOLUTE_HEADER, gl_CHECK_NEXT_HEADERS do + dnl not work in this situation. + AC_EGREP_CPP([vaccine], + [#if defined _AIX && !defined __GNUC__ + AIX vaccine + #endif + ], [gl_aixcc=yes], [gl_aixcc=no]) + if test $gl_aixcc = yes; then + dnl Provide a substitute file. + STDARG_H=stdarg.h + gl_NEXT_HEADERS([stdarg.h]) + dnl Fallback for the case when contains only macro definitions. + if test "$gl_cv_next_stdarg_h" = '""'; then + gl_cv_next_stdarg_h='"///usr/include/stdarg.h"' + NEXT_STDARG_H="$gl_cv_next_stdarg_h" + fi + else + dnl Provide a substitute in , either __va_copy or as a simple + dnl assignment. + gl_CACHE_VAL_SILENT([gl_cv_func___va_copy], [ + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[ +#ifndef __va_copy +error, bail out +#endif + ]])], + [gl_cv_func___va_copy=yes], + [gl_cv_func___va_copy=no])]) + if test $gl_cv_func___va_copy = yes; then + AC_DEFINE([va_copy], [__va_copy], + [Define as a macro for copying va_list variables.]) + else + AH_VERBATIM([gl_VA_COPY], [/* A replacement for va_copy, if needed. */ +#define gl_va_copy(a,b) ((a) = (b))]) + AC_DEFINE([va_copy], [gl_va_copy], + [Define as a macro for copying va_list variables.]) + fi + fi + fi + AC_SUBST([STDARG_H]) + AM_CONDITIONAL([GL_GENERATE_STDARG_H], [test -n "$STDARG_H"]) + AC_SUBST([NEXT_STDARG_H]) +]) diff --git a/src/ChangeLog b/src/ChangeLog index 9fac265ae48..a1aa19e6f2e 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,8 @@ 2011-05-04 Paul Eggert + Use C99's va_copy to avoid undefined behavior on x86-64 GNU/Linux. + * eval.c (verror): doprnt a copy of ap, not the original. (Bug#8545) + * eval.c (verror): OK to create a string of up to MOST_POSITIVE_FIXNUM bytes. diff --git a/src/eval.c b/src/eval.c index 90ef02ef37b..6b4182cb319 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2002,7 +2002,10 @@ verror (const char *m, va_list ap) while (1) { - used = doprnt (buffer, size, m, m + mlen, ap); + va_list ap_copy; + va_copy (ap_copy, ap); + used = doprnt (buffer, size, m, m + mlen, ap_copy); + va_end (ap_copy); /* Note: the -1 below is because `doprnt' returns the number of bytes excluding the terminating null byte, and it always terminates with a -- 2.39.2