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 <vapier@chromium.org>
This commit is contained in:
Gabriele Svelto 2017-04-11 11:51:28 +02:00 committed by Mike Frysinger
parent aa7115cfde
commit a9fca58305
4 changed files with 182 additions and 12 deletions

88
configure vendored
View file

@ -1875,6 +1875,73 @@ $as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile } # 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 <limits.h> declares $2.
For example, HP-UX 11i <limits.h> declares gettimeofday. */
#define $2 innocuous_$2
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char $2 (); below.
Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
<limits.h> exists even on freestanding compilers. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#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 cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
@ -6572,18 +6639,31 @@ fi
done done
for ac_header in a.out.h for ac_header in a.out.h sys/random.h
do : do :
ac_fn_c_check_header_mongrel "$LINENO" "a.out.h" "ac_cv_header_a_out_h" "$ac_includes_default" as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
if test "x$ac_cv_header_a_out_h" = xyes; then : 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 cat >>confdefs.h <<_ACEOF
#define HAVE_A_OUT_H 1 #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF _ACEOF
fi fi
done 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 ax_cxx_compile_cxx11_required=true

View file

@ -72,7 +72,8 @@ AC_ARG_ENABLE(m32,
AC_HEADER_STDC AC_HEADER_STDC
AC_SYS_LARGEFILE AC_SYS_LARGEFILE
AX_PTHREAD 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) AX_CXX_COMPILE_STDCXX(11, noext, mandatory)

View file

@ -27,15 +27,22 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // 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 "common/linux/guid_creator.h"
#include <assert.h> #include <assert.h>
#include <fcntl.h>
#include <pthread.h> #include <pthread.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/stat.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#if defined(HAVE_SYS_RANDOM_H)
#include <sys/random.h>
#endif
// //
// GUIDGenerator // GUIDGenerator
// //
@ -61,28 +68,101 @@ class GUIDGenerator {
} }
static bool CreateGUID(GUID *guid) { static bool CreateGUID(GUID *guid) {
InitOnce(); #if defined(HAVE_ARC4RANDOM) // Android, BSD, ...
guid->data1 = random(); CreateGuidFromArc4Random(guid);
guid->data2 = (uint16_t)(random()); #else // Linux
guid->data3 = (uint16_t)(random()); bool success = false;
UInt32ToBytes(&guid->data4[0], random());
UInt32ToBytes(&guid->data4[4], random()); #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; return true;
} }
private: private:
#ifdef HAVE_ARC4RANDOM
static void CreateGuidFromArc4Random(GUID *guid) {
char *buf = reinterpret_cast<char *>(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() { static void InitOnce() {
pthread_once(&once_control, &InitOnceImpl); pthread_once(&once_control, &InitOnceImpl);
} }
static void 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; static pthread_once_t once_control;
#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM)
static bool CreateGUIDFromGetrandom(GUID *guid) {
char *buf = reinterpret_cast<char *>(guid);
int read_bytes = getrandom(buf, sizeof(GUID), GRND_NONBLOCK);
return (read_bytes == static_cast<int>(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<char *>(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<ssize_t>(sizeof(GUID)));
}
// Populate the GUID using a stream of random bytes obtained from rand().
static void CreateGUIDFromRand(GUID *guid) {
char *buf = reinterpret_cast<char *>(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; pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT;
#endif
bool CreateGUID(GUID *guid) { bool CreateGUID(GUID *guid) {
return GUIDGenerator::CreateGUID(guid); return GUIDGenerator::CreateGUID(guid);

View file

@ -1,11 +1,17 @@
/* src/config.h.in. Generated from configure.ac by autoheader. */ /* 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 <a.out.h> header file. */ /* Define to 1 if you have the <a.out.h> header file. */
#undef HAVE_A_OUT_H #undef HAVE_A_OUT_H
/* define if the compiler supports basic C++11 syntax */ /* define if the compiler supports basic C++11 syntax */
#undef HAVE_CXX11 #undef HAVE_CXX11
/* Define to 1 if you have the `getrandom' function. */
#undef HAVE_GETRANDOM
/* Define to 1 if you have the <inttypes.h> header file. */ /* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H #undef HAVE_INTTYPES_H
@ -27,6 +33,9 @@
/* Define to 1 if you have the <string.h> header file. */ /* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H #undef HAVE_STRING_H
/* Define to 1 if you have the <sys/random.h> header file. */
#undef HAVE_SYS_RANDOM_H
/* Define to 1 if you have the <sys/stat.h> header file. */ /* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H #undef HAVE_SYS_STAT_H