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