Stackdb
Stackdb is a stackable, multi-target and -level source debugger and memory forensics library.
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
loadallpagebypage.c
Go to the documentation of this file.
1 #define _GNU_SOURCE
2 
3 #include <sys/mman.h>
4 #include <dlfcn.h>
5 #include <sys/types.h>
6 #include <unistd.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <errno.h>
10 #include <string.h>
11 
12 /*
13  * Wrap __libc_start_main so that we mlockall/munlockall before
14  * executing anything. This is a best-effort, least-intrusion attempt
15  * to force all of the process's virtual address text pages to be loaded
16  * before main(). They may later be *unloaded*, but by default main()
17  * will begin with all pages loaded into RAM and in the page table.
18  * Well, of course it's not guaranteed; but it should work on any
19  * reasonable kernel that is not experiencing memory pressure.
20  */
21 
22 #ifndef PAGE_SIZE
23 #define PAGE_SIZE 4096
24 #endif
25 
26 #ifndef PROT_SHARED
27 #define PROT_SHARED 0x8
28 #endif
29 
30 /*
31  * Linux has long defaulted RLIMIT_MEMLOCK to 32KB, "for gpg". Shoot
32  * for that and fall back to a page if it doesn't work...
33  */
34 #define MAX_MLOCK_BYTES 32 * 1024
35 
36 static size_t mlock_len = MAX_MLOCK_BYTES;
37 
38 int mlock_page_range(unsigned long start,unsigned long end) {
39  unsigned long current;
40  int retval = 0;
41 
42  current = start;
43  while (current < end) {
44  if (mlock((const void *)current,mlock_len)) {
45  if (mlock_len < MAX_MLOCK_BYTES) {
46  ++retval;
47  }
48  else {
49  /* Adjust lower to single page, try again */
50  mlock_len = PAGE_SIZE;
51  continue;
52  }
53  }
54  else
55  munlockall();
56  //munlock((const void *)current,mlock_len);
57 
58  current += mlock_len;
59  }
60 
61  return retval;
62 }
63 
64 typedef enum {
72 
73 int __libc_start_main(int (*main) (int,char **,char **),
74  int argc,char **ubp_av,
75  void (*init) (void),
76  void (*fini)(void),
77  void (*rtld_fini)(void),
78  void (*stack_end)) {
79  int (*original__libc_start_main)(int (*main) (int,char **,char **),
80  int argc,char **ubp_av,
81  void (*init) (void),
82  void (*fini)(void),
83  void (*rtld_fini)(void),
84  void (*stack_end));
85  /*
86  * We pick 512 on purpose -- it should not force us to require a new
87  * stack page yet -- and it should be big enough to read "any
88  * conceivable" /proc/pid/maps line. We could use PATH_MAX+n, but
89  * PATH_MAX is really big (i.e., a page, I think). So arbitrarily
90  * split the difference and try to play nice for the stack.
91  * Obviously we could check the stack pointer and really play nice,
92  * but eh.
93  */
94 #define LBUFSIZ 512
95  char buf[LBUFSIZ];
96  char buf2[LBUFSIZ];
97  FILE *f;
98  int errors = 0;
99  region_type_t rt;
100  int pf;
101  unsigned long long start,end,offset;
102  int rc;
103  char p[4];
104 
105  /* Open the maps file... */
106  snprintf(buf,LBUFSIZ,"/proc/%d/maps",getpid());
107  f = fopen(buf,"r");
108  if (!f)
109  goto errout;
110 
111  /* Read the maps file and mlock in chunks. */
112  while (1) {
113  rt = 0;
114  pf = 0;
115 
116  errno = 0;
117  /*
118  * If it was an error, try to inform the user if they care. If
119  * error or just EOF, quit; this is best-effort.
120  */
121  if (!fgets(buf,LBUFSIZ,f)) {
122  if (errno)
123  errors *= 100000;
124  break;
125  }
126 
127  //fprintf(stderr,"scanning mmap line %s",buf);
128 
129  rc = sscanf(buf,"%Lx-%Lx %c%c%c%c %Lx %*x:%*x %*d %s",&start,&end,
130  &p[0],&p[1],&p[2],&p[3],&offset,buf2);
131  if (rc == 8 || rc == 7) {
132  rt = 0;
133  if (rc == 8) {
134  /* we got the whole thing, including a path */
135  if (strcmp(buf,"[heap]") == 0)
136  rt |= REGION_TYPE_HEAP;
137  else if (strcmp(buf,"[stack]") == 0)
138  rt |= REGION_TYPE_STACK;
139  else if (strcmp(buf,"[vdso]") == 0)
140  rt |= REGION_TYPE_VDSO;
141  else if (strcmp(buf,"[vsyscall]") == 0)
142  rt |= REGION_TYPE_VSYSCALL;
143  else
144  rt |= REGION_TYPE_FILE;
145  }
146  else {
147  rt |= REGION_TYPE_ANON;
148  }
149 
150  pf = 0;
151  if (p[0] == 'r')
152  pf |= PROT_READ;
153  if (p[1] == 'w')
154  pf |= PROT_WRITE;
155  if (p[2] == 'x')
156  pf |= PROT_EXEC;
157  if (p[3] == 's')
158  pf |= PROT_SHARED;
159 
160  /*
161  * XXX: in the future, check LOADALL_REGIONS env var to see
162  * which regions we should "lock and load"...
163  */
164  errors += mlock_page_range(start,end);
165  }
166  else if (rc > 0 && !errno) {
167  errors *= 10000;
168  //fprintf(stderr,"weird content in /proc/pid/maps (%d)!\n",rc);
169  }
170  else if (rc > 0 && errno) {
171  errors *= 10000;
172  //fprintf(stderr,"weird content in /proc/pid/maps (%d): %s!\n",rc,strerror(errno));
173  }
174  }
175  fclose(f);
176 
177  goto out;
178 
179  errout:
180  snprintf(buf,64,"LOADALL_ERR=%d",errors);
181  putenv(buf);
182 
183  out:
184  original__libc_start_main = dlsym(RTLD_NEXT,"__libc_start_main");
185  return original__libc_start_main(main,argc,ubp_av,
186  init,fini,rtld_fini,stack_end);
187 }
int mlock_page_range(unsigned long start, unsigned long end)
void * p
#define PROT_SHARED
int main(int argc, char **argv)
Definition: debugserver.c:306
region_type_t
int __libc_start_main(int(*main)(int, char **, char **), int argc, char **ubp_av, void(*init)(void), void(*fini)(void), void(*rtld_fini)(void), void(*stack_end))
#define PROT_EXEC
Definition: common.h:108
#define PROT_WRITE
Definition: common.h:107
#define MAX_MLOCK_BYTES
#define LBUFSIZ
#define PAGE_SIZE
#define PROT_READ
Definition: common.h:106