~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/x86/mpx-dig.c

Version: ~ [ linux-4.18 ] ~ [ linux-4.17.14 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.62 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.119 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.147 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.118 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.57 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.27.62 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~ [ linux-next-20180810 ] ~ [ linux-next-20180813 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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__)
 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 
 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 
 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 
 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 
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 
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 
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 
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 
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 
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 
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 }
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 
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
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 
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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp