mpx-dig.c 12 KB

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