summaryrefslogtreecommitdiffstats
path: root/src/san.c
blob: 15fdb7ffe20f9379c159bc20e9c66568866b2dea (plain)
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"

#include "jemalloc/internal/assert.h"
#include "jemalloc/internal/ehooks.h"
#include "jemalloc/internal/san.h"
#include "jemalloc/internal/tsd.h"

/* The sanitizer options. */
size_t opt_san_guard_large = SAN_GUARD_LARGE_EVERY_N_EXTENTS_DEFAULT;
size_t opt_san_guard_small = SAN_GUARD_SMALL_EVERY_N_EXTENTS_DEFAULT;

static inline void
san_find_guarded_addr(edata_t *edata, uintptr_t *guard1, uintptr_t *guard2,
    uintptr_t *addr, size_t size, bool left, bool right) {
	assert(!edata_guarded_get(edata));
	assert(size % PAGE == 0);
	*addr = (uintptr_t)edata_base_get(edata);
	if (left) {
		*guard1 = *addr;
		*addr += SAN_PAGE_GUARD;
	} else {
		*guard1 = 0;
	}

	if (right) {
		*guard2 = *addr + size;
	} else {
		*guard2 = 0;
	}
}

static inline void
san_find_unguarded_addr(edata_t *edata, uintptr_t *guard1, uintptr_t *guard2,
    uintptr_t *addr, size_t size, bool left, bool right) {
	assert(edata_guarded_get(edata));
	assert(size % PAGE == 0);
	*addr = (uintptr_t)edata_base_get(edata);
	if (right) {
		*guard2 = *addr + size;
	} else {
		*guard2 = 0;
	}

	if (left) {
		*guard1 = *addr - SAN_PAGE_GUARD;
		assert(*guard1 != 0);
		*addr = *guard1;
	} else {
		*guard1 = 0;
	}
}

void
san_guard_pages(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, emap_t *emap,
    bool left, bool right, bool remap) {
	assert(left || right);
	if (remap) {
		emap_deregister_boundary(tsdn, emap, edata);
	}

	size_t size_with_guards = edata_size_get(edata);
	size_t usize = (left && right)
	    ? san_two_side_unguarded_sz(size_with_guards)
	    : san_one_side_unguarded_sz(size_with_guards);

	uintptr_t guard1, guard2, addr;
	san_find_guarded_addr(edata, &guard1, &guard2, &addr, usize, left,
	    right);

	assert(edata_state_get(edata) == extent_state_active);
	ehooks_guard(tsdn, ehooks, (void *)guard1, (void *)guard2);

	/* Update the guarded addr and usable size of the edata. */
	edata_size_set(edata, usize);
	edata_addr_set(edata, (void *)addr);
	edata_guarded_set(edata, true);

	if (remap) {
		emap_register_boundary(tsdn, emap, edata, SC_NSIZES,
		    /* slab */ false);
	}
}

static void
san_unguard_pages_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
    emap_t *emap, bool left, bool right, bool remap) {
	assert(left || right);
	/* Remove the inner boundary which no longer exists. */
	if (remap) {
		assert(edata_state_get(edata) == extent_state_active);
		emap_deregister_boundary(tsdn, emap, edata);
	} else {
		assert(edata_state_get(edata) == extent_state_retained);
	}

	size_t size = edata_size_get(edata);
	size_t size_with_guards = (left && right)
	    ? san_two_side_guarded_sz(size)
	    : san_one_side_guarded_sz(size);

	uintptr_t guard1, guard2, addr;
	san_find_unguarded_addr(edata, &guard1, &guard2, &addr, size, left,
	    right);

	ehooks_unguard(tsdn, ehooks, (void *)guard1, (void *)guard2);

	/* Update the true addr and usable size of the edata. */
	edata_size_set(edata, size_with_guards);
	edata_addr_set(edata, (void *)addr);
	edata_guarded_set(edata, false);

	/*
	 * Then re-register the outer boundary including the guards, if
	 * requested.
	 */
	if (remap) {
		emap_register_boundary(tsdn, emap, edata, SC_NSIZES,
		    /* slab */ false);
	}
}

void
san_unguard_pages(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
    emap_t *emap, bool left, bool right) {
	san_unguard_pages_impl(tsdn, ehooks, edata, emap, left, right,
	    /* remap */ true);
}

void
san_unguard_pages_pre_destroy(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
    emap_t *emap) {
	emap_assert_not_mapped(tsdn, emap, edata);
	/*
	 * We don't want to touch the emap of about to be destroyed extents, as
	 * they have been unmapped upon eviction from the retained ecache. Also,
	 * we unguard the extents to the right, because retained extents only
	 * own their right guard page per san_bump_alloc's logic.
	 */
	 san_unguard_pages_impl(tsdn, ehooks, edata, emap, /* left */ false,
	    /* right */ true, /* remap */ false);
}

void
tsd_san_init(tsd_t *tsd) {
	*tsd_san_extents_until_guard_smallp_get(tsd) = opt_san_guard_small;
	*tsd_san_extents_until_guard_largep_get(tsd) = opt_san_guard_large;
}