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