Skip to content

Commit f02794a

Browse files
authored
Automatic ASLR disablement (#1978)
While ASLR is a useful security hardening feature, it introduces unreproducible noise into benchmarks, and we really really really don't want any noise, especially easily avoidable one. Unless prevented by some other security hardening, we can disable ASLR for the current process, and restart it, thus eliminating this noise. Fixes #461
1 parent bc0989a commit f02794a

29 files changed

+106
-8
lines changed

bindings/python/google_benchmark/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ def main(argv=None):
137137
return app.run(_run_benchmarks, argv=argv, flags_parser=_flags_parser)
138138

139139

140+
# FIXME: can we rerun with disabled ASLR?
141+
140142
# Methods for use with custom main function.
141143
initialize = _benchmark.Initialize
142144
run_benchmarks = _benchmark.RunSpecifiedBenchmarks

docs/reducing_variance.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ If you see this error:
5050
you might want to disable the ASLR security hardening feature while running the
5151
benchmark.
5252

53+
The simplest way is to add
54+
```
55+
benchmark::MaybeReenterWithoutASLR(argc, argv);
56+
```
57+
as the first line of your `main()` function. It will try to disable ASLR
58+
for the current processor, and, if successful, re-execute the binary.
59+
Note that `personality(2)` may be forbidden by e.g. seccomp (which happens
60+
by default if you are running in a Docker container).
61+
62+
Note that if you link to `benchmark_main` already does that for you.
63+
5364
To globally disable ASLR on Linux, run
5465
```
5566
echo 0 > /proc/sys/kernel/randomize_va_space

docs/user_guide.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,6 +1268,7 @@ For Example:
12681268
auto BM_test = [](benchmark::State& st, auto Inputs) { /* ... */ };
12691269

12701270
int main(int argc, char** argv) {
1271+
benchmark::MaybeReenterWithoutASLR(argc, argv);
12711272
for (auto& test_input : { /* ... */ })
12721273
benchmark::RegisterBenchmark(test_input.name(), BM_test, test_input);
12731274
benchmark::Initialize(&argc, argv);

include/benchmark/benchmark.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ BENCHMARK(BM_StringCopy);
4040
// my_unittest --benchmark_filter=String
4141
// my_unittest --benchmark_filter='Copy|Creation'
4242
int main(int argc, char** argv) {
43+
benchmark::MaybeReenterWithoutASLR(argc, argv);
4344
benchmark::Initialize(&argc, argv);
4445
benchmark::RunSpecifiedBenchmarks();
4546
benchmark::Shutdown();
@@ -334,6 +335,8 @@ using callback_function = std::function<void(const benchmark::State&)>;
334335
// Default number of minimum benchmark running time in seconds.
335336
const char kDefaultMinTimeStr[] = "0.5s";
336337

338+
BENCHMARK_EXPORT void MaybeReenterWithoutASLR(int, char**);
339+
337340
// Returns the version of the library.
338341
BENCHMARK_EXPORT std::string GetBenchmarkVersion();
339342

@@ -1687,6 +1690,7 @@ class Fixture : public internal::Benchmark {
16871690
// Note the workaround for Hexagon simulator passing argc != 0, argv = NULL.
16881691
#define BENCHMARK_MAIN() \
16891692
int main(int argc, char** argv) { \
1693+
benchmark::MaybeReenterWithoutASLR(argc, argv); \
16901694
char arg0_default[] = "benchmark"; \
16911695
char* args_default = reinterpret_cast<char*>(arg0_default); \
16921696
if (!argv) { \

src/benchmark.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
#include <unistd.h>
2727
#endif
2828

29+
#ifdef BENCHMARK_OS_LINUX
30+
#include <sys/personality.h>
31+
#endif
32+
2933
#include <algorithm>
3034
#include <atomic>
3135
#include <condition_variable>
@@ -813,6 +817,35 @@ int InitializeStreams() {
813817

814818
} // end namespace internal
815819

820+
void MaybeReenterWithoutASLR(int /*argc*/, char** argv) {
821+
// On e.g. Hexagon simulator, argv may be NULL.
822+
if (!argv) return;
823+
824+
#ifdef BENCHMARK_OS_LINUX
825+
const auto curr_personality = personality(0xffffffff);
826+
827+
// We should never fail to read-only query the current personality,
828+
// but let's be cautious.
829+
if (curr_personality == -1) return;
830+
831+
// If ASLR is already disabled, we have nothing more to do.
832+
if (curr_personality & ADDR_NO_RANDOMIZE) return;
833+
834+
// Try to change the personality to disable ASLR.
835+
const auto prev_personality = personality(
836+
static_cast<unsigned long>(curr_personality) | ADDR_NO_RANDOMIZE);
837+
838+
// Have we failed to change the personality? That may happen.
839+
if (prev_personality == -1) return;
840+
841+
execv(argv[0], argv);
842+
// The exec() functions return only if an error has occurred,
843+
// in which case we want to just continue as-is.
844+
#else
845+
return;
846+
#endif
847+
}
848+
816849
std::string GetBenchmarkVersion() {
817850
#ifdef BENCHMARK_VERSION
818851
return {BENCHMARK_VERSION};

test/benchmark_min_time_flag_iters_test.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ static void BM_MyBench(benchmark::State& state) {
4444
BENCHMARK(BM_MyBench);
4545

4646
int main(int argc, char** argv) {
47+
benchmark::MaybeReenterWithoutASLR(argc, argv);
48+
4749
// Make a fake argv and append the new --benchmark_min_time=<foo> to it.
4850
int fake_argc = argc + 1;
4951
std::vector<const char*> fake_argv(static_cast<size_t>(fake_argc));

test/benchmark_min_time_flag_time_test.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ static void BM_MyBench(benchmark::State& state) {
6969
BENCHMARK(BM_MyBench);
7070

7171
int main(int argc, char** argv) {
72+
benchmark::MaybeReenterWithoutASLR(argc, argv);
73+
7274
// Make a fake argv and append the new --benchmark_min_time=<foo> to it.
7375
int fake_argc = argc + 1;
7476
std::vector<const char*> fake_argv(static_cast<size_t>(fake_argc));

test/benchmark_setup_teardown_test.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ BENCHMARK(BM_WithRep)
134134
->Repetitions(4);
135135

136136
int main(int argc, char** argv) {
137+
benchmark::MaybeReenterWithoutASLR(argc, argv);
138+
137139
benchmark::Initialize(&argc, argv);
138140

139141
size_t ret = benchmark::RunSpecifiedBenchmarks(".");

test/complexity_test.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,4 +269,7 @@ ADD_COMPLEXITY_CASES(complexity_capture_name, complexity_capture_name + "_BigO",
269269
// --------------------------- TEST CASES END ------------------------------ //
270270
// ========================================================================= //
271271

272-
int main(int argc, char *argv[]) { RunOutputTests(argc, argv); }
272+
int main(int argc, char *argv[]) {
273+
benchmark::MaybeReenterWithoutASLR(argc, argv);
274+
RunOutputTests(argc, argv);
275+
}

test/diagnostics_test.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ int main(int argc, char* argv[]) {
9494
(void)argc;
9595
(void)argv;
9696
#else
97+
benchmark::MaybeReenterWithoutASLR(argc, argv);
9798
benchmark::internal::GetAbortHandler() = &TestHandler;
9899
benchmark::Initialize(&argc, argv);
99100
benchmark::RunSpecifiedBenchmarks();

0 commit comments

Comments
 (0)