123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809 |
- /* Copyright (C) 2012-2013
- Free Software Foundation
- This file is part of GCC.
- GCC is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
- GCC is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- Under Section 7 of GPL version 3, you are granted additional
- permissions described in the GCC Runtime Library Exception, version
- 3.1, as published by the Free Software Foundation.
- You should have received a copy of the GNU General Public License and
- a copy of the GCC Runtime Library Exception along with this program;
- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
- <http://www.gnu.org/licenses/>. */
- /* This file is part of the vtable security feature implementation.
- The vtable security feature is designed to detect when a virtual
- call is about to be made through an invalid vtable pointer
- (possibly due to data corruption or malicious attacks). The
- compiler finds every virtual call, and inserts a verification call
- before the virtual call. The verification call takes the actual
- vtable pointer value in the object through which the virtual call
- is being made, and compares the vtable pointer against a set of all
- valid vtable pointers that the object could contain (this set is
- based on the declared type of the object). If the pointer is in
- the valid set, execution is allowed to continue; otherwise the
- program is halted.
- There are several pieces needed in order to make this work: 1. For
- every virtual class in the program (i.e. a class that contains
- virtual methods), we need to build the set of all possible valid
- vtables that an object of that class could point to. This includes
- vtables for any class(es) that inherit from the class under
- consideration. 2. For every such data set we build up, we need a
- way to find and reference the data set. This is complicated by the
- fact that the real vtable addresses are not known until runtime,
- when the program is loaded into memory, but we need to reference the
- sets at compile time when we are inserting verification calls into
- the program. 3. We need to find every virtual call in the program,
- and insert the verification call (with the appropriate arguments)
- before the virtual call. 4. We need some runtime library pieces:
- the code to build up the data sets at runtime; the code to actually
- perform the verification using the data sets; and some code to set
- protections on the data sets, so they themselves do not become
- hacker targets.
- To find and reference the set of valid vtable pointers for any given
- virtual class, we create a special global varible for each virtual
- class. We refer to this as the "vtable map variable" for that
- class. The vtable map variable has the type "void *", and is
- initialized by the compiler to NULL. At runtime when the set of
- valid vtable pointers for a virtual class, e.g. class Foo, is built,
- the vtable map variable for class Foo is made to point to the set.
- During compile time, when the compiler is inserting verification
- calls into the program, it passes the vtable map variable for the
- appropriate class to the verification call, so that at runtime the
- verification call can find the appropriate data set.
- The actual set of valid vtable pointers for a polymorphic class,
- e.g. class Foo, cannot be built until runtime, when the vtables get
- loaded into memory and their addresses are known. But the knowledge
- about which vtables belong in which class' hierarchy is only known
- at compile time. Therefore at compile time we collect class
- hierarchy and vtable information about every virtual class, and we
- generate calls to build up the data sets at runtime. To build the
- data sets, we call one of the functions we add to the runtime
- library, __VLTRegisterPair. __VLTRegisterPair takes two arguments,
- a vtable map variable and the address of a vtable. If the vtable
- map variable is currently NULL, it creates a new data set (hash
- table), makes the vtable map variable point to the new data set, and
- inserts the vtable address into the data set. If the vtable map
- variable is not NULL, it just inserts the vtable address into the
- data set. In order to make sure that our data sets are built before
- any verification calls happen, we create a special constructor
- initialization function for each compilation unit, give it a very
- high initialization priority, and insert all of our calls to
- __VLTRegisterPair into our special constructor initialization
- function. */
- /* This file contains the main externally visible runtime library
- functions for vtable verification: __VLTChangePermission,
- __VLTRegisterPair, and __VLTVerifyVtablePointer. It also contains
- debug versions __VLTRegisterPairDebug and
- __VLTVerifyVtablePointerDebug, which have extra parameters in order
- to make it easier to debug verification failures.
- The final piece of functionality implemented in this file is symbol
- resolution for multiple instances of the same vtable map variable.
- If the same virtual class is used in two different compilation
- units, then each compilation unit will create a vtable map variable
- for the class. We need all instances of the same vtable map
- variable to point to the same (single) set of valid vtable
- pointers for the class, so we wrote our own hashtable-based symbol
- resolution for vtable map variables (with a tiny optimization in
- the case where there is only one instance of the variable).
- There are two other important pieces to the runtime for vtable
- verification besides the main pieces that go into libstdc++.so: two
- special tiny shared libraries, libvtv_init.so and libvtv_stubs.so.
- libvtv_init.so is built from vtv_init.cc. It is designed to help
- minimize the calls made to mprotect (see the comments in
- vtv_init.cc for more details). Anything compiled with
- "-fvtable-verify=std" must be linked with libvtv_init.so (the gcc
- driver has been modified to do this). vtv_stubs.so is built from
- vtv_stubs.cc. It replaces the main runtime functions
- (__VLTChangePermissino, __VLTRegisterPair and
- __VLTVerifyVtablePointer) with stub functions that do nothing. If
- a programmer has a library that was built with verification, but
- wishes to not have verification turned on, the programmer can link
- in the vtv_stubs.so library. */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #if defined (__CYGWIN__) || defined (__MINGW32__)
- #include <windows.h>
- #include <winternl.h>
- #include <psapi.h>
- #else
- #include <execinfo.h>
- #endif
- #include <unistd.h>
- #if !defined (__CYGWIN__) && !defined (__MINGW32__)
- #include <sys/mman.h>
- #include <link.h>
- #endif
- #include <errno.h>
- #include <fcntl.h>
- #include <limits.h>
- /* For gthreads suppport */
- #include <bits/c++config.h>
- #include <ext/concurrence.h>
- #include "vtv_utils.h"
- #include "vtv_malloc.h"
- #include "vtv_set.h"
- #include "vtv_map.h"
- #include "vtv_rts.h"
- #include "vtv_fail.h"
- #include "vtv-change-permission.h"
- #if defined (__CYGWIN__) || defined (__MINGW32__)
- // porting: fix link error to libc
- void __fortify_fail (const char * msg){
- OutputDebugString(msg);
- abort();
- }
- #else
- extern "C" {
- /* __fortify_fail is a function in glibc that calls __libc_message,
- causing it to print out a program termination error message
- (including the name of the binary being terminated), a stack
- trace where the error occurred, and a memory map dump. Ideally
- we would have called __libc_message directly, but that function
- does not appear to be accessible to functions outside glibc,
- whereas __fortify_fail is. We call __fortify_fail from
- __vtv_really_fail. We looked at calling __libc_fatal, which is
- externally accessible, but it does not do the back trace and
- memory dump. */
- extern void __fortify_fail (const char *) __attribute__((noreturn));
- } /* extern "C" */
- #endif
- /* The following variables are used only for debugging and performance
- tuning purposes. Therefore they do not need to be "protected".
- They cannot be used to attack the vtable verification system and if
- they become corrupted it will not affect the correctness or
- security of any of the rest of the vtable verification feature. */
- unsigned int num_calls_to_regset = 0;
- unsigned int num_calls_to_regpair = 0;
- unsigned int num_calls_to_verify_vtable = 0;
- unsigned long long regset_cycles = 0;
- unsigned long long regpair_cycles = 0;
- unsigned long long verify_vtable_cycles = 0;
- /* Be careful about initialization of statics in this file. Some of
- the routines below are called before any runtime initialization for
- statics in this file will be done. For example, dont try to
- initialize any of these statics with a runtime call (for ex:
- sysconf). The initialization will happen after calls to the routines
- to protect/unprotec the vtabla_map variables */
- /* No need to mark the following variables with VTV_PROTECTED_VAR.
- These are either const or are only used for debugging/tracing.
- debugging/tracing will not be ON on production environments */
- static const bool debug_hash = HASHTABLE_STATS;
- static const int debug_functions = 0;
- static const int debug_init = 0;
- static const int debug_verify_vtable = 0;
- #ifdef VTV_DEBUG
- static const int debug_functions = 1;
- static const int debug_init = 1;
- static const int debug_verify_vtable = 1;
- #endif
- /* Global file descriptor variables for logging, tracing and debugging. */
- static int init_log_fd = -1;
- static int verify_vtable_log_fd = -1;
- /* This holds a formatted error logging message, to be written to the
- vtable verify failures log. */
- static char debug_log_message[1024];
- #ifdef __GTHREAD_MUTEX_INIT
- static __gthread_mutex_t change_permissions_lock = __GTHREAD_MUTEX_INIT;
- #else
- static __gthread_mutex_t change_permissions_lock;
- #endif
- #ifndef VTV_STATS
- #define VTV_STATS 0
- #endif
- #if VTV_STATS
- static inline unsigned long long
- get_cycle_count (void)
- {
- return rdtsc();
- }
- static inline void
- accumulate_cycle_count (unsigned long long *sum, unsigned long long start)
- {
- unsigned long long end = rdtsc();
- *sum = *sum + (end - start);
- }
- static inline void
- increment_num_calls (unsigned int *num_calls)
- {
- *num_calls = *num_calls + 1;
- }
- #else
- static inline unsigned long long
- get_cycle_count (void)
- {
- return (unsigned long long) 0;
- }
- static inline void
- accumulate_cycle_count (unsigned long long *sum __attribute__((__unused__)),
- unsigned long long start __attribute__((__unused__)))
- {
- /* Do nothing. */
- }
- static inline void
- increment_num_calls (unsigned int *num_calls __attribute__((__unused__)))
- {
- /* Do nothing. */
- }
- #endif
- /* Types needed by insert_only_hash_sets. */
- typedef uintptr_t int_vptr;
- /* The set of valid vtable pointers for each virtual class is stored
- in a hash table. This is the hashing function used for the hash
- table. For more information on the implementation of the hash
- table, see the class insert_only_hash_sets in vtv_set.h. */
- struct vptr_hash
- {
- /* Hash function, used to convert vtable pointer, V, (a memory
- address) into an index into the hash table. */
- size_t
- operator() (int_vptr v) const
- {
- const uint32_t x = 0x7a35e4d9;
- const int shift = (sizeof (v) == 8) ? 23 : 21;
- v = x * v;
- return v ^ (v >> shift);
- }
- };
- /* This is the memory allocator used to create the hash table data
- sets of valid vtable pointers. We use VTV_malloc in order to keep
- track of which pages have been allocated, so we can update the
- protections on those pages appropriately. See the class
- insert_only_hash_sets in vtv_set.h for more information. */
- struct vptr_set_alloc
- {
- /* Memory allocator operator. N is the number of bytes to be
- allocated. */
- void *
- operator() (size_t n) const
- {
- return __vtv_malloc (n);
- }
- };
- /* Instantiate the template classes (in vtv_set.h) for our particular
- hash table needs. */
- typedef insert_only_hash_sets<int_vptr, vptr_hash, vptr_set_alloc> vtv_sets;
- typedef vtv_sets::insert_only_hash_set vtv_set;
- typedef vtv_set * vtv_set_handle;
- typedef vtv_set_handle * vtv_set_handle_handle;
- /* Records for caching the section header information that we have
- read out of the file(s) on disk (in dl_iterate_phdr_callback), to
- avoid having to re-open and re-read the same file multiple
- times. */
- struct sect_hdr_data
- {
- #if defined (__CYGWIN__) || defined (__MINGW32__)
- uintptr_t dlpi_addr; /* The header address in the INFO record,
- passed in from dl_iterate_phdr. */
- uintptr_t mp_low; /* Start address of the .vtable_map_vars
- section in memory. */
- #else
- ElfW (Addr) dlpi_addr; /* The header address in the INFO record,
- passed in from dl_iterate_phdr. */
- ElfW (Addr) mp_low; /* Start address of the .vtable_map_vars
- section in memory. */
- #endif
- size_t mp_size; /* Size of the .vtable_map_vars section in
- memory. */
- };
- /* Array for caching the section header information, read from file,
- to avoid re-opening and re-reading the same file over-and-over
- again. */
- #define MAX_ENTRIES 250
- static struct sect_hdr_data vtv_sect_info_cache[MAX_ENTRIES] VTV_PROTECTED_VAR;
- unsigned int num_cache_entries VTV_PROTECTED_VAR = 0;
- /* This function takes the LOAD_ADDR for an object opened by the
- dynamic loader, and checks the array of cached file data to see if
- there is an entry with the same addres. If it finds such an entry,
- it returns the record for that entry; otherwise it returns
- NULL. */
- #if defined (__CYGWIN__) || defined (__MINGW32__)
- struct sect_hdr_data *
- search_cached_file_data (uintptr_t load_addr)
- #else
- struct sect_hdr_data *
- search_cached_file_data (ElfW (Addr) load_addr)
- #endif
- {
- unsigned int i;
- for (i = 0; i < num_cache_entries; ++i)
- {
- if (vtv_sect_info_cache[i].dlpi_addr == load_addr)
- return &(vtv_sect_info_cache[i]);
- }
- return NULL;
- }
- /* This function tries to read COUNT bytes out of the file referred to
- by FD into the buffer BUF. It returns the actual number of bytes
- it succeeded in reading. */
- static size_t
- ReadPersistent (int fd, void *buf, size_t count)
- {
- char *buf0 = (char *) buf;
- size_t num_bytes = 0;
- while (num_bytes < count)
- {
- int len;
- len = read (fd, buf0 + num_bytes, count - num_bytes);
- if (len < 0)
- return -1;
- if (len == 0)
- break;
- num_bytes += len;
- }
- return num_bytes;
- }
- /* This function tries to read COUNT bytes, starting at OFFSET from
- the file referred to by FD, and put them into BUF. It calls
- ReadPersistent to help it do so. It returns the actual number of
- bytes read, or -1 if it fails altogether. */
- static size_t
- ReadFromOffset (int fd, void *buf, const size_t count, const off_t offset)
- {
- off_t off = lseek (fd, offset, SEEK_SET);
- if (off != (off_t) -1)
- return ReadPersistent (fd, buf, count);
- return -1;
- }
- /* The function takes a MESSAGE and attempts to write it to the vtable
- memory protection log (for debugging purposes). If the file is not
- open, it attempts to open the file first. */
- static void
- log_memory_protection_data (char *message)
- {
- static int log_fd = -1;
- if (log_fd == -1)
- log_fd = __vtv_open_log ("vtv_memory_protection_data.log");
- __vtv_add_to_log (log_fd, "%s", message);
- }
- #if defined (__CYGWIN__) || defined (__MINGW32__)
- static void
- read_section_offset_and_length (char *name,
- uintptr_t addr,
- const char *sect_name,
- int mprotect_flags,
- off_t *sect_offset,
- WORD *sect_len)
- {
- bool found = false;
- struct sect_hdr_data *cached_data = NULL;
- /* Check to see if we already have the data for this file. */
- cached_data = search_cached_file_data (addr);
- if (cached_data)
- {
- *sect_offset = cached_data->mp_low;
- *sect_len = cached_data->mp_size;
- return;
- }
- // check for DOS Header magic bytes
- if (*(WORD *)addr == 0x5A4D)
- {
- int name_len = strlen (sect_name);
- int fd = -1;
- /* Attempt to open the binary file on disk. */
- if (strlen (name) == 0)
- {
- return;
- }
- else
- fd = open (name, O_RDONLY | O_BINARY);
- if (fd != -1)
- {
- /* Find the section header information in memory. */
- PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)addr;
- PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((char *)addr
- + pDosHeader->e_lfanew);
- PIMAGE_FILE_HEADER pFileHeader = &pNtHeaders->FileHeader;
- DWORD PointerToStringTable = pFileHeader->PointerToSymbolTable
- + (pFileHeader->NumberOfSymbols*0x12);
- PIMAGE_SECTION_HEADER sect_hdr =
- (PIMAGE_SECTION_HEADER)((char *)&pNtHeaders->OptionalHeader
- + pFileHeader->SizeOfOptionalHeader);
- /* Loop through all the section headers, looking for one whose
- name is ".vtable_map_vars". */
- for (int i = 0; i < pFileHeader->NumberOfSections && !found; ++i)
- {
- char header_name[64];
- /* Check if we have to get the section name from the COFF string
- table. */
- if (sect_hdr[i].Name[0] == '/')
- {
- if (atoi((const char*)sect_hdr[i].Name+1) == 0)
- {
- continue;
- }
- off_t name_offset = PointerToStringTable
- + atoi((const char*)sect_hdr[i].Name+1);
- size_t bytes_read = ReadFromOffset (fd, &header_name, 64,
- name_offset);
- VTV_ASSERT (bytes_read > 0);
- }
- else
- {
- memcpy (&header_name, sect_hdr[i].Name,
- sizeof (sect_hdr[i].Name));
- }
- if (memcmp (header_name, sect_name, name_len) == 0)
- {
- /* We found the section; get its load offset and
- size. */
- *sect_offset = sect_hdr[i].VirtualAddress;
- if (sect_hdr[i].Misc.VirtualSize % VTV_PAGE_SIZE != 0)
- *sect_len = sect_hdr[i].Misc.VirtualSize + VTV_PAGE_SIZE
- - (sect_hdr[i].Misc.VirtualSize % VTV_PAGE_SIZE);
- else
- *sect_len = sect_hdr[i].Misc.VirtualSize;
- found = true;
- }
- }
- close (fd);
- }
- }
- if (*sect_offset != 0 && *sect_len != 0)
- {
- /* Calculate the page location in memory, making sure the
- address is page-aligned. */
- uintptr_t start_addr = addr + *sect_offset;
- *sect_offset = start_addr & ~(VTV_PAGE_SIZE - 1);
- *sect_len = *sect_len - 1;
- /* Since we got this far, we must not have found these pages in
- the cache, so add them to it. NOTE: We could get here either
- while making everything read-only or while making everything
- read-write. We will only update the cache if we get here on
- a read-write (to make absolutely sure the cache is writable
- -- also the read-write pass should come before the read-only
- pass). */
- if ((mprotect_flags & PROT_WRITE)
- && num_cache_entries < MAX_ENTRIES)
- {
- vtv_sect_info_cache[num_cache_entries].dlpi_addr = addr;
- vtv_sect_info_cache[num_cache_entries].mp_low = *sect_offset;
- vtv_sect_info_cache[num_cache_entries].mp_size = *sect_len;
- num_cache_entries++;
- }
- }
- }
- #else
- static void
- read_section_offset_and_length (struct dl_phdr_info *info,
- const char *sect_name,
- int mprotect_flags,
- off_t *sect_offset,
- ElfW (Word) *sect_len)
- {
- char program_name[PATH_MAX];
- char *cptr;
- bool found = false;
- struct sect_hdr_data *cached_data = NULL;
- const ElfW (Phdr) *phdr_info = info->dlpi_phdr;
- const ElfW (Ehdr) *ehdr_info =
- (const ElfW (Ehdr) *) (info->dlpi_addr + info->dlpi_phdr[0].p_vaddr
- - info->dlpi_phdr[0].p_offset);
- /* Get the name of the main executable. This may or may not include
- arguments passed to the program. Find the first space, assume it
- is the start of the argument list, and change it to a '\0'. */
- snprintf (program_name, sizeof (program_name), program_invocation_name);
- /* Check to see if we already have the data for this file. */
- cached_data = search_cached_file_data (info->dlpi_addr);
- if (cached_data)
- {
- *sect_offset = cached_data->mp_low;
- *sect_len = cached_data->mp_size;
- return;
- }
- /* Find the first non-escaped space in the program name and make it
- the end of the string. */
- cptr = strchr (program_name, ' ');
- if (cptr != NULL && cptr[-1] != '\\')
- cptr[0] = '\0';
- if ((phdr_info->p_type == PT_PHDR || phdr_info->p_type == PT_LOAD)
- && (ehdr_info->e_shoff && ehdr_info->e_shnum))
- {
- int name_len = strlen (sect_name);
- int fd = -1;
- /* Attempt to open the binary file on disk. */
- if (strlen (info->dlpi_name) == 0)
- {
- /* If the constructor initialization function was put into
- the preinit array, then this function will get called
- while handling preinit array stuff, in which case
- program_invocation_name has not been initialized. In
- that case we can get the filename of the executable from
- "/proc/self/exe". */
- if (strlen (program_name) > 0)
- {
- if (phdr_info->p_type == PT_PHDR)
- fd = open (program_name, O_RDONLY);
- }
- else
- fd = open ("/proc/self/exe", O_RDONLY);
- }
- else
- fd = open (info->dlpi_name, O_RDONLY);
- if (fd != -1)
- {
- /* Find the section header information in the file. */
- ElfW (Half) strtab_idx = ehdr_info->e_shstrndx;
- ElfW (Shdr) shstrtab;
- off_t shstrtab_offset = ehdr_info->e_shoff +
- (ehdr_info->e_shentsize * strtab_idx);
- size_t bytes_read = ReadFromOffset (fd, &shstrtab, sizeof (shstrtab),
- shstrtab_offset);
- VTV_ASSERT (bytes_read == sizeof (shstrtab));
- ElfW (Shdr) sect_hdr;
- /* This code will be needed once we have crated libvtv.so. */
- bool is_libvtv = false;
- /*
- if (strstr (info->dlpi_name, "libvtv.so"))
- is_libvtv = true;
- */
- /* Loop through all the section headers, looking for one whose
- name is ".vtable_map_vars". */
- for (int i = 0; i < ehdr_info->e_shnum && !found; ++i)
- {
- off_t offset = ehdr_info->e_shoff + (ehdr_info->e_shentsize * i);
- bytes_read = ReadFromOffset (fd, §_hdr, sizeof (sect_hdr),
- offset);
- VTV_ASSERT (bytes_read == sizeof (sect_hdr));
- char header_name[64];
- off_t name_offset = shstrtab.sh_offset + sect_hdr.sh_name;
- bytes_read = ReadFromOffset (fd, &header_name, 64, name_offset);
- VTV_ASSERT (bytes_read > 0);
- if (memcmp (header_name, sect_name, name_len) == 0)
- {
- /* We found the section; get its load offset and
- size. */
- *sect_offset = sect_hdr.sh_addr;
- if (!is_libvtv)
- *sect_len = sect_hdr.sh_size - VTV_PAGE_SIZE;
- else
- *sect_len = sect_hdr.sh_size;
- found = true;
- }
- }
- close (fd);
- }
- }
- if (*sect_offset != 0 && *sect_len != 0)
- {
- /* Calculate the page location in memory, making sure the
- address is page-aligned. */
- ElfW (Addr) start_addr = (const ElfW (Addr)) info->dlpi_addr
- + *sect_offset;
- *sect_offset = start_addr & ~(VTV_PAGE_SIZE - 1);
- *sect_len = *sect_len - 1;
- /* Since we got this far, we must not have found these pages in
- the cache, so add them to it. NOTE: We could get here either
- while making everything read-only or while making everything
- read-write. We will only update the cache if we get here on
- a read-write (to make absolutely sure the cache is writable
- -- also the read-write pass should come before the read-only
- pass). */
- if ((mprotect_flags & PROT_WRITE)
- && num_cache_entries < MAX_ENTRIES)
- {
- vtv_sect_info_cache[num_cache_entries].dlpi_addr = info->dlpi_addr;
- vtv_sect_info_cache[num_cache_entries].mp_low = *sect_offset;
- vtv_sect_info_cache[num_cache_entries].mp_size = *sect_len;
- num_cache_entries++;
- }
- }
- }
- #endif
- #if defined (__CYGWIN__) || defined (__MINGW32__)
- /* This function is used to iterate over all loaded modules and searches
- for a section called ".vtable_map_vars". The only interaction with
- the binary file on disk of the module is to read section names in the
- COFF string table. If the module contains a ".vtable_map_vars" section,
- read section offset and size from the section header of the loaded module.
- Call 'mprotect' on those pages, setting the protection either to
- read-only or read-write, depending on what's in data.
- The calls to change the protection occur in vtv_unprotect_vtable_vars
- and vtv_protect_vtable_vars. */
- static int
- iterate_modules (void *data)
- {
- int * mprotect_flags = (int *) data;
- off_t map_sect_offset = 0;
- WORD map_sect_len = 0;
- char buffer[1024];
- const char *map_sect_name = VTV_PROTECTED_VARS_SECTION;
- HMODULE hMods[1024];
- HANDLE hProcess;
- DWORD cbNeeded;
- hProcess = GetCurrentProcess ();
- if (NULL == hProcess)
- return 0;
- if (EnumProcessModules (hProcess, hMods, sizeof (hMods), &cbNeeded))
- {
- /* Iterate over all loaded modules. */
- for (unsigned int i = 0; i < (cbNeeded / sizeof (HMODULE)); i++)
- {
- char szModName[MAX_PATH];
- if (GetModuleFileNameExA (hProcess, hMods[i], szModName,
- sizeof (szModName)))
- {
- map_sect_offset = 0;
- map_sect_len = 0;
- read_section_offset_and_length (szModName,
- (uintptr_t) hMods[i],
- map_sect_name,
- *mprotect_flags,
- &map_sect_offset,
- &map_sect_len);
- if (debug_functions)
- {
- snprintf (buffer, sizeof(buffer),
- " Looking at load module %s to change permissions to %s\n",
- szModName,
- (*mprotect_flags & PROT_WRITE) ? "READ/WRITE" : "READ-ONLY");
- log_memory_protection_data (buffer);
- }
- /* See if we actually found the section. */
- if (map_sect_offset && map_sect_len)
- {
- unsigned long long start;
- int result;
- if (debug_functions)
- {
- snprintf (buffer, sizeof (buffer),
- " (%s): Protecting %p to %p\n",
- szModName,
- (void *) map_sect_offset,
- (void *) (map_sect_offset + map_sect_len));
- log_memory_protection_data (buffer);
- }
- /* Change the protections on the pages for the section. */
- start = get_cycle_count ();
- result = mprotect ((void *) map_sect_offset, map_sect_len,
- *mprotect_flags);
- accumulate_cycle_count (&mprotect_cycles, start);
- if (result == -1)
- {
- if (debug_functions)
- {
- snprintf (buffer, sizeof (buffer),
- "Failed called to mprotect for %s error: ",
- (*mprotect_flags & PROT_WRITE) ?
- "READ/WRITE" : "READ-ONLY");
- log_memory_protection_data (buffer);
- perror(NULL);
- }
- VTV_error();
- }
- else
- {
- if (debug_functions)
- {
- snprintf (buffer, sizeof (buffer),
- "mprotect'ed range [%p, %p]\n",
- (void *) map_sect_offset,
- (char *) map_sect_offset + map_sect_len);
- log_memory_protection_data (buffer);
- }
- }
- increment_num_calls (&num_calls_to_mprotect);
- /* num_pages_protected += (map_sect_len + VTV_PAGE_SIZE - 1)
- / VTV_PAGE_SIZE; */
- num_pages_protected += (map_sect_len + 4096 - 1) / 4096;
- continue;
- }
- }
- }
- }
- CloseHandle(hProcess);
- return 0;
- }
- #else
- /* This is the callback function used by dl_iterate_phdr (which is
- called from vtv_unprotect_vtable_vars and vtv_protect_vtable_vars).
- It attempts to find the binary file on disk for the INFO record
- that dl_iterate_phdr passes in; open the binary file, and read its
- section header information. If the file contains a
- ".vtable_map_vars" section, read the section offset and size. Use
- the section offset and size, in conjunction with the data in INFO
- to locate the pages in memory where the section is. Call
- 'mprotect' on those pages, setting the protection either to
- read-only or read-write, depending on what's in DATA. */
- static int
- dl_iterate_phdr_callback (struct dl_phdr_info *info, size_t, void *data)
- {
- int * mprotect_flags = (int *) data;
- off_t map_sect_offset = 0;
- ElfW (Word) map_sect_len = 0;
- char buffer[1024];
- char program_name[1024];
- const char *map_sect_name = VTV_PROTECTED_VARS_SECTION;
- /* Check to see if this is the record for the Linux Virtual Dynamic
- Shared Object (linux-vdso.so.1), which exists only in memory (and
- therefore cannot be read from disk). */
- if (strcmp (info->dlpi_name, "linux-vdso.so.1") == 0)
- return 0;
- if (strlen (info->dlpi_name) == 0
- && info->dlpi_addr != 0)
- return 0;
- /* Get the name of the main executable. This may or may not include
- arguments passed to the program. Find the first space, assume it
- is the start of the argument list, and change it to a '\0'. */
- snprintf (program_name, sizeof (program_name), program_invocation_name);
- read_section_offset_and_length (info, map_sect_name, *mprotect_flags,
- &map_sect_offset, &map_sect_len);
- if (debug_functions)
- {
- snprintf (buffer, sizeof(buffer),
- " Looking at load module %s to change permissions to %s\n",
- ((strlen (info->dlpi_name) == 0) ? program_name
- : info->dlpi_name),
- (*mprotect_flags & PROT_WRITE) ? "READ/WRITE" : "READ-ONLY");
- log_memory_protection_data (buffer);
- }
- /* See if we actually found the section. */
- if (map_sect_offset && map_sect_len)
- {
- unsigned long long start;
- int result;
- if (debug_functions)
- {
- snprintf (buffer, sizeof (buffer),
- " (%s): Protecting %p to %p\n",
- ((strlen (info->dlpi_name) == 0) ? program_name
- : info->dlpi_name),
- (void *) map_sect_offset,
- (void *) (map_sect_offset + map_sect_len));
- log_memory_protection_data (buffer);
- }
- /* Change the protections on the pages for the section. */
- start = get_cycle_count ();
- result = mprotect ((void *) map_sect_offset, map_sect_len,
- *mprotect_flags);
- accumulate_cycle_count (&mprotect_cycles, start);
- if (result == -1)
- {
- if (debug_functions)
- {
- snprintf (buffer, sizeof (buffer),
- "Failed called to mprotect for %s error: ",
- (*mprotect_flags & PROT_WRITE) ?
- "READ/WRITE" : "READ-ONLY");
- log_memory_protection_data (buffer);
- perror(NULL);
- }
- VTV_error();
- }
- else
- {
- if (debug_functions)
- {
- snprintf (buffer, sizeof (buffer),
- "mprotect'ed range [%p, %p]\n",
- (void *) map_sect_offset,
- (char *) map_sect_offset + map_sect_len);
- log_memory_protection_data (buffer);
- }
- }
- increment_num_calls (&num_calls_to_mprotect);
- /* num_pages_protected += (map_sect_len + VTV_PAGE_SIZE - 1) / VTV_PAGE_SIZE; */
- num_pages_protected += (map_sect_len + 4096 - 1) / 4096;
- }
- return 0;
- }
- #endif
- /* This function explicitly changes the protection (read-only or read-write)
- on the vtv_sect_info_cache, which is used for speeding up look ups in the
- function dl_iterate_phdr_callback. This data structure needs to be
- explicitly made read-write before any calls to dl_iterate_phdr_callback,
- because otherwise it may still be read-only when dl_iterate_phdr_callback
- attempts to write to it.
- More detailed explanation: dl_iterate_phdr_callback finds all the
- .vtable_map_vars sections in all loaded objects (including the main program)
- and (depending on where it was called from) either makes all the pages in the
- sections read-write or read-only. The vtv_sect_info_cache should be in the
- .vtable_map_vars section for libstdc++.so, which means that normally it would
- be read-only until libstdc++.so is processed by dl_iterate_phdr_callback
- (on the read-write pass), after which it will be writable. But if any loaded
- object gets processed before libstdc++.so, it will attempt to update the
- data cache, which will still be read-only, and cause a seg fault. Hence
- we need a special function, called before dl_iterate_phdr_callback, that
- will make the data cache writable. */
- static void
- change_protections_on_phdr_cache (int protection_flag)
- {
- char * low_address = (char *) &(vtv_sect_info_cache);
- size_t cache_size = MAX_ENTRIES * sizeof (struct sect_hdr_data);
- low_address = (char *) ((uintptr_t) low_address & ~(VTV_PAGE_SIZE - 1));
-
- if (mprotect ((void *) low_address, cache_size, protection_flag) == -1)
- VTV_error ();
- }
- /* Unprotect all the vtable map vars and other side data that is used
- to keep the core hash_map data. All of these data have been put
- into relro sections */
- static void
- vtv_unprotect_vtable_vars (void)
- {
- int mprotect_flags;
- mprotect_flags = PROT_READ | PROT_WRITE;
- change_protections_on_phdr_cache (mprotect_flags);
- #if defined (__CYGWIN__) || defined (__MINGW32__)
- iterate_modules ((void *) &mprotect_flags);
- #else
- dl_iterate_phdr (dl_iterate_phdr_callback, (void *) &mprotect_flags);
- #endif
- }
- /* Protect all the vtable map vars and other side data that is used
- to keep the core hash_map data. All of these data have been put
- into relro sections */
- static void
- vtv_protect_vtable_vars (void)
- {
- int mprotect_flags;
- mprotect_flags = PROT_READ;
- #if defined (__CYGWIN__) || defined (__MINGW32__)
- iterate_modules ((void *) &mprotect_flags);
- #else
- dl_iterate_phdr (dl_iterate_phdr_callback, (void *) &mprotect_flags);
- #endif
- change_protections_on_phdr_cache (mprotect_flags);
- }
- #ifndef __GTHREAD_MUTEX_INIT
- static void
- initialize_change_permissions_mutexes ()
- {
- __GTHREAD_MUTEX_INIT_FUNCTION (&change_permissions_lock);
- }
- #endif
- /* Variables needed for getting the statistics about the hashtable set. */
- #if HASHTABLE_STATS
- _AtomicStatCounter stat_contains = 0;
- _AtomicStatCounter stat_insert = 0;
- _AtomicStatCounter stat_resize = 0;
- _AtomicStatCounter stat_create = 0;
- _AtomicStatCounter stat_probes_in_non_trivial_set = 0;
- _AtomicStatCounter stat_contains_size0 = 0;
- _AtomicStatCounter stat_contains_size1 = 0;
- _AtomicStatCounter stat_contains_size2 = 0;
- _AtomicStatCounter stat_contains_size3 = 0;
- _AtomicStatCounter stat_contains_size4 = 0;
- _AtomicStatCounter stat_contains_size5 = 0;
- _AtomicStatCounter stat_contains_size6 = 0;
- _AtomicStatCounter stat_contains_size7 = 0;
- _AtomicStatCounter stat_contains_size8 = 0;
- _AtomicStatCounter stat_contains_size9 = 0;
- _AtomicStatCounter stat_contains_size10 = 0;
- _AtomicStatCounter stat_contains_size11 = 0;
- _AtomicStatCounter stat_contains_size12 = 0;
- _AtomicStatCounter stat_contains_size13_or_more = 0;
- _AtomicStatCounter stat_contains_sizes = 0;
- _AtomicStatCounter stat_grow_from_size0_to_1 = 0;
- _AtomicStatCounter stat_grow_from_size1_to_2 = 0;
- _AtomicStatCounter stat_double_the_number_of_buckets = 0;
- _AtomicStatCounter stat_insert_found_hash_collision = 0;
- _AtomicStatCounter stat_contains_in_non_trivial_set = 0;
- _AtomicStatCounter stat_insert_key_that_was_already_present = 0;
- #endif
- /* Record statistics about the hash table sets, for debugging. */
- static void
- log_set_stats (void)
- {
- #if HASHTABLE_STATS
- if (set_log_fd == -1)
- set_log_fd = __vtv_open_log ("vtv_set_stats.log");
- __vtv_add_to_log (set_log_fd, "---\n%s\n",
- insert_only_hash_tables_stats().c_str());
- #endif
- }
- /* Change the permissions on all the pages we have allocated for the
- data sets and all the ".vtable_map_var" sections in memory (which
- contain our vtable map variables). PERM indicates whether to make
- the permissions read-only or read-write. */
- extern "C" /* This is only being applied to __VLTChangePermission*/
- void
- __VLTChangePermission (int perm)
- {
- if (debug_functions)
- {
- if (perm == __VLTP_READ_WRITE)
- fprintf (stdout, "Changing VLT permisisons to Read-Write.\n");
- else if (perm == __VLTP_READ_ONLY)
- fprintf (stdout, "Changing VLT permissions to Read-only.\n");
- else
- fprintf (stdout, "Unrecognized permissions value: %d\n", perm);
- }
- #ifndef __GTHREAD_MUTEX_INIT
- static __gthread_once_t mutex_once VTV_PROTECTED_VAR = __GTHREAD_ONCE_INIT;
- __gthread_once (&mutex_once, initialize_change_permissions_mutexes);
- #endif
- /* Ordering of these unprotect/protect calls is very important.
- You first need to unprotect all the map vars and side
- structures before you do anything with the core data
- structures (hash_maps) */
- if (perm == __VLTP_READ_WRITE)
- {
- /* TODO: Need to revisit this code for dlopen. It most probably
- is not unlocking the protected vtable vars after for load
- module that is not the first load module. */
- __gthread_mutex_lock (&change_permissions_lock);
- vtv_unprotect_vtable_vars ();
- __vtv_malloc_init ();
- __vtv_malloc_unprotect ();
- }
- else if (perm == __VLTP_READ_ONLY)
- {
- if (debug_hash)
- log_set_stats();
- __vtv_malloc_protect ();
- vtv_protect_vtable_vars ();
- __gthread_mutex_unlock (&change_permissions_lock);
- }
- }
- /* This is the memory allocator used to create the hash table that
- maps from vtable map variable name to the data set that vtable map
- variable should point to. This is part of our vtable map variable
- symbol resolution, which is necessary because the same vtable map
- variable may be created by multiple compilation units and we need a
- method to make sure that all vtable map variables for a particular
- class point to the same data set at runtime. */
- struct insert_only_hash_map_allocator
- {
- /* N is the number of bytes to allocate. */
- void *
- alloc (size_t n) const
- {
- return __vtv_malloc (n);
- }
- /* P points to the memory to be deallocated; N is the number of
- bytes to deallocate. */
- void
- dealloc (void *p, size_t) const
- {
- __vtv_free (p);
- }
- };
- /* Explicitly instantiate this class since this file is compiled with
- -fno-implicit-templates. These are for the hash table that is used
- to do vtable map variable symbol resolution. */
- template class insert_only_hash_map <vtv_set_handle *,
- insert_only_hash_map_allocator >;
- typedef insert_only_hash_map <vtv_set_handle *,
- insert_only_hash_map_allocator > s2s;
- typedef const s2s::key_type vtv_symbol_key;
- static s2s * vtv_symbol_unification_map VTV_PROTECTED_VAR = NULL;
- const unsigned long SET_HANDLE_HANDLE_BIT = 0x2;
- /* In the case where a vtable map variable is the only instance of the
- variable we have seen, it points directly to the set of valid
- vtable pointers. All subsequent instances of the 'same' vtable map
- variable point to the first vtable map variable. This function,
- given a vtable map variable PTR, checks a bit to see whether it's
- pointing directly to the data set or to the first vtable map
- variable. */
- static inline bool
- is_set_handle_handle (void * ptr)
- {
- return ((uintptr_t) ptr & SET_HANDLE_HANDLE_BIT)
- == SET_HANDLE_HANDLE_BIT;
- }
- /* Returns the actual pointer value of a vtable map variable, PTR (see
- comments for is_set_handle_handle for more details). */
- static inline vtv_set_handle *
- ptr_from_set_handle_handle (void * ptr)
- {
- return (vtv_set_handle *) ((uintptr_t) ptr & ~SET_HANDLE_HANDLE_BIT);
- }
- /* Given a vtable map variable, PTR, this function sets the bit that
- says this is the second (or later) instance of a vtable map
- variable. */
- static inline vtv_set_handle_handle
- set_handle_handle (vtv_set_handle * ptr)
- {
- return (vtv_set_handle_handle) ((uintptr_t) ptr | SET_HANDLE_HANDLE_BIT);
- }
- static inline void
- register_set_common (void **set_handle_ptr, size_t num_args,
- void **vtable_ptr_array, bool debug)
- {
- /* Now figure out what pointer to use for the set pointer, for the
- inserts. */
- vtv_set_handle *handle_ptr = (vtv_set_handle *) set_handle_ptr;
- if (debug)
- VTV_DEBUG_ASSERT (vtv_symbol_unification_map != NULL);
- if (!is_set_handle_handle (*set_handle_ptr))
- handle_ptr = (vtv_set_handle *) set_handle_ptr;
- else
- handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr);
- /* Now we've got the set and it's initialized, add the vtable
- pointers. */
- for (size_t index = 0; index < num_args; ++index)
- {
- int_vptr vtbl_ptr = (int_vptr) vtable_ptr_array[index];
- vtv_sets::insert (vtbl_ptr, handle_ptr);
- }
- }
- static inline void
- register_pair_common (void **set_handle_ptr, const void *vtable_ptr,
- const char *set_symbol_name, const char *vtable_name,
- bool debug)
- {
- /* Now we've got the set and it's initialized, add the vtable
- pointer (assuming that it's not NULL...It may be NULL, as we may
- have called this function merely to initialize the set
- pointer). */
- int_vptr vtbl_ptr = (int_vptr) vtable_ptr;
- if (vtbl_ptr)
- {
- vtv_set_handle *handle_ptr = (vtv_set_handle *) set_handle_ptr;
- if (debug)
- VTV_DEBUG_ASSERT (vtv_symbol_unification_map != NULL);
- if (!is_set_handle_handle (*set_handle_ptr))
- handle_ptr = (vtv_set_handle *) set_handle_ptr;
- else
- handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr);
- vtv_sets::insert (vtbl_ptr, handle_ptr);
- }
- if (debug && debug_init)
- {
- if (init_log_fd == -1)
- init_log_fd = __vtv_open_log("vtv_init.log");
- __vtv_add_to_log(init_log_fd,
- "Registered %s : %s (%p) 2 level deref = %s\n",
- set_symbol_name, vtable_name, vtbl_ptr,
- is_set_handle_handle(*set_handle_ptr) ? "yes" : "no" );
- }
- }
- /* This routine initializes a set handle to a vtable set. It makes
- sure that there is only one set handle for a particular set by
- using a map from set name to pointer to set handle. Since there
- will be multiple copies of the pointer to the set handle (one per
- compilation unit that uses it), it makes sure to initialize all the
- pointers to the set handle so that the set handle is unique. To
- make this a little more efficient and avoid a level of indirection
- in some cases, the first pointer to handle for a particular handle
- becomes the handle itself and the other pointers will point to the
- set handle. This is the debug version of this function, so it
- outputs extra debugging messages and logging. SET_HANDLE_PTR is
- the address of the vtable map variable, SET_SYMBOL_KEY is the hash
- table key (containing the name of the map variable and the hash
- value) and SIZE_HINT is a guess for the best initial size for the
- set of vtable pointers that SET_HANDLE_POINTER will point to. */
- static inline void
- init_set_symbol_debug (void **set_handle_ptr, const void *set_symbol_key,
- size_t size_hint)
- {
- VTV_DEBUG_ASSERT (set_handle_ptr);
- if (vtv_symbol_unification_map == NULL)
- {
- /* TODO: For now we have chosen 1024, but we need to come up with a
- better initial size for this. */
- vtv_symbol_unification_map = s2s::create (1024);
- VTV_DEBUG_ASSERT(vtv_symbol_unification_map);
- }
- vtv_set_handle *handle_ptr = (vtv_set_handle *) set_handle_ptr;
- vtv_symbol_key *symbol_key_ptr = (vtv_symbol_key *) set_symbol_key;
- const s2s::value_type * map_value_ptr =
- vtv_symbol_unification_map->get (symbol_key_ptr);
- char buffer[200];
- if (map_value_ptr == NULL)
- {
- if (*handle_ptr != NULL)
- {
- snprintf (buffer, sizeof (buffer),
- "*** Found non-NULL local set ptr %p missing for symbol"
- " %.*s",
- *handle_ptr, symbol_key_ptr->n, symbol_key_ptr->bytes);
- __vtv_log_verification_failure (buffer, true);
- VTV_DEBUG_ASSERT (0);
- }
- }
- else if (*handle_ptr != NULL &&
- (handle_ptr != *map_value_ptr &&
- ptr_from_set_handle_handle (*handle_ptr) != *map_value_ptr))
- {
- VTV_DEBUG_ASSERT (*map_value_ptr != NULL);
- snprintf (buffer, sizeof(buffer),
- "*** Found diffence between local set ptr %p and set ptr %p"
- "for symbol %.*s",
- *handle_ptr, *map_value_ptr,
- symbol_key_ptr->n, symbol_key_ptr->bytes);
- __vtv_log_verification_failure (buffer, true);
- VTV_DEBUG_ASSERT (0);
- }
- else if (*handle_ptr == NULL)
- {
- /* Execution should not reach this point. */
- }
- if (*handle_ptr != NULL)
- {
- if (!is_set_handle_handle (*set_handle_ptr))
- handle_ptr = (vtv_set_handle *) set_handle_ptr;
- else
- handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr);
- vtv_sets::resize (size_hint, handle_ptr);
- return;
- }
- VTV_DEBUG_ASSERT (*handle_ptr == NULL);
- if (map_value_ptr != NULL)
- {
- if (*map_value_ptr == handle_ptr)
- vtv_sets::resize (size_hint, *map_value_ptr);
- else
- {
- /* The one level handle to the set already exists. So, we
- are adding one level of indirection here and we will
- store a pointer to the one level handle here. */
- vtv_set_handle_handle * handle_handle_ptr =
- (vtv_set_handle_handle *)handle_ptr;
- *handle_handle_ptr = set_handle_handle(*map_value_ptr);
- VTV_DEBUG_ASSERT(*handle_handle_ptr != NULL);
- /* The handle can itself be NULL if the set has only
- been initiazlied with size hint == 1. */
- vtv_sets::resize (size_hint, *map_value_ptr);
- }
- }
- else
- {
- /* We will create a new set. So, in this case handle_ptr is the
- one level pointer to the set handle. Create copy of map name
- in case the memory where this comes from gets unmapped by
- dlclose. */
- size_t map_key_len = symbol_key_ptr->n + sizeof (vtv_symbol_key);
- void *map_key = __vtv_malloc (map_key_len);
- memcpy (map_key, symbol_key_ptr, map_key_len);
- s2s::value_type *value_ptr;
- vtv_symbol_unification_map =
- vtv_symbol_unification_map->find_or_add_key ((vtv_symbol_key *)map_key,
- &value_ptr);
- *value_ptr = handle_ptr;
- /* TODO: We should verify the return value. */
- vtv_sets::create (size_hint, handle_ptr);
- VTV_DEBUG_ASSERT (size_hint <= 1 || *handle_ptr != NULL);
- }
- if (debug_init)
- {
- if (init_log_fd == -1)
- init_log_fd = __vtv_open_log ("vtv_init.log");
- __vtv_add_to_log (init_log_fd,
- "Init handle:%p for symbol:%.*s hash:%u size_hint:%lu"
- "number of symbols:%lu \n",
- set_handle_ptr, symbol_key_ptr->n,
- symbol_key_ptr->bytes, symbol_key_ptr->hash, size_hint,
- vtv_symbol_unification_map->size ());
- }
- }
- /* This routine initializes a set handle to a vtable set. It makes
- sure that there is only one set handle for a particular set by
- using a map from set name to pointer to set handle. Since there
- will be multiple copies of the pointer to the set handle (one per
- compilation unit that uses it), it makes sure to initialize all the
- pointers to the set handle so that the set handle is unique. To
- make this a little more efficient and avoid a level of indirection
- in some cases, the first pointer to handle for a particular handle
- becomes the handle itself and the other pointers will point to the
- set handle. This is the debug version of this function, so it
- outputs extra debugging messages and logging. SET_HANDLE_PTR is
- the address of the vtable map variable, SET_SYMBOL_KEY is the hash
- table key (containing the name of the map variable and the hash
- value) and SIZE_HINT is a guess for the best initial size for the
- set of vtable pointers that SET_HANDLE_POINTER will point to. */
- void
- __VLTRegisterSetDebug (void **set_handle_ptr, const void *set_symbol_key,
- size_t size_hint, size_t num_args,
- void **vtable_ptr_array)
- {
- unsigned long long start = get_cycle_count ();
- increment_num_calls (&num_calls_to_regset);
- VTV_DEBUG_ASSERT(set_handle_ptr != NULL);
- init_set_symbol_debug (set_handle_ptr, set_symbol_key, size_hint);
- register_set_common (set_handle_ptr, num_args, vtable_ptr_array, true);
- accumulate_cycle_count (®set_cycles, start);
- }
- /* This function takes a the address of a vtable map variable
- (SET_HANDLE_PTR), a VTABLE_PTR to add to the data set, the name of
- the vtable map variable (SET_SYMBOL_NAME) and the name of the
- vtable (VTABLE_NAME) being pointed to. If the vtable map variable
- is NULL it creates a new data set and initializes the variable,
- otherwise it uses our symbol unification to find the right data
- set; in either case it then adds the vtable pointer to the set.
- The other two parameters are used for debugging information. */
- void
- __VLTRegisterPairDebug (void **set_handle_ptr, const void *set_symbol_key,
- size_t size_hint, const void *vtable_ptr,
- const char *set_symbol_name, const char *vtable_name)
- {
- unsigned long long start = get_cycle_count ();
- increment_num_calls (&num_calls_to_regpair);
- VTV_DEBUG_ASSERT(set_handle_ptr != NULL);
- init_set_symbol_debug (set_handle_ptr, set_symbol_key, size_hint);
- register_pair_common (set_handle_ptr, vtable_ptr, set_symbol_name, vtable_name,
- true);
- accumulate_cycle_count (®pair_cycles, start);
- }
- /* This is the debug version of the verification function. It takes
- the address of a vtable map variable (SET_HANDLE_PTR) and a
- VTABLE_PTR to validate, as well as the name of the vtable map
- variable (SET_SYMBOL_NAME) and VTABLE_NAME, which are used for
- debugging messages. It checks to see if VTABLE_PTR is in the set
- pointed to by SET_HANDLE_PTR. If so, it returns VTABLE_PTR,
- otherwise it calls __vtv_verify_fail, which usually logs error
- messages and calls abort. */
- const void *
- __VLTVerifyVtablePointerDebug (void **set_handle_ptr, const void *vtable_ptr,
- const char *set_symbol_name,
- const char *vtable_name)
- {
- unsigned long long start = get_cycle_count ();
- VTV_DEBUG_ASSERT (set_handle_ptr != NULL && *set_handle_ptr != NULL);
- int_vptr vtbl_ptr = (int_vptr) vtable_ptr;
- increment_num_calls (&num_calls_to_verify_vtable);
- vtv_set_handle *handle_ptr;
- if (!is_set_handle_handle (*set_handle_ptr))
- handle_ptr = (vtv_set_handle *) set_handle_ptr;
- else
- handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr);
- if (vtv_sets::contains (vtbl_ptr, handle_ptr))
- {
- if (debug_verify_vtable)
- {
- if (verify_vtable_log_fd == -1)
- __vtv_open_log ("vtv_verify_vtable.log");
- __vtv_add_to_log (verify_vtable_log_fd,
- "Verified %s %s value = %p\n",
- set_symbol_name, vtable_name, vtable_ptr);
- }
- }
- else
- {
- /* We failed to find the vtable pointer in the set of valid
- pointers. Log the error data and call the failure
- function. */
- snprintf (debug_log_message, sizeof (debug_log_message),
- "Looking for %s in %s\n", vtable_name, set_symbol_name);
- __vtv_verify_fail_debug (set_handle_ptr, vtable_ptr, debug_log_message);
- /* Normally __vtv_verify_fail_debug will call abort, so we won't
- execute the return below. If we get this far, the assumption
- is that the programmer has replaced __vtv_verify_fail_debug
- with some kind of secondary verification AND this secondary
- verification succeeded, so the vtable pointer is valid. */
- }
- accumulate_cycle_count (&verify_vtable_cycles, start);
- return vtable_ptr;
- }
- /* This routine initializes a set handle to a vtable set. It makes
- sure that there is only one set handle for a particular set by
- using a map from set name to pointer to set handle. Since there
- will be multiple copies of the pointer to the set handle (one per
- compilation unit that uses it), it makes sure to initialize all the
- pointers to the set handle so that the set handle is unique. To
- make this a little more efficient and avoid a level of indirection
- in some cases, the first pointer to handle for a particular handle
- becomes the handle itself and the other pointers will point to the
- set handle. SET_HANDLE_PTR is the address of the vtable map
- variable, SET_SYMBOL_KEY is the hash table key (containing the name
- of the map variable and the hash value) and SIZE_HINT is a guess
- for the best initial size for the set of vtable pointers that
- SET_HANDLE_POINTER will point to.*/
- static inline void
- init_set_symbol (void **set_handle_ptr, const void *set_symbol_key,
- size_t size_hint)
- {
- vtv_set_handle *handle_ptr = (vtv_set_handle *) set_handle_ptr;
- if (*handle_ptr != NULL)
- {
- if (!is_set_handle_handle (*set_handle_ptr))
- handle_ptr = (vtv_set_handle *) set_handle_ptr;
- else
- handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr);
- vtv_sets::resize (size_hint, handle_ptr);
- return;
- }
- if (vtv_symbol_unification_map == NULL)
- vtv_symbol_unification_map = s2s::create (1024);
- vtv_symbol_key *symbol_key_ptr = (vtv_symbol_key *) set_symbol_key;
- const s2s::value_type *map_value_ptr =
- vtv_symbol_unification_map->get (symbol_key_ptr);
- if (map_value_ptr != NULL)
- {
- if (*map_value_ptr == handle_ptr)
- vtv_sets::resize (size_hint, *map_value_ptr);
- else
- {
- /* The one level handle to the set already exists. So, we
- are adding one level of indirection here and we will
- store a pointer to the one level pointer here. */
- vtv_set_handle_handle *handle_handle_ptr =
- (vtv_set_handle_handle *) handle_ptr;
- *handle_handle_ptr = set_handle_handle (*map_value_ptr);
- vtv_sets::resize (size_hint, *map_value_ptr);
- }
- }
- else
- {
- /* We will create a new set. So, in this case handle_ptr is the
- one level pointer to the set handle. Create copy of map name
- in case the memory where this comes from gets unmapped by
- dlclose. */
- size_t map_key_len = symbol_key_ptr->n + sizeof (vtv_symbol_key);
- void * map_key = __vtv_malloc (map_key_len);
- memcpy (map_key, symbol_key_ptr, map_key_len);
- s2s::value_type * value_ptr;
- vtv_symbol_unification_map =
- vtv_symbol_unification_map->find_or_add_key ((vtv_symbol_key *)map_key,
- &value_ptr);
- *value_ptr = handle_ptr;
- /* TODO: We should verify the return value. */
- vtv_sets::create (size_hint, handle_ptr);
- }
- }
- /* This routine initializes a set handle to a vtable set. It makes
- sure that there is only one set handle for a particular set by
- using a map from set name to pointer to set handle. Since there
- will be multiple copies of the pointer to the set handle (one per
- compilation unit that uses it), it makes sure to initialize all the
- pointers to the set handle so that the set handle is unique. To
- make this a little more efficient and avoid a level of indirection
- in some cases, the first pointer to handle for a particular handle
- becomes the handle itself and the other pointers will point to the
- set handle. SET_HANDLE_PTR is the address of the vtable map
- variable, SET_SYMBOL_KEY is the hash table key (containing the name
- of the map variable and the hash value) and SIZE_HINT is a guess
- for the best initial size for the set of vtable pointers that
- SET_HANDLE_POINTER will point to.*/
- void
- __VLTRegisterSet (void **set_handle_ptr, const void *set_symbol_key,
- size_t size_hint, size_t num_args, void **vtable_ptr_array)
- {
- unsigned long long start = get_cycle_count ();
- increment_num_calls (&num_calls_to_regset);
- init_set_symbol (set_handle_ptr, set_symbol_key, size_hint);
- register_set_common (set_handle_ptr, num_args, vtable_ptr_array, false);
- accumulate_cycle_count (®set_cycles, start);
- }
- /* This function takes a the address of a vtable map variable
- (SET_HANDLE_PTR) and a VTABLE_PTR. If the vtable map variable is
- NULL it creates a new data set and initializes the variable,
- otherwise it uses our symbol unification to find the right data
- set; in either case it then adds the vtable pointer to the set. */
- void
- __VLTRegisterPair (void **set_handle_ptr, const void *set_symbol_key,
- size_t size_hint, const void *vtable_ptr)
- {
- unsigned long long start = get_cycle_count ();
- increment_num_calls (&num_calls_to_regpair);
- init_set_symbol (set_handle_ptr, set_symbol_key, size_hint);
- register_pair_common (set_handle_ptr, vtable_ptr, NULL, NULL, false);
- accumulate_cycle_count (®pair_cycles, start);
- }
- /* This is the main verification function. It takes the address of a
- vtable map variable (SET_HANDLE_PTR) and a VTABLE_PTR to validate.
- It checks to see if VTABLE_PTR is in the set pointed to by
- SET_HANDLE_PTR. If so, it returns VTABLE_PTR, otherwise it calls
- __vtv_verify_fail, which usually logs error messages and calls
- abort. Since this function gets called VERY frequently, it is
- important for it to be as efficient as possible. */
- const void *
- __VLTVerifyVtablePointer (void ** set_handle_ptr, const void * vtable_ptr)
- {
- unsigned long long start = get_cycle_count ();
- int_vptr vtbl_ptr = (int_vptr) vtable_ptr;
- vtv_set_handle *handle_ptr;
- increment_num_calls (&num_calls_to_verify_vtable);
- if (!is_set_handle_handle (*set_handle_ptr))
- handle_ptr = (vtv_set_handle *) set_handle_ptr;
- else
- handle_ptr = ptr_from_set_handle_handle (*set_handle_ptr);
- if (!vtv_sets::contains (vtbl_ptr, handle_ptr))
- {
- __vtv_verify_fail ((void **) handle_ptr, vtable_ptr);
- /* Normally __vtv_verify_fail will call abort, so we won't
- execute the return below. If we get this far, the assumption
- is that the programmer has replaced __vtv_verify_fail with
- some kind of secondary verification AND this secondary
- verification succeeded, so the vtable pointer is valid. */
- }
- accumulate_cycle_count (&verify_vtable_cycles, start);
- return vtable_ptr;
- }
- static int page_count_2 = 0;
- #if !defined (__CYGWIN__) && !defined (__MINGW32__)
- static int
- dl_iterate_phdr_count_pages (struct dl_phdr_info *info,
- size_t unused __attribute__ ((__unused__)),
- void *data)
- {
- int *mprotect_flags = (int *) data;
- off_t map_sect_offset = 0;
- ElfW (Word) map_sect_len = 0;
- const char *map_sect_name = VTV_PROTECTED_VARS_SECTION;
- /* Check to see if this is the record for the Linux Virtual Dynamic
- Shared Object (linux-vdso.so.1), which exists only in memory (and
- therefore cannot be read from disk). */
- if (strcmp (info->dlpi_name, "linux-vdso.so.1") == 0)
- return 0;
- if (strlen (info->dlpi_name) == 0
- && info->dlpi_addr != 0)
- return 0;
- read_section_offset_and_length (info, map_sect_name, *mprotect_flags,
- &map_sect_offset, &map_sect_len);
- /* See if we actually found the section. */
- if (map_sect_len)
- page_count_2 += (map_sect_len + VTV_PAGE_SIZE - 1) / VTV_PAGE_SIZE;
- return 0;
- }
- #endif
- static void
- count_all_pages (void)
- {
- int mprotect_flags;
- mprotect_flags = PROT_READ;
- page_count_2 = 0;
- #if defined (__CYGWIN__) || defined (__MINGW32__)
- iterate_modules ((void *) &mprotect_flags);
- #else
- dl_iterate_phdr (dl_iterate_phdr_count_pages, (void *) &mprotect_flags);
- #endif
- page_count_2 += __vtv_count_mmapped_pages ();
- }
- void
- __VLTDumpStats (void)
- {
- int log_fd = __vtv_open_log ("vtv-runtime-stats.log");
- if (log_fd != -1)
- {
- count_all_pages ();
- __vtv_add_to_log (log_fd,
- "Calls: mprotect (%d) regset (%d) regpair (%d)"
- " verify_vtable (%d)\n",
- num_calls_to_mprotect, num_calls_to_regset,
- num_calls_to_regpair, num_calls_to_verify_vtable);
- __vtv_add_to_log (log_fd,
- "Cycles: mprotect (%lld) regset (%lld) "
- "regpair (%lld) verify_vtable (%lld)\n",
- mprotect_cycles, regset_cycles, regpair_cycles,
- verify_vtable_cycles);
- __vtv_add_to_log (log_fd,
- "Pages protected (1): %d\n", num_pages_protected);
- __vtv_add_to_log (log_fd, "Pages protected (2): %d\n", page_count_2);
- close (log_fd);
- }
- }
- /* This function is called from __VLTVerifyVtablePointerDebug; it
- sends as much debugging information as it can to the error log
- file, then calls __vtv_verify_fail. SET_HANDLE_PTR is the pointer
- to the set of valid vtable pointers, VTBL_PTR is the pointer that
- was not found in the set, and DEBUG_MSG is the message to be
- written to the log file before failing. n */
- void
- __vtv_verify_fail_debug (void **set_handle_ptr, const void *vtbl_ptr,
- const char *debug_msg)
- {
- __vtv_log_verification_failure (debug_msg, false);
- /* Call the public interface in case it has been overwritten by
- user. */
- __vtv_verify_fail (set_handle_ptr, vtbl_ptr);
- __vtv_log_verification_failure ("Returned from __vtv_verify_fail."
- " Secondary verification succeeded.\n", false);
- }
- /* This function calls __fortify_fail with a FAILURE_MSG and then
- calls abort. */
- void
- __vtv_really_fail (const char *failure_msg)
- {
- __fortify_fail (failure_msg);
- /* We should never get this far; __fortify_fail calls __libc_message
- which prints out a back trace and a memory dump and then is
- supposed to call abort, but let's play it safe anyway and call abort
- ourselves. */
- abort ();
- }
- /* This function takes an error MSG, a vtable map variable
- (DATA_SET_PTR) and a vtable pointer (VTBL_PTR). It is called when
- an attempt to verify VTBL_PTR with the set pointed to by
- DATA_SET_PTR failed. It outputs a failure message with the
- addresses involved, and calls __vtv_really_fail. */
- static void
- vtv_fail (const char *msg, void **data_set_ptr, const void *vtbl_ptr)
- {
- char buffer[128];
- int buf_len;
- const char *format_str =
- "*** Unable to verify vtable pointer (%p) in set (%p) *** \n";
- snprintf (buffer, sizeof (buffer), format_str, vtbl_ptr,
- is_set_handle_handle(*data_set_ptr) ?
- ptr_from_set_handle_handle (*data_set_ptr) :
- *data_set_ptr);
- buf_len = strlen (buffer);
- /* Send this to to stderr. */
- write (2, buffer, buf_len);
- #ifndef VTV_NO_ABORT
- __vtv_really_fail (msg);
- #endif
- }
- /* Send information about what we were trying to do when verification
- failed to the error log, then call vtv_fail. This function can be
- overwritten/replaced by the user, to implement a secondary
- verification function instead. DATA_SET_PTR is the vtable map
- variable used for the failed verification, and VTBL_PTR is the
- vtable pointer that was not found in the set. */
- void
- __vtv_verify_fail (void **data_set_ptr, const void *vtbl_ptr)
- {
- char log_msg[256];
- snprintf (log_msg, sizeof (log_msg), "Looking for vtable %p in set %p.\n",
- vtbl_ptr,
- is_set_handle_handle (*data_set_ptr) ?
- ptr_from_set_handle_handle (*data_set_ptr) :
- *data_set_ptr);
- __vtv_log_verification_failure (log_msg, false);
- const char *format_str =
- "*** Unable to verify vtable pointer (%p) in set (%p) *** \n";
- snprintf (log_msg, sizeof (log_msg), format_str, vtbl_ptr, *data_set_ptr);
- __vtv_log_verification_failure (log_msg, false);
- __vtv_log_verification_failure (" Backtrace: \n", true);
- const char *fail_msg = "Potential vtable pointer corruption detected!!\n";
- vtv_fail (fail_msg, data_set_ptr, vtbl_ptr);
- }
|