145
|
1 /* Copyright (C) 2012-2020 Free Software Foundation, Inc.
|
111
|
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>
|
|
35 #if defined (__CYGWIN__) || defined (__MINGW32__)
|
|
36 #include <windows.h>
|
|
37 #else
|
|
38 #include <sys/mman.h>
|
|
39 #endif
|
|
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
|
|
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
|
|
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
|
|
147 __vtv_malloc_dump_stats ();
|
|
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
|
|
180 #if defined (__CYGWIN__) || defined (__MINGW32__)
|
|
181 if ((allocated = VirtualAlloc(NULL, size, MEM_RESERVE|MEM_COMMIT,
|
|
182 PAGE_READWRITE)) == 0)
|
|
183 #else
|
|
184 if ((allocated = mmap (NULL, size, PROT_READ | PROT_WRITE,
|
|
185 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == 0)
|
|
186 #endif
|
|
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
|
|
197 obstack_chunk_free (void *)
|
|
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
|
|
213 #if defined (__CYGWIN__) || defined (__MINGW32__)
|
|
214 if (VTV_PAGE_SIZE != sysconf_SC_PAGE_SIZE())
|
|
215 #else
|
|
216 if (VTV_PAGE_SIZE != sysconf (_SC_PAGE_SIZE))
|
|
217 #endif
|
|
218 VTV_error ();
|
|
219
|
|
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
|
|
225 obstack_specify_allocation (&vtv_obstack, VTV_PAGE_SIZE, sizeof (long),
|
|
226 obstack_chunk_alloc, obstack_chunk_free);
|
|
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 }
|