]>
Commit | Line | Data |
---|---|---|
8d9254fc | 1 | /* Copyright (C) 2012-2020 Free Software Foundation, Inc. |
2077db1b CT |
2 | |
3 | This file is part of GCC. | |
4 | ||
5 | GCC is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 3, or (at your option) | |
8 | any later version. | |
9 | ||
10 | GCC is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | Under Section 7 of GPL version 3, you are granted additional | |
16 | permissions described in the GCC Runtime Library Exception, version | |
17 | 3.1, as published by the Free Software Foundation. | |
18 | ||
19 | You should have received a copy of the GNU General Public License and | |
20 | a copy of the GCC Runtime Library Exception along with this program; | |
21 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
22 | <http://www.gnu.org/licenses/>. */ | |
23 | ||
24 | /* This file is part of the vtable verification runtime library. It | |
25 | contains our memory allocation and deallocation routines, which we | |
26 | use in order to keep track of the pages in memory in which our sets | |
27 | of valid vtable pointes are stored. (We need to know the pages so | |
28 | we can set the protections on them appropriately). For more | |
29 | information about the vtable verification feature, see the comments | |
30 | in vtv_rts.cc. We use the existing obstack implementation in our | |
31 | memory allocation scheme. */ | |
32 | ||
33 | #include <stdlib.h> | |
34 | #include <unistd.h> | |
f7f049fa CT |
35 | #if defined (__CYGWIN__) || defined (__MINGW32__) |
36 | #include <windows.h> | |
37 | #else | |
2077db1b | 38 | #include <sys/mman.h> |
f7f049fa | 39 | #endif |
2077db1b CT |
40 | #include <sys/types.h> |
41 | #include <sys/stat.h> | |
42 | #include <fcntl.h> | |
43 | #include <stdio.h> | |
44 | ||
45 | #include "vtv_utils.h" | |
46 | #include "vtv_malloc.h" | |
47 | #include "obstack.h" | |
48 | ||
49 | /* The following variables are used only for debugging and performance tuning | |
50 | purposes. Therefore they do not need to be "protected". They cannot be used | |
51 | to attack the vtable verification system and if they become corrupted it will | |
52 | not affect the correctness or security of any of the rest of the vtable | |
53 | verification feature. */ | |
54 | ||
55 | unsigned int num_calls_to_mprotect = 0; | |
56 | unsigned int num_pages_protected = 0; | |
57 | unsigned int long long mprotect_cycles = 0; | |
58 | ||
59 | /* Put the following variables in our ".vtable_map_vars" section so | |
60 | that they are protected. They are explicitly unprotected and | |
61 | protected again by calls to __vtv_unprotect and __vtv_protect */ | |
62 | ||
63 | static struct obstack vtv_obstack VTV_PROTECTED_VAR; | |
64 | static void *current_chunk VTV_PROTECTED_VAR = 0; | |
65 | static size_t current_chunk_size VTV_PROTECTED_VAR = 0; | |
66 | static int malloc_initialized VTV_PROTECTED_VAR = 0; | |
67 | ||
f7f049fa CT |
68 | #if defined (__CYGWIN__) || defined (__MINGW32__) |
69 | //sysconf(_SC_PAGE_SIZE) port | |
70 | long sysconf_SC_PAGE_SIZE() | |
71 | { | |
72 | SYSTEM_INFO si; | |
73 | GetSystemInfo(&si); | |
74 | long pageSize = (long)si.dwPageSize; | |
75 | return pageSize; | |
76 | //return 4096; // standard usermode 32bit pagesize in bytes // FIXME | |
77 | } | |
78 | #endif | |
79 | ||
2077db1b CT |
80 | /* The function goes through and counts all the pages we have allocated |
81 | so far. It returns the page count. */ | |
82 | ||
83 | int | |
84 | __vtv_count_mmapped_pages (void) | |
85 | { | |
86 | int count = 0; | |
87 | struct _obstack_chunk * ci = (struct _obstack_chunk *) current_chunk; | |
88 | while (ci) | |
89 | { | |
90 | count++; | |
91 | ci = ci->prev; | |
92 | } | |
93 | ||
94 | return count; | |
95 | } | |
96 | ||
97 | /* This function goes through all of the pages we have allocated so | |
98 | far and calls mprotect to change the protections on the pages, | |
99 | according to the value of PROTECTION_FLAG. */ | |
100 | ||
101 | static void | |
102 | change_protections_on_data_chunks (int protection_flag) | |
103 | { | |
104 | struct _obstack_chunk *ci; | |
105 | ci = (struct _obstack_chunk *) current_chunk; | |
106 | ||
107 | while (ci) | |
108 | { | |
109 | /* Initial set up for mprotect call.*/ | |
110 | struct _obstack_chunk *protect_start = ci; | |
111 | size_t chunk_size; | |
112 | size_t total_size; | |
113 | unsigned int num_pages_in_chunk; | |
114 | char *next_page; | |
115 | unsigned long long start, end; | |
116 | int result; | |
117 | ||
118 | ||
119 | /* As long as the next 'chunk' is adjacent to the current one, | |
120 | keep going down the list. */ | |
121 | do | |
122 | { | |
123 | chunk_size = (ci->limit - (char *) ci); | |
124 | total_size = (ci->limit - (char *) protect_start); | |
125 | num_pages_in_chunk = chunk_size / VTV_PAGE_SIZE; | |
126 | if (chunk_size % VTV_PAGE_SIZE > 0) | |
127 | num_pages_in_chunk++; | |
128 | next_page = (char *) ci + (num_pages_in_chunk * VTV_PAGE_SIZE); | |
129 | ci = ci->prev; | |
130 | } while (ci && (char *) ci == next_page); | |
131 | ||
132 | VTV_DEBUG_ASSERT (((unsigned long) protect_start & (VTV_PAGE_SIZE - 1)) | |
133 | == 0); | |
134 | ||
135 | /* Protect the contiguous chunks so far. */ | |
136 | start = rdtsc (); | |
137 | result = mprotect (protect_start, total_size, protection_flag); | |
138 | end = rdtsc (); | |
139 | mprotect_cycles += end - start; | |
140 | if (result == -1) | |
141 | VTV_error (); | |
142 | num_calls_to_mprotect++; | |
143 | num_pages_protected += (total_size + VTV_PAGE_SIZE - 1)/ VTV_PAGE_SIZE; | |
144 | } | |
145 | ||
146 | #ifdef VTV_DEBUG | |
b0cca5ec | 147 | __vtv_malloc_dump_stats (); |
2077db1b CT |
148 | #endif |
149 | } | |
150 | ||
151 | /* This function makes all of our allocated pages read-only. */ | |
152 | ||
153 | void | |
154 | __vtv_malloc_protect (void) | |
155 | { | |
156 | change_protections_on_data_chunks (PROT_READ); | |
157 | } | |
158 | ||
159 | /* This function makes all of our allocated pages read-write. */ | |
160 | ||
161 | void | |
162 | __vtv_malloc_unprotect (void) | |
163 | { | |
164 | change_protections_on_data_chunks (PROT_READ | PROT_WRITE); | |
165 | } | |
166 | ||
167 | /* Allocates a SIZE-sized chunk of memory that is aligned to a page | |
168 | boundary. The amount of memory requested (SIZE) must be a multiple | |
169 | of the page size. Note: We must use mmap to allocate the memory; | |
170 | using malloc here will cause problems. */ | |
171 | ||
172 | static void * | |
173 | obstack_chunk_alloc (size_t size) | |
174 | { | |
175 | /* Increase size to the next multiple of VTV_PAGE_SIZE. */ | |
176 | size = (size + (VTV_PAGE_SIZE - 1)) & (~(VTV_PAGE_SIZE - 1)); | |
177 | VTV_DEBUG_ASSERT ((size & (VTV_PAGE_SIZE - 1)) == 0); | |
178 | void *allocated; | |
179 | ||
f7f049fa CT |
180 | #if defined (__CYGWIN__) || defined (__MINGW32__) |
181 | if ((allocated = VirtualAlloc(NULL, size, MEM_RESERVE|MEM_COMMIT, | |
182 | PAGE_READWRITE)) == 0) | |
183 | #else | |
2077db1b CT |
184 | if ((allocated = mmap (NULL, size, PROT_READ | PROT_WRITE, |
185 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == 0) | |
f7f049fa | 186 | #endif |
2077db1b CT |
187 | VTV_error (); |
188 | ||
189 | VTV_DEBUG_ASSERT (((unsigned long) allocated & (VTV_PAGE_SIZE - 1)) == 0); | |
190 | ||
191 | current_chunk = allocated; | |
192 | current_chunk_size = size; | |
193 | return allocated; | |
194 | } | |
195 | ||
196 | static void | |
37697711 | 197 | obstack_chunk_free (void *) |
2077db1b CT |
198 | { |
199 | /* Do nothing. For our purposes there should be very little | |
200 | de-allocation. */ | |
201 | } | |
202 | ||
203 | /* This function sets up and initializes the obstack pieces for our | |
204 | memory allocation scheme. */ | |
205 | ||
206 | void | |
207 | __vtv_malloc_init (void) | |
208 | { | |
209 | /* Make sure we only execute the main body of this function ONCE. */ | |
210 | if (malloc_initialized) | |
211 | return; | |
212 | ||
f7f049fa CT |
213 | #if defined (__CYGWIN__) || defined (__MINGW32__) |
214 | if (VTV_PAGE_SIZE != sysconf_SC_PAGE_SIZE()) | |
215 | #else | |
2077db1b | 216 | if (VTV_PAGE_SIZE != sysconf (_SC_PAGE_SIZE)) |
f7f049fa | 217 | #endif |
2077db1b CT |
218 | VTV_error (); |
219 | ||
2077db1b CT |
220 | /* We guarantee that the obstack alloc failed handler will never be |
221 | called because in case the allocation of the chunk fails, it will | |
222 | never return */ | |
223 | obstack_alloc_failed_handler = NULL; | |
224 | ||
37697711 AM |
225 | obstack_specify_allocation (&vtv_obstack, VTV_PAGE_SIZE, sizeof (long), |
226 | obstack_chunk_alloc, obstack_chunk_free); | |
2077db1b CT |
227 | malloc_initialized = 1; |
228 | } | |
229 | ||
230 | /* This is our external interface for the memory allocation. SIZE is | |
231 | the requested number of bytes to be allocated/ */ | |
232 | ||
233 | void * | |
234 | __vtv_malloc (size_t size) | |
235 | { | |
236 | return obstack_alloc (&vtv_obstack, size); | |
237 | } | |
238 | ||
239 | ||
240 | /* This is our external interface for memory deallocation. */ | |
241 | ||
242 | void | |
243 | __vtv_free (void *) | |
244 | { | |
245 | /* Do nothing. We dont care about recovering unneded memory at this | |
246 | time. */ | |
247 | } | |
248 | ||
249 | ||
250 | /* This is a debugging function tat collects statistics about our | |
251 | memory allocation. */ | |
252 | void | |
253 | __vtv_malloc_stats (void) | |
254 | { | |
255 | int count = 0; | |
256 | struct _obstack_chunk * ci = (struct _obstack_chunk *) current_chunk; | |
257 | while (ci) | |
258 | { | |
259 | count++; | |
260 | ci = ci->prev; | |
261 | } | |
262 | fprintf (stderr, | |
263 | "__vtv_malloc_stats:\n Page Size = %lu bytes\n " | |
264 | "Number of pages = %d\n", static_cast<unsigned long>(VTV_PAGE_SIZE), | |
265 | count); | |
266 | } | |
267 | ||
268 | /* This is a debugging function. It writes out our memory allocation | |
269 | statistics to a log file. */ | |
270 | ||
271 | void | |
272 | __vtv_malloc_dump_stats (void) | |
273 | { | |
274 | static int fd = -1; | |
275 | ||
276 | if (fd == -1) | |
277 | fd = __vtv_open_log ("vtv_mem_protection.log"); | |
278 | if (fd == -1) | |
279 | return; | |
280 | ||
281 | int count = 0; | |
282 | struct _obstack_chunk * ci = (struct _obstack_chunk *) current_chunk; | |
283 | while (ci) | |
284 | { | |
285 | count++; | |
286 | ci = ci->prev; | |
287 | } | |
288 | ||
289 | __vtv_add_to_log (fd, "__vtv_malloc_protect protected=%d pages\n", count); | |
290 | } |