diff --git a/configure.ac b/configure.ac index 5c331c370dc..09a026d888a 100644 --- a/configure.ac +++ b/configure.ac @@ -36,6 +36,10 @@ AC_PROG_VERSION_CHECK([flex], [2.5.33], [2.5.34]) # Enable Compiler/LibC GNU extensions AC_GNU_SOURCE +# Check for the strlcpy and strlcat functions +AX_CHECK_STRLCPY +AX_CHECK_STRLCAT + # Check for gettext AM_GNU_GETTEXT([external]) AC_PROG_VERSION_CHECK([msgfmt xgettext], [0.15]) diff --git a/lib/framework/strlfuncs.h b/lib/framework/strlfuncs.h index 6c780cdf980..257dbcfd76d 100644 --- a/lib/framework/strlfuncs.h +++ b/lib/framework/strlfuncs.h @@ -22,6 +22,14 @@ #include #include +#ifndef HAVE_VALID_STRLCPY +# ifdef HAVE_SYSTEM_STRLCPY + // If the system provides a non-conformant strlcpy we use our own +# ifdef strlcpy +# undef strlcpy +# endif +# define strlcpy wz_strlcpy +# endif // HAVE_SYSTEM_STRLCPY /** * A safer variant of \c strncpy and its completely unsafe variant \c strcpy. * Copy src to string dst of size siz. At most siz-1 characters @@ -61,7 +69,16 @@ static inline size_t strlcpy(char *WZ_DECL_RESTRICT dst, const char *WZ_DECL_RES return(s - src - 1); /* count does not include NUL */ } +#endif // HAVE_VALID_STRLCPY +#ifndef HAVE_VALID_STRLCAT +# ifdef HAVE_SYSTEM_STRLCAT + // If the system provides a non-conformant strlcat we use our own +# ifdef strlcat +# undef strlcat +# endif +# define strlcat wz_strlcat +# endif // HAVE_SYSTEM_STRLCAT /** * A safer variant of \c strncat and its completely unsafe variant \c strcat. * Appends src to string dst of size siz (unlike strncat, siz is the @@ -105,6 +122,7 @@ static inline size_t strlcat(char *WZ_DECL_RESTRICT dst, const char *WZ_DECL_RES return(dlen + (s - src)); /* count does not include NUL */ } +#endif // HAVE_VALID_STRLCAT /* Static array versions of common string functions. Safer because one less parameter to screw up. * Can only be used on strings longer than the length of a pointer, because we use this for debugging. */ diff --git a/m4/strlfuncs.m4 b/m4/strlfuncs.m4 new file mode 100644 index 00000000000..46e975df0fc --- /dev/null +++ b/m4/strlfuncs.m4 @@ -0,0 +1,129 @@ +# strlfuncs.m4 +dnl Copyright (C) 2009 Giel van Schijndel +dnl Copyright (C) 2009 Warzone Resurrection Project +dnl +dnl This file is free software; I, Giel van Schijndel give unlimited permission +dnl to copy and/or distribute it, with or without modifications, as long as +dnl this notice is preserved. +dnl +dnl This file can be used in projects which are not available under the GNU +dnl General Public License or the GNU Library General Public License. Please +dnl note that the the rest of the Warzone 2100 package is covered by the GNU +dnl General Public License. +dnl It is not *not* in the public domain. + +dnl Authors: +dnl Giel van Schijndel , 2009 + +AC_PREREQ(2.56) + +AC_DEFUN([AX_CHECK_STRLCPY],[ + AC_CHECK_FUNC([strlcpy],[ + AC_DEFINE([HAVE_SYSTEM_STRLCPY], [], [The system provides strlcpy]) + AC_RUN_IFELSE([AC_LANG_PROGRAM([[ +#include +#include + +static const char srcstr1[] = "source string 1"; +static char buf1[sizeof(srcstr1)]; +static char buf2[sizeof(srcstr1) * 2]; +static char buf3[sizeof(srcstr1) / 2]; +]],[[ +static const char initial_value = 0x55; +/* Make sure that all buffers are filled with an arbitrary non-NULL value, to + * check that overflow doesn't occur. + */ +memset(buf1, initial_value, sizeof(buf1)); +memset(buf2, initial_value, sizeof(buf2)); +memset(buf3, initial_value, sizeof(buf3)); + +/* Check whether behaviour is consistent with strcpy/strncpy for strings where + * the buffer size matches + */ +if (strlcpy(buf1, srcstr1, sizeof(buf1)) != strlen(srcstr1) + || memcmp(srcstr1, buf1, sizeof(buf1)) != 0) + exit(EXIT_FAILURE); +/* Check whether behaviour is consistent with strcpy/strncpy for strings where + * the buffer size is larger than required + */ +if (strlcpy(buf2, srcstr1, sizeof(buf2)) != strlen(srcstr1) + || memcmp(srcstr1, buf2, sizeof(srcstr1)) != 0) + exit(EXIT_FAILURE); +/* Check whether strlcpy *always* returns the amount of characters in the + * source string */ +if (strlcpy(buf3, srcstr1, 0) != strlen(srcstr1) + || buf3[0] != initial_value /* verify that no data has been written */ + || strlcpy(buf3, srcstr1, sizeof(buf3)) != strlen(srcstr1) +/* Check whether strlcpy fills the buffer properly with the first part of the + * source string if the buffer is too small + */ + || memcmp(srcstr1, buf3, sizeof(buf3) - 1) != 0 +/* Check whether strlcpy properly guarantees NUL-termination */ + || buf3[sizeof(buf3) - 1] != '\0') + exit(EXIT_FAILURE); +]]) + ],[AC_DEFINE([HAVE_VALID_STRLCPY], [], [The system provides a strlcpy we can use])]) + ]) +]) + +AC_DEFUN([AX_CHECK_STRLCAT],[ + AC_CHECK_FUNC([strlcat],[ + AC_DEFINE([HAVE_SYSTEM_STRLCAT], [], [The system provides strlcat]) + AC_RUN_IFELSE([AC_LANG_PROGRAM([[ +#include +#include + +static const char srcstr1[] = "source string 1"; +static const char srcstr2[] = "concatenation string"; +static char buf1[sizeof(srcstr1) + sizeof(srcstr2)]; +static char buf2[(sizeof(srcstr1) + sizeof(srcstr2)) * 2]; +static char buf3[sizeof(srcstr1) / 2]; +static char buf4[(sizeof(srcstr1) + sizeof(srcstr2) / 2)]; +]],[[ +static const char initial_value = 0x55; +/* Make sure that all buffers are filled with an arbitrary non-NULL value, to + * check that overflow doesn't occur. + */ +memset(buf1, initial_value, sizeof(buf1)); +memset(buf2, initial_value, sizeof(buf2)); +memset(buf3, initial_value, sizeof(buf3)); +memset(buf4, initial_value, sizeof(buf4)); + +/* Prefill the buffers so that we can check concatenation */ +strncpy(buf1, srcstr1, sizeof(buf1)); +strncpy(buf2, srcstr1, sizeof(buf2)); +strncpy(buf3, srcstr1, sizeof(buf3)); +strncpy(buf4, srcstr1, sizeof(buf4)); + +/* Check whether behaviour is consistent with strcat/strncat for strings where + * the buffer size matches + */ +if (strlcat(buf1, srcstr2, sizeof(buf1)) != (strlen(srcstr1) + strlen(srcstr2)) + || memcmp(srcstr1, buf1, strlen(srcstr1)) != 0 + || memcmp(srcstr2, buf1 + strlen(srcstr1), sizeof(srcstr2)) != 0) + exit(EXIT_FAILURE); +/* Check whether behaviour is consistent with strcat/strncat for strings where + * the buffer size is larger than required + */ +if (strlcat(buf2, srcstr2, sizeof(buf2)) != (strlen(srcstr1) + strlen(srcstr2)) + || memcmp(srcstr1, buf2, strlen(srcstr1)) != 0 + || memcmp(srcstr2, buf2 + strlen(srcstr1), sizeof(srcstr2)) != 0) + exit(EXIT_FAILURE); +/* Check whether strlcat *always* returns the amount of characters the + * resulting string would have if the destination buffer was large enough. + */ +if (strlcat(buf3, srcstr2, 0) != strlen(srcstr2) + || strlcat(buf3, srcstr2, sizeof(buf3)) != (sizeof(buf3) + strlen(srcstr2)) +/* Check whether strlcat fills the buffer properly with the first part of the + * source string if the buffer is too small + */ + || memcmp(srcstr1, buf3, sizeof(buf3) - 1) != 0) + exit(EXIT_FAILURE); +/* Check whether strlcat properly guarantees NUL-termination */ +if (strlcat(buf4, srcstr2, sizeof(buf4)) != (strlen(srcstr1) + strlen(srcstr2)) + || buf4[sizeof(buf4) - 1] != '\0') + exit(EXIT_FAILURE); +]]) + ],[AC_DEFINE([HAVE_VALID_STRLCAT], [], [The system provides a strlcat we can use])]) + ]) +])