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