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