Commits

Anonymous committed 4041f57

added the access counting SEGV handler, its C interface, and its test

Comments (0)

Files changed (8)

-2009-01-23  Paolo Calafiura
+2009-01-29  Paolo Calafiura
+	* Tagging CxxUtils-00-00-15
+        * added PtrAccessSIGVHandler and its test. This class provides a handle function that logs the  
+	addresses that had an access violation. It can be installed as sigaction handler using its
+        C facade in cPtrAccessSIGVHandler
+
+2009-01-26  Paolo Calafiura
 	* Tagging CxxUtils-00-00-14
         * turned procmaps into a "pseudo-singleton" so that maps can be read only once. At the same time
 	added support for rereading maps when an address can not be located in loaded entries.

CxxUtils/PtrAccessSEGVHandler.h

+// This file's extension implies that it's C, but it's really -*- C++ -*-.
+#ifndef CXXUTILS_PTRACCESSSEGVHANDLER_H
+#define CXXUTILS_PTRACCESSSEGVHANDLER_H 1
+
+/**
+ * @file CxxUtils/PtrAccessSEGVHandler.h
+ * @author Paolo Calafiura
+ * @date Jan 2009
+ * @brief This class provides a handle function that logs the  
+ *       addresses that had an access violation. It can be installed as sigaction handler using its
+ *       C facade in cPtrAccessSIGVHandler.h. The purpose (at least the original one) is to protect
+ *       all pages containing heap objects managed by StoreGateSvc, install the handler, and then
+ *       see which objects have actually been accessed. 
+ *
+ * $Id: PtrAccessSEGVHandler.h,v 1.1 2009-01-30 00:50:51 calaf Exp $
+ */
+
+#include <vector>
+#include <signal.h>   /* siginfo_t */
+
+class procmaps;
+
+class PtrAccessSEGVHandler {
+public:
+  ///the list of accessed pointers
+  typedef std::vector<void*> accessed_t;
+  typedef accessed_t::const_iterator const_iterator;
+  const accessed_t& accessedPtrs() const { return m_accessed; }
+  const_iterator beginAccessedPtrs() const { return m_accessed.begin(); }
+  const_iterator endAccessedPtrs() const { return m_accessed.end(); }
+  void reset() { m_accessed.clear(); }
+  
+  PtrAccessSEGVHandler(procmaps& pmaps, size_t reservedSize=65535) :
+    m_pmaps(pmaps) 
+  {
+    m_accessed.reserve(reservedSize);
+  }
+
+  ///the actual signal handler
+  void handle(int signal_number,siginfo_t *sigi,void *unused);
+
+private:
+  int restorePageProt(void * addr);
+  procmaps& m_pmaps;
+  accessed_t m_accessed;
+};
+#endif

CxxUtils/cPtrAccessSEGVHandler.h

+// This file's extension implies that it's C, but it's really -*- C++ -*-.
+#ifndef CXXUTILS_CPTRACCESSSEGVHANDLER_H
+#define CXXUTILS_CPTRACCESSSEGVHANDLER_H 1
+
+/**
+ * @file CxxUtils/cPtrAccessSEGVHandler.h
+ * @author Paolo Calafiura
+ * @date Jan 2009
+ * @brief a C wrapper providing access to PtrAccessSEGVHandler::handle the way sigaction wants it
+ *    Example:
+ *  PtrAccessSEGVHandler h(p);
+ *  setPtrAccessSEGVHandler(h);
+ *  struct sigaction sa;
+ *  sa.sa_sigaction= cPtrAccessSEGVHandler; 
+ *  sigaction(SIGSEGV,&sa, NULL);
+ *
+ * $Id: cPtrAccessSEGVHandler.h,v 1.1 2009-01-30 00:50:51 calaf Exp $
+ */
+
+#include <signal.h>   /*siginfo_t*/
+class PtrAccessSEGVHandler;
+
+void setPtrAccessSEGVHandler(PtrAccessSEGVHandler* h);
+void cPtrAccessSEGVHandler(int signal, siginfo_t* si, void* old);
+#endif

CxxUtils/page_address.h

+#ifndef CXXUTILS_PAGEADDRESS_H
+#define CXXUTILS_PAGEADDRESS_H 1
+const long PAGESIZE= 0x1000;  //sysconf(_SC_PAGE_SIZE)
+inline void* page_address(void* addr) { 
+  return (void*)((long)addr & ~(PAGESIZE-1)); 
+}
+#endif
 ## unit tests
 
 use TestTools	   TestTools-*         AtlasTest
+apply_pattern UnitTest_run unit_test=SEGVHandler
 apply_pattern UnitTest_run unit_test=hashtable
 apply_pattern UnitTest_run unit_test=fpcompare
 apply_pattern UnitTest_run unit_test=procmaps

src/PtrAccessSEGVHandler.cxx

+#include "CxxUtils/PtrAccessSEGVHandler.h"
+#include <sys/mman.h> /* mprotect */
+#include "CxxUtils/page_address.h"
+#include "CxxUtils/procmaps.h"
+
+#define DEBUG 1
+
+int
+PtrAccessSEGVHandler::restorePageProt(void * addr) {
+    int rc(-1);
+    const procmaps::Entry *e=m_pmaps.getEntry(addr);
+    if (0 != e) {
+      long pageProt(0);
+      if (e->readable) pageProt |= PROT_READ;
+      if (e->writable) pageProt |= PROT_WRITE;
+      if (e->executable) pageProt |= PROT_EXEC;
+      //restore READ/WRITE access to page, FIXME what about PROT_EXEC?
+      //we should really restore original protection
+      rc=mprotect(page_address(addr), PAGESIZE, pageProt);
+      if (0 != rc) printf("WARNING restoring page protection failed for addr=%x\n",addr);
+#ifdef DEBUG
+      else printf("DEBUG restored page protection @%x for page @%x containing addr=%x\n",pageProt,page_address(addr),addr);
+#endif
+    } else printf("WARNING no entry in procmap for addr=%x, page protection not restored \n",addr);
+    return rc;
+  }
+
+void
+PtrAccessSEGVHandler::handle(int /*signal_number*/,siginfo_t *sigi,void * /*unused*/) {
+    void *addr=sigi->si_addr;
+#ifdef DEBUG
+    printf("page fault @address=%x\n",sigi->si_addr);
+#ifdef __ARCH_SI_TRAPNO
+    printf("page fault trapno=%d\n",sigi->si_trapno);
+#endif
+    printf("page fault signo=%d\n",sigi->si_signo);
+    printf("page fault errno=%d\n",sigi->si_errno);
+    printf("this page fault failed because ");
+    if (SEGV_MAPERR == sigi->si_code) 
+      printf("you tried to access an invalid address\n");
+    else if (SEGV_ACCERR == sigi->si_code) {
+      printf("you tried to access a protected address\n");
+    }  else printf(" an unknown reason. Page fault code=%d\n",sigi->si_code);
+#endif
+    //record the access and restore page protection
+    if (SEGV_ACCERR == sigi->si_code) {
+      m_accessed.push_back(addr);
+      restorePageProt(addr);
+    } else abort();
+  }

src/cPtrAccessSEGVHandler.cxx

+#include "CxxUtils/cPtrAccessSEGVHandler.h"
+#include "CxxUtils/PtrAccessSEGVHandler.h"
+namespace {
+  PtrAccessSEGVHandler* s_pHandler(0);
+}
+
+void setPtrAccessSEGVHandler(PtrAccessSEGVHandler* h) { s_pHandler=h; }
+void cPtrAccessSEGVHandler(int signal, siginfo_t* si, void* old) {
+  s_pHandler->handle(signal, si, old);
+}

test/SEGVHandler_test.cxx

+#include <iostream>
+#include <signal.h>   /*sigaction*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h> /* mprotect */
+#include "CxxUtils/procmaps.h"
+#include "CxxUtils/page_address.h"
+#include "CxxUtils/PtrAccessSEGVHandler.h"
+#include "CxxUtils/cPtrAccessSEGVHandler.h"
+
+using namespace std;
+
+int main(void) {
+
+  int rc=0;
+  struct sigaction sa, stdSEGV;
+  sigaction(SIGSEGV,NULL,&stdSEGV);
+  sa = stdSEGV;
+  //  sa.sa_sigaction=trapReads;
+  procmaps p;
+  PtrAccessSEGVHandler h(p);
+  setPtrAccessSEGVHandler(&h);
+  sa.sa_sigaction= cPtrAccessSEGVHandler; 
+  sa.sa_flags=SA_SIGINFO;
+  rc=sigaction(SIGSEGV,&sa,NULL);
+  printf("sigaction installing handler returned %d\n",rc);
+  //  int *pInt=(int*)0x8000;
+  //  int *pInt=(int*)&cPtrAccessSEGVHandler; 
+  int *pInt=new int(11);
+  printf("@pInt=%x\n",pInt);
+  //  delete pInt;
+  //FIXME we can not R/W protect any address used by handler
+  //In particular procmaps entries can not be protected!
+  rc=mprotect((void*)(page_address(pInt)),0x1000,PROT_NONE);
+  if (rc) printf("mprotect heap failed\n");
+  else printf("set NONE protection @%x for page @%x containing addr=%x\n",
+	      PROT_NONE,page_address(pInt),pInt);
+  //can not mprotect stack objects
+//   string foo("bar");
+//   string *pFoo(&foo);
+//   rc=mprotect((void*)(page_address(pFoo)),0x1000,PROT_NONE);
+//   if (rc) printf("mprotect stack failed\n");
+  string *pString(new string("bar"));
+  printf("@pString=%x\n",pString);
+  rc=mprotect((void*)(page_address(pString)),0x1000,PROT_NONE);
+  if (rc) printf("mprotect string failed\n");
+  else printf("set NONE protection @%x for page @%x containing addr=%x\n",
+	      PROT_NONE,page_address(pString),pString);
+
+
+
+  //let's get some SEGVs
+  printf("try to read 11\n");
+  printf("accessing pInt %d\n",*pInt);
+  printf("reading again from pInt %d\n",*pInt);
+  printf("try to write 33\n");
+  *pInt=33;
+  printf("read %d\n",*pInt);
+  cout << "reading from string " << *pString << endl;
+  cout << "reading again from string " << *pString << endl;
+
+
+  //restore default/old handler
+  sigaction(SIGSEGV,&stdSEGV,NULL);
+  printf("try to read 33\n");
+  printf("read %d\n",*pInt);
+  PtrAccessSEGVHandler::const_iterator i(h.beginAccessedPtrs()),
+    e(h.endAccessedPtrs());
+  cout << "accessed ptrs" << endl;
+  while (i != e) {
+    cout << '@' << hex << *i++ << endl; 
+  }
+  delete pInt;
+  delete pString;
+  return rc;
+}