Commits

Anonymous committed f9a8646

adapted PageAccessControl iface to SG needs. Keyed entries by page boundary address

  • Participants
  • Parent commits a3a028e
  • Tags CxxUtils-00-00-17

Comments (0)

Files changed (7)

File CxxUtils/PageAccessControl.h

  *       /proc/self/maps
  * NOTE that protectPage will leak memory when asked to write-protect a page
  * 
- * $Id: PageAccessControl.h,v 1.2 2009-02-04 22:21:44 calaf Exp $
+ * $Id: PageAccessControl.h,v 1.3 2009-02-12 23:04:38 calaf Exp $
  */
 
 #include <vector>
 #include <sys/mman.h> /*PROT_NONE*/
 
-class procmaps;
+#include "CxxUtils/procmaps.h"
 
 class PageAccessControl {
 public:
-  ///the list of accessed pointers
+  PageAccessControl(size_t reservedSize=65535) :
+    m_pmaps()
+  {
+    //we must reserve enough elements, or we risk vector allocating in a protected page during handle()...
+    m_protected.reserve(reservedSize);
+  }
   PageAccessControl(procmaps& pmaps, size_t reservedSize=65535) :
     m_pmaps(pmaps)
   {
   /// write-locked
   template <typename T>
   bool protectPage(T* addr, int prot) {
-    return protectPage_i(addr, sizeof(T), prot);
+    return protectPage(addr, sizeof(T), prot);
   }
   
   ///forbid access to the page containing addr, setting its prot to PROT_NONE
   template <typename T>
-  bool forbidPage(T* addr) {
-    return protectPage_i(addr, sizeof(T), PROT_NONE);
+  bool forbidPage(const T* addr) {
+    return forbidPage(addr, sizeof(T));
+  }
+
+  ///forbid access to the page containing addr, setting its prot to PROT_NONE
+  bool forbidPage(const void* addr, size_t objSize) {
+    return protectPage(addr, objSize, PROT_NONE);
   }
     
-  bool restorePageProt(void * addr);
+  ///FIXME this will not work well for objects spanning across pages
+  bool forbidPage(const void* addr) {
+    return protectPage(addr, 4, PROT_NONE);
+  }
+    
+  ///void* version of protectPage. Used to implement all others 
+  bool protectPage(const void* addr, size_t objSize, int prot);
+  
+  bool restorePageProt(const void * addr);
   void sort();
 
   /// @class Entry describes the protection of a memory region (see mprotect(2))
   struct Entry {
-    Entry(void* a, size_t l, int p): addr(a), lenProt(l), prot(p) {}
-    /// address for which page protection was requested. Used as key
+    Entry(void* a, size_t l, int p, void* pl);
+    /// address of page for which protection was requested. Used as key
     void* addr;
-    ///lenght of the protected region, from page boundary to end of obj @addr
+    ///lenght of the protected region, from page addr to end of protected obj
     size_t lenProt; 
     int prot;
+    ///pointer to the heap fragment we leaked before protecting the page
+    void* leak;
   };
 
 private:
-  bool protectPage_i(void* addr, size_t objSize, int prot);
 
   /// the content of /proc/self/maps
-  procmaps& m_pmaps;
+  procmaps m_pmaps;
 
   typedef std::vector<Entry> protected_t;
   // can not preallocate map

File CxxUtils/page_access.h

 #include <sys/mman.h> /* mprotect */
 #define DEBUG 1
 namespace athena {
+  const unsigned int PAGESIZE= 0x1000;  //sysconf(_SC_PAGE_SIZE)
+
   void* page_address(void* addr);
   void* next_page_address(void* addr);
   int page_protect(void* addr, int prot);

File src/PageAccessControl.cxx

 #include "CxxUtils/page_access.h"  
 using athena::page_address;
 using athena::next_page_address;
-#include "CxxUtils/procmaps.h"
+
+PageAccessControl::Entry::Entry(void* a, size_t l, int p, void* pl): 
+  addr(page_address(a)), lenProt(l), prot(p), leak(pl) {}
+
 
 void
 PageAccessControl::sort() {
   }
 }
 bool
-PageAccessControl::restorePageProt(void * addr) {
+PageAccessControl::restorePageProt(const void * caddr) {
+  void* addr(const_cast<void*>(caddr));
   int rc(-1);
   sort();
+  Entry ea(addr,0,0,0);
   protected_t::iterator entry=
-    std::lower_bound(m_protected.begin(), m_protected.end(), Entry(addr,0,0));
-  if (entry->addr == addr) {
+    std::lower_bound(m_protected.begin(), m_protected.end(), ea);
+  if (entry != m_protected.end() &&
+      entry->addr == ea.addr) {
     //found it. Restore page prot
     rc=mprotect( page_address(entry->addr), entry->lenProt, entry->prot);
+    if (rc==0) {
+#ifdef DEBUG
+      printf("PageAccessControl::restorePageProt DEBUG: restored protection %i for page %p containing address %p \n",
+	     entry->prot,
+	     page_address(entry->addr), 
+	     entry->addr);
+      printf(" FIXME NOT Freeing memory at %p \n", entry->leak );
+#endif
+      //      free(entry->leak);
+      entry->leak=0;
+    }
   } else printf("WARNING no entry in procmap for addr=%p, page protection not restored \n",addr);
   return (rc == 0);
 }
 
 bool
-PageAccessControl::protectPage_i(void* addr, size_t objSize, int prot) {
+PageAccessControl::protectPage(const void* caddr, size_t objSize, int prot) {
+  void* addr(const_cast<void*>(caddr));
   int  rc(-1);
   const procmaps::Entry *e=m_pmaps.getEntry(addr,false);
   //this is the lenght of the range we are going to protect
   if (0 != e) {
-    unsigned int lenProt = (unsigned int)addr - (unsigned int)(page_address(addr)) + objSize;
-    unsigned int nextUnprot = (unsigned int)(next_page_address((void*)((unsigned int)page_address(addr) + lenProt -1)));
-    unsigned int nextProt = (unsigned int)page_address(addr) + lenProt;
+    void *pageAddr = page_address(addr);
+    size_t lenProt = (size_t)addr - (size_t)(pageAddr) + objSize;
+    size_t nextProt = (size_t)addr + objSize;
+    size_t nextUnprot = (size_t)(next_page_address((void*)(nextProt-1)));
     int pageProt(PROT_NONE);
     if (e->readable) pageProt |= PROT_READ;
     if (e->writable) pageProt |= PROT_WRITE;
     if (pageProt != prot) {
       //fill up the space from nextProt to nextUnprot to avoid allocations
       //in the locked pages, and SEGVs...
+      void *leak(0);
       if (0 == (prot & PROT_WRITE)) {
-	void *leak=malloc(nextUnprot-nextProt);
+	size_t lenLeak(nextUnprot-nextProt-1);
+	leak=malloc(lenLeak);
+	if ((size_t)leak<nextUnprot && (size_t)leak + lenLeak>=nextUnprot) {
+	  //we do not want to allocate our buffer memory past the current
+	  //page, so trim it down
+	  free(leak);
+	  lenLeak=nextUnprot - (size_t)leak -1;
+	  leak=malloc(lenLeak);
+	} 
+	if ((size_t)leak >= nextUnprot) {
+	  //leak has been allocated into next page
+	  //better get rid of it as it will likely
+	  //be locked by another protectPage
+	  free(leak);
+	  leak=0; 
+	} else {
 #ifdef DEBUG
-	printf("PageAccessControl::protectPage_i DEBUG: fill up space from %p to 0x%x to avoid allocations in locked pages\n",
-	       leak, nextUnprot-1);
+	  printf("PageAccessControl::protectPage DEBUG: fill up space from %p to 0x%x to avoid allocations in locked pages\n",
+		 leak, (int)leak+lenLeak);
 #endif
+	}
       }
-      if (0 == (rc = mprotect( page_address(addr),
+      
+      if (0 == (rc = mprotect( pageAddr,
 			       lenProt,
 			       prot))) {
-	m_protected.push_back(Entry(addr,lenProt, pageProt));
+	m_protected.push_back(Entry(pageAddr,lenProt, pageProt, leak));
 	m_protectedIsSorted=false; //FIXME we should use a mapvector
 #ifdef DEBUG
-	printf("PageAccessControl::protectPage_i DEBUG: set protection %i for page range %p - 0x%x containing address range=%p - 0x%x\n",
+	printf("PageAccessControl::protectPage DEBUG: set protection %i for page range %p - 0x%x containing address range=%p - 0x%x\n",
 	       prot,
-	       page_address(addr), 
+	       pageAddr, 
 	       nextUnprot - 1, 
 	       addr,
 	       nextProt -1 );
 #endif
       }
     } else rc=0;
-  } else printf("PageAccessControl::protectPage_i WARNING: no entry in procmap for addr=%p, page protection not restored \n",addr);
+  } else printf("PageAccessControl::protectPage WARNING: no entry in procmap for addr=%p, page protection not restored \n",addr);
   return (rc == 0);
 }

File src/PtrAccessSEGVHandler.cxx

 #endif
     //record the access and restore page protection
     if (SEGV_ACCERR == sigi->si_code) {
+      m_pac.restorePageProt(addr);
       m_accessed.push_back(addr);
-      m_pac.restorePageProt(addr);
     } else abort();
 }

File src/page_access.cxx

 #include "CxxUtils/page_access.h"
 namespace athena{
 
-  const unsigned int PAGESIZE= 0x1000;  //sysconf(_SC_PAGE_SIZE)
-
   void* page_address(void* addr) { 
     return (void*)((long)addr & ~(PAGESIZE-1)); 
   }

File test/PageAccessControl_test.cxx

   PageAccessControl pac(pmaps);
   //protect a heap object
   int* pi= new int(2);
+  void* pv = malloc(10);
   assert(pac.forbidPage(pi));
-  //  cout << *pi << endl;
+  //FIXME assert(pac.forbidPage(pv, 10));
+  //assert(pac.protectPage(pv, 10, PROT_READ));
   assert(pac.restorePageProt(pi));
+  //assert(pac.restorePageProt(pv));
   cout << "accessing restored pointer " << *pi << endl;
-  
+
   
 
 //make valgrind happy
   delete pi;
+  free(pv);
   boost::singleton_pool<boost::pool_allocator_tag, sizeof(procmaps::Entry)>::release_memory();
 
   cout << "*** PageAccessControl_test OK ***" <<endl;

File test/SEGVHandler_test.cxx

   //  p.loadMaps(true); //dump new memory map
   new int(12);
   //let's get some SEGVs
-  printf("try to read 11\n");
+  printf("try to read 12\n");
   printf("accessing pInt %d\n",*pInt);
   printf("reading again from pInt %d\n",*pInt);
   printf("try to write 33\n");