1
2
3
4
5
6#include <stdlib.h>
7#include <sys/types.h>
8#include <unistd.h>
9#include <stdio.h>
10#include <errno.h>
11#include <sys/stat.h>
12#include <sys/mman.h>
13#include <string.h>
14#include <fcntl.h>
15#include "mpx-debug.h"
16#include "mpx-mm.h"
17#include "mpx-hw.h"
18
19unsigned long bounds_dir_global;
20
21#define mpx_dig_abort() __mpx_dig_abort(__FILE__, __func__, __LINE__)
22static void inline __mpx_dig_abort(const char *file, const char *func, int line)
23{
24 fprintf(stderr, "MPX dig abort @ %s::%d in %s()\n", file, line, func);
25 printf("MPX dig abort @ %s::%d in %s()\n", file, line, func);
26 abort();
27}
28
29
30
31
32
33
34
35
36
37
38
39
40long nr_incore(void *ptr, unsigned long size_bytes)
41{
42 int i;
43 long ret = 0;
44 long vec_len = size_bytes / PAGE_SIZE;
45 unsigned char *vec = malloc(vec_len);
46 int incore_ret;
47
48 if (!vec)
49 mpx_dig_abort();
50
51 incore_ret = mincore(ptr, size_bytes, vec);
52 if (incore_ret) {
53 printf("mincore ret: %d\n", incore_ret);
54 perror("mincore");
55 mpx_dig_abort();
56 }
57 for (i = 0; i < vec_len; i++)
58 ret += vec[i];
59 free(vec);
60 return ret;
61}
62
63int open_proc(int pid, char *file)
64{
65 static char buf[100];
66 int fd;
67
68 snprintf(&buf[0], sizeof(buf), "/proc/%d/%s", pid, file);
69 fd = open(&buf[0], O_RDONLY);
70 if (fd < 0)
71 perror(buf);
72
73 return fd;
74}
75
76struct vaddr_range {
77 unsigned long start;
78 unsigned long end;
79};
80struct vaddr_range *ranges;
81int nr_ranges_allocated;
82int nr_ranges_populated;
83int last_range = -1;
84
85int __pid_load_vaddrs(int pid)
86{
87 int ret = 0;
88 int proc_maps_fd = open_proc(pid, "maps");
89 char linebuf[10000];
90 unsigned long start;
91 unsigned long end;
92 char rest[1000];
93 FILE *f = fdopen(proc_maps_fd, "r");
94
95 if (!f)
96 mpx_dig_abort();
97 nr_ranges_populated = 0;
98 while (!feof(f)) {
99 char *readret = fgets(linebuf, sizeof(linebuf), f);
100 int parsed;
101
102 if (readret == NULL) {
103 if (feof(f))
104 break;
105 mpx_dig_abort();
106 }
107
108 parsed = sscanf(linebuf, "%lx-%lx%s", &start, &end, rest);
109 if (parsed != 3)
110 mpx_dig_abort();
111
112 dprintf4("result[%d]: %lx-%lx<->%s\n", parsed, start, end, rest);
113 if (nr_ranges_populated >= nr_ranges_allocated) {
114 ret = -E2BIG;
115 break;
116 }
117 ranges[nr_ranges_populated].start = start;
118 ranges[nr_ranges_populated].end = end;
119 nr_ranges_populated++;
120 }
121 last_range = -1;
122 fclose(f);
123 close(proc_maps_fd);
124 return ret;
125}
126
127int pid_load_vaddrs(int pid)
128{
129 int ret;
130
131 dprintf2("%s(%d)\n", __func__, pid);
132 if (!ranges) {
133 nr_ranges_allocated = 4;
134 ranges = malloc(nr_ranges_allocated * sizeof(ranges[0]));
135 dprintf2("%s(%d) allocated %d ranges @ %p\n", __func__, pid,
136 nr_ranges_allocated, ranges);
137 assert(ranges != NULL);
138 }
139 do {
140 ret = __pid_load_vaddrs(pid);
141 if (!ret)
142 break;
143 if (ret == -E2BIG) {
144 dprintf2("%s(%d) need to realloc\n", __func__, pid);
145 nr_ranges_allocated *= 2;
146 ranges = realloc(ranges,
147 nr_ranges_allocated * sizeof(ranges[0]));
148 dprintf2("%s(%d) allocated %d ranges @ %p\n", __func__,
149 pid, nr_ranges_allocated, ranges);
150 assert(ranges != NULL);
151 dprintf1("reallocating to hold %d ranges\n", nr_ranges_allocated);
152 }
153 } while (1);
154
155 dprintf2("%s(%d) done\n", __func__, pid);
156
157 return ret;
158}
159
160static inline int vaddr_in_range(unsigned long vaddr, struct vaddr_range *r)
161{
162 if (vaddr < r->start)
163 return 0;
164 if (vaddr >= r->end)
165 return 0;
166 return 1;
167}
168
169static inline int vaddr_mapped_by_range(unsigned long vaddr)
170{
171 int i;
172
173 if (last_range > 0 && vaddr_in_range(vaddr, &ranges[last_range]))
174 return 1;
175
176 for (i = 0; i < nr_ranges_populated; i++) {
177 struct vaddr_range *r = &ranges[i];
178
179 if (vaddr_in_range(vaddr, r))
180 continue;
181 last_range = i;
182 return 1;
183 }
184 return 0;
185}
186
187const int bt_entry_size_bytes = sizeof(unsigned long) * 4;
188
189void *read_bounds_table_into_buf(unsigned long table_vaddr)
190{
191#ifdef MPX_DIG_STANDALONE
192 static char bt_buf[MPX_BOUNDS_TABLE_SIZE_BYTES];
193 off_t seek_ret = lseek(fd, table_vaddr, SEEK_SET);
194 if (seek_ret != table_vaddr)
195 mpx_dig_abort();
196
197 int read_ret = read(fd, &bt_buf, sizeof(bt_buf));
198 if (read_ret != sizeof(bt_buf))
199 mpx_dig_abort();
200 return &bt_buf;
201#else
202 return (void *)table_vaddr;
203#endif
204}
205
206int dump_table(unsigned long table_vaddr, unsigned long base_controlled_vaddr,
207 unsigned long bde_vaddr)
208{
209 unsigned long offset_inside_bt;
210 int nr_entries = 0;
211 int do_abort = 0;
212 char *bt_buf;
213
214 dprintf3("%s() base_controlled_vaddr: 0x%012lx bde_vaddr: 0x%012lx\n",
215 __func__, base_controlled_vaddr, bde_vaddr);
216
217 bt_buf = read_bounds_table_into_buf(table_vaddr);
218
219 dprintf4("%s() read done\n", __func__);
220
221 for (offset_inside_bt = 0;
222 offset_inside_bt < MPX_BOUNDS_TABLE_SIZE_BYTES;
223 offset_inside_bt += bt_entry_size_bytes) {
224 unsigned long bt_entry_index;
225 unsigned long bt_entry_controls;
226 unsigned long this_bt_entry_for_vaddr;
227 unsigned long *bt_entry_buf;
228 int i;
229
230 dprintf4("%s() offset_inside_bt: 0x%lx of 0x%llx\n", __func__,
231 offset_inside_bt, MPX_BOUNDS_TABLE_SIZE_BYTES);
232 bt_entry_buf = (void *)&bt_buf[offset_inside_bt];
233 if (!bt_buf) {
234 printf("null bt_buf\n");
235 mpx_dig_abort();
236 }
237 if (!bt_entry_buf) {
238 printf("null bt_entry_buf\n");
239 mpx_dig_abort();
240 }
241 dprintf4("%s() reading *bt_entry_buf @ %p\n", __func__,
242 bt_entry_buf);
243 if (!bt_entry_buf[0] &&
244 !bt_entry_buf[1] &&
245 !bt_entry_buf[2] &&
246 !bt_entry_buf[3])
247 continue;
248
249 nr_entries++;
250
251 bt_entry_index = offset_inside_bt/bt_entry_size_bytes;
252 bt_entry_controls = sizeof(void *);
253 this_bt_entry_for_vaddr =
254 base_controlled_vaddr + bt_entry_index*bt_entry_controls;
255
256
257
258
259
260 if (this_bt_entry_for_vaddr > 0x00007fffffffffffUL)
261 this_bt_entry_for_vaddr |= 0xffff800000000000;
262
263 if (!vaddr_mapped_by_range(this_bt_entry_for_vaddr)) {
264 printf("bt_entry_buf: %p\n", bt_entry_buf);
265 printf("there is a bte for %lx but no mapping\n",
266 this_bt_entry_for_vaddr);
267 printf(" bde vaddr: %016lx\n", bde_vaddr);
268 printf("base_controlled_vaddr: %016lx\n", base_controlled_vaddr);
269 printf(" table_vaddr: %016lx\n", table_vaddr);
270 printf(" entry vaddr: %016lx @ offset %lx\n",
271 table_vaddr + offset_inside_bt, offset_inside_bt);
272 do_abort = 1;
273 mpx_dig_abort();
274 }
275 if (DEBUG_LEVEL < 4)
276 continue;
277
278 printf("table entry[%lx]: ", offset_inside_bt);
279 for (i = 0; i < bt_entry_size_bytes; i += sizeof(unsigned long))
280 printf("0x%016lx ", bt_entry_buf[i]);
281 printf("\n");
282 }
283 if (do_abort)
284 mpx_dig_abort();
285 dprintf4("%s() done\n", __func__);
286 return nr_entries;
287}
288
289int search_bd_buf(char *buf, int len_bytes, unsigned long bd_offset_bytes,
290 int *nr_populated_bdes)
291{
292 unsigned long i;
293 int total_entries = 0;
294
295 dprintf3("%s(%p, %x, %lx, ...) buf end: %p\n", __func__, buf,
296 len_bytes, bd_offset_bytes, buf + len_bytes);
297
298 for (i = 0; i < len_bytes; i += sizeof(unsigned long)) {
299 unsigned long bd_index = (bd_offset_bytes + i) / sizeof(unsigned long);
300 unsigned long *bounds_dir_entry_ptr = (unsigned long *)&buf[i];
301 unsigned long bounds_dir_entry;
302 unsigned long bd_for_vaddr;
303 unsigned long bt_start;
304 unsigned long bt_tail;
305 int nr_entries;
306
307 dprintf4("%s() loop i: %ld bounds_dir_entry_ptr: %p\n", __func__, i,
308 bounds_dir_entry_ptr);
309
310 bounds_dir_entry = *bounds_dir_entry_ptr;
311 if (!bounds_dir_entry) {
312 dprintf4("no bounds dir at index 0x%lx / 0x%lx "
313 "start at offset:%lx %lx\n", bd_index, bd_index,
314 bd_offset_bytes, i);
315 continue;
316 }
317 dprintf3("found bounds_dir_entry: 0x%lx @ "
318 "index 0x%lx buf ptr: %p\n", bounds_dir_entry, i,
319 &buf[i]);
320
321 bounds_dir_entry &= ~0x1;
322 (*nr_populated_bdes)++;
323 dprintf4("nr_populated_bdes: %p\n", nr_populated_bdes);
324 dprintf4("*nr_populated_bdes: %d\n", *nr_populated_bdes);
325
326 bt_start = bounds_dir_entry;
327 bt_tail = bounds_dir_entry + MPX_BOUNDS_TABLE_SIZE_BYTES - 1;
328 if (!vaddr_mapped_by_range(bt_start)) {
329 printf("bounds directory 0x%lx points to nowhere\n",
330 bounds_dir_entry);
331 mpx_dig_abort();
332 }
333 if (!vaddr_mapped_by_range(bt_tail)) {
334 printf("bounds directory end 0x%lx points to nowhere\n",
335 bt_tail);
336 mpx_dig_abort();
337 }
338
339
340
341
342
343 bd_for_vaddr = bd_index * (1UL<<20);
344
345 nr_entries = dump_table(bounds_dir_entry, bd_for_vaddr,
346 bounds_dir_global+bd_offset_bytes+i);
347 total_entries += nr_entries;
348 dprintf5("dir entry[%4ld @ %p]: 0x%lx %6d entries "
349 "total this buf: %7d bd_for_vaddrs: 0x%lx -> 0x%lx\n",
350 bd_index, buf+i,
351 bounds_dir_entry, nr_entries, total_entries,
352 bd_for_vaddr, bd_for_vaddr + (1UL<<20));
353 }
354 dprintf3("%s(%p, %x, %lx, ...) done\n", __func__, buf, len_bytes,
355 bd_offset_bytes);
356 return total_entries;
357}
358
359int proc_pid_mem_fd = -1;
360
361void *fill_bounds_dir_buf_other(long byte_offset_inside_bounds_dir,
362 long buffer_size_bytes, void *buffer)
363{
364 unsigned long seekto = bounds_dir_global + byte_offset_inside_bounds_dir;
365 int read_ret;
366 off_t seek_ret = lseek(proc_pid_mem_fd, seekto, SEEK_SET);
367
368 if (seek_ret != seekto)
369 mpx_dig_abort();
370
371 read_ret = read(proc_pid_mem_fd, buffer, buffer_size_bytes);
372
373 if (read_ret != buffer_size_bytes)
374 mpx_dig_abort();
375
376 return buffer;
377}
378void *fill_bounds_dir_buf_self(long byte_offset_inside_bounds_dir,
379 long buffer_size_bytes, void *buffer)
380
381{
382 unsigned char vec[buffer_size_bytes / PAGE_SIZE];
383 char *dig_bounds_dir_ptr =
384 (void *)(bounds_dir_global + byte_offset_inside_bounds_dir);
385
386
387
388
389 int incore_ret;
390
391 int incore = 0;
392 int i;
393
394 dprintf4("%s() dig_bounds_dir_ptr: %p\n", __func__, dig_bounds_dir_ptr);
395
396 incore_ret = mincore(dig_bounds_dir_ptr, buffer_size_bytes, &vec[0]);
397 if (incore_ret) {
398 printf("mincore ret: %d\n", incore_ret);
399 perror("mincore");
400 mpx_dig_abort();
401 }
402 for (i = 0; i < sizeof(vec); i++)
403 incore += vec[i];
404 dprintf4("%s() total incore: %d\n", __func__, incore);
405 if (!incore)
406 return NULL;
407 dprintf3("%s() total incore: %d\n", __func__, incore);
408 return dig_bounds_dir_ptr;
409}
410
411int inspect_pid(int pid)
412{
413 static int dig_nr;
414 long offset_inside_bounds_dir;
415 char bounds_dir_buf[sizeof(unsigned long) * (1UL << 15)];
416 char *dig_bounds_dir_ptr;
417 int total_entries = 0;
418 int nr_populated_bdes = 0;
419 int inspect_self;
420
421 if (getpid() == pid) {
422 dprintf4("inspecting self\n");
423 inspect_self = 1;
424 } else {
425 dprintf4("inspecting pid %d\n", pid);
426 mpx_dig_abort();
427 }
428
429 for (offset_inside_bounds_dir = 0;
430 offset_inside_bounds_dir < MPX_BOUNDS_TABLE_SIZE_BYTES;
431 offset_inside_bounds_dir += sizeof(bounds_dir_buf)) {
432 static int bufs_skipped;
433 int this_entries;
434
435 if (inspect_self) {
436 dig_bounds_dir_ptr =
437 fill_bounds_dir_buf_self(offset_inside_bounds_dir,
438 sizeof(bounds_dir_buf),
439 &bounds_dir_buf[0]);
440 } else {
441 dig_bounds_dir_ptr =
442 fill_bounds_dir_buf_other(offset_inside_bounds_dir,
443 sizeof(bounds_dir_buf),
444 &bounds_dir_buf[0]);
445 }
446 if (!dig_bounds_dir_ptr) {
447 bufs_skipped++;
448 continue;
449 }
450 this_entries = search_bd_buf(dig_bounds_dir_ptr,
451 sizeof(bounds_dir_buf),
452 offset_inside_bounds_dir,
453 &nr_populated_bdes);
454 total_entries += this_entries;
455 }
456 printf("mpx dig (%3d) complete, SUCCESS (%8d / %4d)\n", ++dig_nr,
457 total_entries, nr_populated_bdes);
458 return total_entries + nr_populated_bdes;
459}
460
461#ifdef MPX_DIG_REMOTE
462int main(int argc, char **argv)
463{
464 int err;
465 char *c;
466 unsigned long bounds_dir_entry;
467 int pid;
468
469 printf("mpx-dig starting...\n");
470 err = sscanf(argv[1], "%d", &pid);
471 printf("parsing: '%s', err: %d\n", argv[1], err);
472 if (err != 1)
473 mpx_dig_abort();
474
475 err = sscanf(argv[2], "%lx", &bounds_dir_global);
476 printf("parsing: '%s': %d\n", argv[2], err);
477 if (err != 1)
478 mpx_dig_abort();
479
480 proc_pid_mem_fd = open_proc(pid, "mem");
481 if (proc_pid_mem_fd < 0)
482 mpx_dig_abort();
483
484 inspect_pid(pid);
485 return 0;
486}
487#endif
488
489long inspect_me(struct mpx_bounds_dir *bounds_dir)
490{
491 int pid = getpid();
492
493 pid_load_vaddrs(pid);
494 bounds_dir_global = (unsigned long)bounds_dir;
495 dprintf4("enter %s() bounds dir: %p\n", __func__, bounds_dir);
496 return inspect_pid(pid);
497}
498