aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib/src/tests/util/memory_trap/memory_trap_test.cpp
blob: a220e81f3a348c791ed39996a3cdfb90662ac465 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/util/memory_trap.h>
#include <vespa/vespalib/gtest/gtest.h>
#include <cstdlib>

using namespace vespalib;
using namespace ::testing;

template <typename T>
void do_not_optimize_away(T&& t) noexcept {
    asm volatile("" : : "m"(t) : "memory"); // Clobber the value to avoid losing it to compiler optimizations
}

struct MemoryTrapTest : Test {
    static void SetUpTestSuite() {
        // Don't overwrite env var if already set; we'll assume it's done for a good reason.
        setenv("VESPA_USE_MPROTECT_TRAP", "true", 0);
    }
};

TEST_F(MemoryTrapTest, untouched_memory_traps_do_not_trigger) {
    InlineMemoryTrap<2> stack_trap;
    HeapMemoryTrap heap_trap(4);
    // No touching == no crashing. Good times.
}

TEST_F(MemoryTrapTest, write_to_stack_trap_eventually_discovered) {
    // We don't explicitly test death messages since the way the process dies depends on
    // whether mprotect is enabled, whether ASAN instrumentation is enabled etc.
    ASSERT_DEATH({
        InlineMemoryTrap<2> stack_trap;
        // This may trigger immediately or on destruction. Either way it eventually kills the process.
        stack_trap.trapper().buffer()[0] = 0x01;
    },"");
}

TEST_F(MemoryTrapTest, write_to_heap_trap_eventually_discovered) {
    ASSERT_DEATH({
        HeapMemoryTrap heap_trap(4);
        // This may trigger immediately or on destruction. Either way it eventually kills the process.
        heap_trap.trapper().buffer()[heap_trap.trapper().size() - 1] = 0x01;
    },"");
}

TEST_F(MemoryTrapTest, read_from_hw_backed_trap_crashes_process) {
    if (!MemoryRangeTrapper::hw_trapping_enabled()) {
        return;
    }
    ASSERT_DEATH({
        HeapMemoryTrap heap_trap(4); // Entire buffer should always be covered
        // Clobber trap just in case the compiler is clever enough to look into the trap implementation
        // and see that we memset everything to zero and `dummy` can thus be constant-promoted to 0
        // (probably won't dare to do this anyway due to opaque mprotect() that touches buffer pointer).
        do_not_optimize_away(heap_trap);
        char dummy = heap_trap.trapper().buffer()[0];
        do_not_optimize_away(dummy); // never reached
    },"");
}

GTEST_MAIN_RUN_ALL_TESTS()