From a9fca58305afcb16a7ae539d87d672c702aba3f1 Mon Sep 17 00:00:00 2001 From: Gabriele Svelto Date: Tue, 11 Apr 2017 11:51:28 +0200 Subject: [PATCH] Make minidump name generation on Linux as random as possible This patch ensures that two crashes taken within the same second have different minidump names. The random characters used in the minidump filename are now read from /dev/urandom where possible or generated via arc4random(). If neither is available we fall back to regular rand() but mixing the address of an object to the current time when generating the random seed to make it slightly less predictable. BUG=681 Change-Id: I2e97454859ed386e199b2628d6b7e87e16481b75 Reviewed-on: https://chromium-review.googlesource.com/445784 Reviewed-by: Mike Frysinger --- configure | 88 ++++++++++++++++++++++++++++-- configure.ac | 3 +- src/common/linux/guid_creator.cc | 94 +++++++++++++++++++++++++++++--- src/config.h.in | 9 +++ 4 files changed, 182 insertions(+), 12 deletions(-) diff --git a/configure b/configure index 200d1dc9..dcfd3c76 100755 --- a/configure +++ b/configure @@ -1875,6 +1875,73 @@ $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. @@ -6572,18 +6639,31 @@ fi done -for ac_header in a.out.h +for ac_header in a.out.h sys/random.h do : - ac_fn_c_check_header_mongrel "$LINENO" "a.out.h" "ac_cv_header_a_out_h" "$ac_includes_default" -if test "x$ac_cv_header_a_out_h" = xyes; then : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF -#define HAVE_A_OUT_H 1 +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done +for ac_func in arc4random getrandom +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + ax_cxx_compile_cxx11_required=true diff --git a/configure.ac b/configure.ac index bc72017c..fe4e8a5a 100644 --- a/configure.ac +++ b/configure.ac @@ -72,7 +72,8 @@ AC_ARG_ENABLE(m32, AC_HEADER_STDC AC_SYS_LARGEFILE AX_PTHREAD -AC_CHECK_HEADERS([a.out.h]) +AC_CHECK_HEADERS([a.out.h sys/random.h]) +AC_CHECK_FUNCS([arc4random getrandom]) AX_CXX_COMPILE_STDCXX(11, noext, mandatory) diff --git a/src/common/linux/guid_creator.cc b/src/common/linux/guid_creator.cc index bfb308ee..c92e69f4 100644 --- a/src/common/linux/guid_creator.cc +++ b/src/common/linux/guid_creator.cc @@ -27,15 +27,22 @@ // (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 "common/linux/eintr_wrapper.h" #include "common/linux/guid_creator.h" #include +#include #include #include #include +#include #include #include +#if defined(HAVE_SYS_RANDOM_H) +#include +#endif + // // GUIDGenerator // @@ -61,28 +68,101 @@ class GUIDGenerator { } static bool CreateGUID(GUID *guid) { - InitOnce(); - guid->data1 = random(); - guid->data2 = (uint16_t)(random()); - guid->data3 = (uint16_t)(random()); - UInt32ToBytes(&guid->data4[0], random()); - UInt32ToBytes(&guid->data4[4], random()); +#if defined(HAVE_ARC4RANDOM) // Android, BSD, ... + CreateGuidFromArc4Random(guid); +#else // Linux + bool success = false; + +#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM) + success = CreateGUIDFromGetrandom(guid); +#endif // HAVE_SYS_RANDOM_H && HAVE_GETRANDOM + if (!success) { + success = CreateGUIDFromDevUrandom(guid); + } + + if (!success) { + CreateGUIDFromRand(guid); + success = true; + } +#endif + + // Put in the version according to RFC 4122. + guid->data3 &= 0x0fff; + guid->data3 |= 0x4000; + + // Put in the variant according to RFC 4122. + guid->data4[0] &= 0x3f; + guid->data4[0] |= 0x80; + return true; } private: +#ifdef HAVE_ARC4RANDOM + static void CreateGuidFromArc4Random(GUID *guid) { + char *buf = reinterpret_cast(guid); + + for (size_t i = 0; i < sizeof(GUID); i += sizeof(uint32_t)) { + uint32_t random_data = arc4random(); + + memcpy(buf + i, &random_data, sizeof(uint32_t)); + } + } +#else static void InitOnce() { pthread_once(&once_control, &InitOnceImpl); } static void InitOnceImpl() { - srandom(time(NULL)); + // time(NULL) is a very poor seed, so lacking anything better mix an + // address into it. We drop the four rightmost bits as they're likely to + // be 0 on almost all architectures. + srand(time(NULL) | ((uintptr_t)&once_control >> 4)); } static pthread_once_t once_control; + +#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM) + static bool CreateGUIDFromGetrandom(GUID *guid) { + char *buf = reinterpret_cast(guid); + int read_bytes = getrandom(buf, sizeof(GUID), GRND_NONBLOCK); + + return (read_bytes == static_cast(sizeof(GUID))); + } +#endif // HAVE_SYS_RANDOM_H && HAVE_GETRANDOM + + // Populate the GUID using random bytes read from /dev/urandom, returns false + // if the GUID wasn't fully populated with random data. + static bool CreateGUIDFromDevUrandom(GUID *guid) { + char *buf = reinterpret_cast(guid); + int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + + if (fd == -1) { + return false; + } + + ssize_t read_bytes = HANDLE_EINTR(read(fd, buf, sizeof(GUID))); + close(fd); + + return (read_bytes == static_cast(sizeof(GUID))); + } + + // Populate the GUID using a stream of random bytes obtained from rand(). + static void CreateGUIDFromRand(GUID *guid) { + char *buf = reinterpret_cast(guid); + + InitOnce(); + + for (size_t i = 0; i < sizeof(GUID); i++) { + buf[i] = rand(); + } + } +#endif }; +#ifndef HAVE_ARC4RANDOM pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT; +#endif bool CreateGUID(GUID *guid) { return GUIDGenerator::CreateGUID(guid); diff --git a/src/config.h.in b/src/config.h.in index ee6a67ad..a436d6ba 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -1,11 +1,17 @@ /* src/config.h.in. Generated from configure.ac by autoheader. */ +/* Define to 1 if you have the `arc4random' function. */ +#undef HAVE_ARC4RANDOM + /* Define to 1 if you have the header file. */ #undef HAVE_A_OUT_H /* define if the compiler supports basic C++11 syntax */ #undef HAVE_CXX11 +/* Define to 1 if you have the `getrandom' function. */ +#undef HAVE_GETRANDOM + /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H @@ -27,6 +33,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RANDOM_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H