int main(int argc, char **argv) { int err; char *c; unsigned long bounds_dir_entry; int pid; printf("mpx-dig starting...\n"); err = sscanf(argv[1], "%d", &pid); printf("parsing: '%s', err: %d\n", argv[1], err); if (err != 1) mpx_dig_abort(); err = sscanf(argv[2], "%lx", &bounds_dir_global); printf("parsing: '%s': %d\n", argv[2], err); if (err != 1) mpx_dig_abort(); proc_pid_mem_fd = open_proc(pid, "mem"); if (proc_pid_mem_fd < 0) mpx_dig_abort(); inspect_pid(pid); return 0; }
int parse_pid_stat_small(pid_t pid, struct proc_pid_stat_small *s) { char *tok, *p; int fd; int n; fd = open_proc(pid, "stat"); if (fd < 0) return -1; n = read(fd, buf, BUF_SIZE); if (n < 1) { pr_err("stat for %d is corrupted\n", pid); close(fd); return -1; } close(fd); memset(s, 0, sizeof(*s)); tok = strchr(buf, ' '); if (!tok) goto err; *tok++ = '\0'; if (*tok != '(') goto err; s->pid = atoi(buf); p = strrchr(tok + 1, ')'); if (!p) goto err; *tok = '\0'; *p = '\0'; strncpy(s->comm, tok + 1, sizeof(s->comm)); n = sscanf(p + 1, " %c %d %d %d", &s->state, &s->ppid, &s->pgid, &s->sid); if (n < 4) goto err; return 0; err: pr_err("Parsing %d's stat failed (#fields do not match)\n", pid); return -1; }
static int check_fcntl(void) { u32 v[2]; int fd; fd = open_proc(PROC_SELF, "comm"); if (fd < 0) return -1; if (fcntl(fd, F_GETOWNER_UIDS, (long)v)) { pr_perror("Can'r fetch file owner UIDs"); close(fd); return -1; } close(fd); return 0; }
int __pid_load_vaddrs(int pid) { int ret = 0; int proc_maps_fd = open_proc(pid, "maps"); char linebuf[10000]; unsigned long start; unsigned long end; char rest[1000]; FILE *f = fdopen(proc_maps_fd, "r"); if (!f) mpx_dig_abort(); nr_ranges_populated = 0; while (!feof(f)) { char *readret = fgets(linebuf, sizeof(linebuf), f); int parsed; if (readret == NULL) { if (feof(f)) break; mpx_dig_abort(); } parsed = sscanf(linebuf, "%lx-%lx%s", &start, &end, rest); if (parsed != 3) mpx_dig_abort(); dprintf4("result[%d]: %lx-%lx<->%s\n", parsed, start, end, rest); if (nr_ranges_populated >= nr_ranges_allocated) { ret = -E2BIG; break; } ranges[nr_ranges_populated].start = start; ranges[nr_ranges_populated].end = end; nr_ranges_populated++; } last_range = -1; fclose(f); close(proc_maps_fd); return ret; }
int switch_ns(int pid, struct ns_desc *nd, int *rst) { char buf[32]; int nsfd; int ret = -1; nsfd = open_proc(pid, "ns/%s", nd->str); if (nsfd < 0) { pr_perror("Can't open ns file"); goto err_ns; } if (rst) { snprintf(buf, sizeof(buf), "/proc/self/ns/%s", nd->str); *rst = open(buf, O_RDONLY); if (*rst < 0) { pr_perror("Can't open ns file"); goto err_rst; } } ret = setns(nsfd, nd->cflag); if (ret < 0) { pr_perror("Can't setns %d/%s", pid, nd->str); goto err_set; } close(nsfd); return 0; err_set: if (rst) close(*rst); err_rst: close(nsfd); err_ns: return -1; }
static int dump_one_shmem(struct shmem_info_dump *si) { struct iovec *iovs; struct page_pipe *pp; struct page_xfer xfer; int err, ret = -1, fd; unsigned char *map = NULL; void *addr = NULL; unsigned long pfn, nrpages; pr_info("Dumping shared memory %ld\n", si->shmid); nrpages = (si->size + PAGE_SIZE - 1) / PAGE_SIZE; map = xmalloc(nrpages * sizeof(*map)); if (!map) goto err; fd = open_proc(si->pid, "map_files/%lx-%lx", si->start, si->end); if (fd < 0) goto err; addr = mmap(NULL, si->size, PROT_READ, MAP_SHARED, fd, 0); close(fd); if (addr == MAP_FAILED) { pr_err("Can't map shmem 0x%lx (0x%lx-0x%lx)\n", si->shmid, si->start, si->end); goto err; } /* * We can't use pagemap here, because this vma is * not mapped to us at all, but mincore reports the * pagecache status of a file, which is correct in * this case. */ err = mincore(addr, si->size, map); if (err) goto err_unmap; iovs = xmalloc(((nrpages + 1) / 2) * sizeof(struct iovec)); if (!iovs) goto err_unmap; pp = create_page_pipe((nrpages + 1) / 2, iovs, true); if (!pp) goto err_iovs; err = open_page_xfer(&xfer, CR_FD_SHMEM_PAGEMAP, si->shmid); if (err) goto err_pp; for (pfn = 0; pfn < nrpages; pfn++) { if (!(map[pfn] & PAGE_RSS)) continue; again: ret = page_pipe_add_page(pp, (unsigned long)addr + pfn * PAGE_SIZE); if (ret == -EAGAIN) { ret = dump_pages(pp, &xfer, addr); if (ret) goto err_xfer; page_pipe_reinit(pp); goto again; } else if (ret) goto err_xfer; } ret = dump_pages(pp, &xfer, addr); err_xfer: xfer.close(&xfer); err_pp: destroy_page_pipe(pp); err_iovs: xfree(iovs); err_unmap: munmap(addr, si->size); err: xfree(map); return ret; }
static int dump_one_shmem(struct shmem_info_dump *si) { struct iovec *iovs; struct page_pipe *pp; struct page_pipe_buf *ppb; struct page_xfer xfer; int err, ret = -1, fd; unsigned char *map = NULL; void *addr = NULL; unsigned long pfn, nrpages; pr_info("Dumping shared memory %ld\n", si->shmid); nrpages = (si->size + PAGE_SIZE - 1) / PAGE_SIZE; map = xmalloc(nrpages * sizeof(*map)); if (!map) goto err; fd = open_proc(si->pid, "map_files/%lx-%lx", si->start, si->end); if (fd < 0) goto err; addr = mmap(NULL, si->size, PROT_READ, MAP_SHARED, fd, 0); close(fd); if (addr == MAP_FAILED) { pr_err("Can't map shmem 0x%lx (0x%lx-0x%lx)\n", si->shmid, si->start, si->end); goto err; } /* * We can't use pagemap here, because this vma is * not mapped to us at all, but mincore reports the * pagecache status of a file, which is correct in * this case. */ err = mincore(addr, si->size, map); if (err) goto err_unmap; iovs = xmalloc(((nrpages + 1) / 2) * sizeof(struct iovec)); if (!iovs) goto err_unmap; pp = create_page_pipe((nrpages + 1) / 2, iovs); if (!pp) goto err_iovs; for (pfn = 0; pfn < nrpages; pfn++) { if (!(map[pfn] & PAGE_RSS)) continue; if (page_pipe_add_page(pp, (unsigned long)addr + pfn * PAGE_SIZE)) goto err_pp; } list_for_each_entry(ppb, &pp->bufs, l) if (vmsplice(ppb->p[1], ppb->iov, ppb->nr_segs, SPLICE_F_GIFT | SPLICE_F_NONBLOCK) != ppb->pages_in * PAGE_SIZE) { pr_perror("Can't get shmem into page-pipe"); goto err_pp; } err = open_page_xfer(&xfer, CR_FD_SHMEM_PAGEMAP, si->shmid); if (err) goto err_pp; ret = page_xfer_dump_pages(&xfer, pp, (unsigned long)addr); xfer.close(&xfer); err_pp: destroy_page_pipe(pp); err_iovs: xfree(iovs); err_unmap: munmap(addr, si->size); err: xfree(map); return ret; }
int parse_pid_status(pid_t pid, struct proc_status_creds *cr) { struct bfd f; int done = 0; int ret = -1; char *str; f.fd = open_proc(pid, "status"); if (f.fd < 0) { pr_perror("Can't open proc status"); return -1; } if (bfdopenr(&f)) return -1; while (done < 9) { str = breadline(&f); if (str == NULL) break; if (IS_ERR(str)) goto err_parse; if (!strncmp(str, "State:", 6)) { cr->state = str[7]; done++; } if (!strncmp(str, "PPid:", 5)) { if (sscanf(str, "PPid:\t%d", &cr->ppid) != 1) { pr_err("Unable to parse: %s\n", str); goto err_parse; } done++; } if (!strncmp(str, "Uid:", 4)) { if (ids_parse(str + 5, cr->uids)) goto err_parse; done++; } if (!strncmp(str, "Gid:", 4)) { if (ids_parse(str + 5, cr->gids)) goto err_parse; done++; } if (!strncmp(str, "CapInh:", 7)) { if (cap_parse(str + 8, cr->cap_inh)) goto err_parse; done++; } if (!strncmp(str, "CapEff:", 7)) { if (cap_parse(str + 8, cr->cap_eff)) goto err_parse; done++; } if (!strncmp(str, "CapPrm:", 7)) { if (cap_parse(str + 8, cr->cap_prm)) goto err_parse; done++; } if (!strncmp(str, "CapBnd:", 7)) { if (cap_parse(str + 8, cr->cap_bnd)) goto err_parse; done++; } if (!strncmp(str, "Seccomp:", 8)) { if (sscanf(str + 9, "%d", &cr->seccomp_mode) != 1) { goto err_parse; } if (cr->seccomp_mode == SECCOMP_MODE_FILTER) { pr_err("SECCOMP_MODE_FILTER not currently supported\n"); goto err_parse; } done++; } } if (done >= 8) ret = 0; err_parse: if (ret) pr_err("Error parsing proc status file\n"); bclose(&f); return ret; }
int parse_pid_stat(pid_t pid, struct proc_pid_stat *s) { char *tok, *p; int fd; int n; fd = open_proc(pid, "stat"); if (fd < 0) return -1; n = read(fd, buf, BUF_SIZE); close(fd); if (n < 1) { pr_err("stat for %d is corrupted\n", pid); return -1; } memset(s, 0, sizeof(*s)); tok = strchr(buf, ' '); if (!tok) goto err; *tok++ = '\0'; if (*tok != '(') goto err; s->pid = atoi(buf); p = strrchr(tok + 1, ')'); if (!p) goto err; *tok = '\0'; *p = '\0'; strlcpy(s->comm, tok + 1, sizeof(s->comm)); n = sscanf(p + 1, " %c %d %d %d %d %d %u %lu %lu %lu %lu " "%lu %lu %ld %ld %ld %ld %d %d %llu %lu %ld %lu %lu %lu %lu " "%lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu %lu %ld " "%lu %lu %lu %lu %lu %lu %lu %d", &s->state, &s->ppid, &s->pgid, &s->sid, &s->tty_nr, &s->tty_pgrp, &s->flags, &s->min_flt, &s->cmin_flt, &s->maj_flt, &s->cmaj_flt, &s->utime, &s->stime, &s->cutime, &s->cstime, &s->priority, &s->nice, &s->num_threads, &s->zero0, &s->start_time, &s->vsize, &s->mm_rss, &s->rsslim, &s->start_code, &s->end_code, &s->start_stack, &s->esp, &s->eip, &s->sig_pending, &s->sig_blocked, &s->sig_ignored, &s->sig_handled, &s->wchan, &s->zero1, &s->zero2, &s->exit_signal, &s->task_cpu, &s->rt_priority, &s->policy, &s->delayacct_blkio_ticks, &s->gtime, &s->cgtime, &s->start_data, &s->end_data, &s->start_brk, &s->arg_start, &s->arg_end, &s->env_start, &s->env_end, &s->exit_code); if (n < 50) goto err; return 0; err: pr_err("Parsing %d's stat failed (#fields do not match)\n", pid); return -1; }
int parse_smaps(pid_t pid, struct vm_area_list *vma_area_list) { struct vma_area *vma_area = NULL; unsigned long start, end, pgoff, prev_end = 0; char r, w, x, s; int ret = -1; struct vma_file_info vfi; struct vma_file_info prev_vfi = {}; DIR *map_files_dir = NULL; struct bfd f; vma_area_list->nr = 0; vma_area_list->nr_aios = 0; vma_area_list->longest = 0; vma_area_list->priv_size = 0; INIT_LIST_HEAD(&vma_area_list->h); f.fd = open_proc(pid, "smaps"); if (f.fd < 0) goto err_n; if (bfdopenr(&f)) goto err_n; map_files_dir = opendir_proc(pid, "map_files"); if (!map_files_dir) /* old kernel? */ goto err; while (1) { int num; char file_path[32]; bool eof; char *str; str = breadline(&f); if (IS_ERR(str)) goto err; eof = (str == NULL); if (!eof && !is_vma_range_fmt(str)) { if (!strncmp(str, "Nonlinear", 9)) { BUG_ON(!vma_area); pr_err("Nonlinear mapping found %016"PRIx64"-%016"PRIx64"\n", vma_area->e->start, vma_area->e->end); /* * VMA is already on list and will be * freed later as list get destroyed. */ vma_area = NULL; goto err; } else if (!strncmp(str, "VmFlags: ", 9)) { BUG_ON(!vma_area); if (parse_vmflags(&str[9], vma_area)) goto err; continue; } else continue; } if (vma_area && vma_list_add(vma_area, vma_area_list, &prev_end, &vfi, &prev_vfi)) goto err; if (eof) break; vma_area = alloc_vma_area(); if (!vma_area) goto err; memzero(file_path, sizeof(file_path)); num = sscanf(str, "%lx-%lx %c%c%c%c %lx %x:%x %lu %31s", &start, &end, &r, &w, &x, &s, &pgoff, &vfi.dev_maj, &vfi.dev_min, &vfi.ino, file_path); if (num < 10) { pr_err("Can't parse: %s\n", str); goto err; } vma_area->e->start = start; vma_area->e->end = end; vma_area->e->pgoff = pgoff; vma_area->e->prot = PROT_NONE; if (r == 'r') vma_area->e->prot |= PROT_READ; if (w == 'w') vma_area->e->prot |= PROT_WRITE; if (x == 'x') vma_area->e->prot |= PROT_EXEC; if (s == 's') vma_area->e->flags = MAP_SHARED; else if (s == 'p') vma_area->e->flags = MAP_PRIVATE; else { pr_err("Unexpected VMA met (%c)\n", s); goto err; } if (handle_vma(pid, vma_area, file_path, map_files_dir, &vfi, &prev_vfi, vma_area_list)) goto err; } vma_area = NULL; ret = 0; err: bclose(&f); err_n: if (map_files_dir) closedir(map_files_dir); xfree(vma_area); return ret; }
int sysctl_op(struct sysctl_req *req, size_t nr_req, int op, unsigned int ns) { int i, fd, ret; struct sysctl_userns_req *userns_req; struct sysctl_req *cur; if (nr_req == 0) return 0; if (ns & ~KNOWN_NS_MASK) { pr_err("don't know how to restore some namespaces in %u\n", ns); return -1; } /* The way sysctl files behave on open/write depends on the namespace * they correspond to. If we don't want to interact with something in a * namespace (e.g. kernel/cap_last_cap is global), we can do this from * the current process. Similarly, if we're accessing net namespaces, * we can just do the operation from our current process, since * anything with CAP_NET_ADMIN can write to the net/ sysctls, and we * still have that even when restoring in a user ns. * * For IPC/UTS, we restore them as described above. * * For read operations, we need to copy the values back to return. * Fortunately, we only do read on dump (or global reads on restore), * so we can do those in process as well. */ if (!ns || ns & CLONE_NEWNET || op == CTL_READ) return __nonuserns_sysctl_op(req, nr_req, op); /* * In order to avoid lots of opening of /proc/sys for each struct sysctl_req, * we encode each array of sysctl_reqs into one contiguous region of memory so * it can be passed via userns_call if necessary. It looks like this: * * struct sysctl_userns_req struct sysctl_req name arg * --------------------------------------------------------------------------- * | op | nr_req | reqs | <fields> | name | arg | "the name" | "the arg" ... * --------------------------------------------------------------------------- * |____^ |______|__^ ^ * |_______________| */ userns_req = alloca(MAX_UNSFD_MSG_SIZE); userns_req->op = op; userns_req->nr_req = nr_req; userns_req->ns = ns; userns_req->reqs = (struct sysctl_req *) (&userns_req[1]); cur = userns_req->reqs; for (i = 0; i < nr_req; i++) { int arg_len = sysctl_userns_arg_size(req[i].type); int name_len = strlen(req[i].name) + 1; int total_len = sizeof(*cur) + arg_len + name_len; if (((char *) cur) + total_len >= ((char *) userns_req) + MAX_UNSFD_MSG_SIZE) { pr_err("sysctl msg %s too big: %d\n", req[i].name, total_len); return -1; } /* copy over the non-pointer fields */ cur->type = req[i].type; cur->flags = req[i].flags; cur->name = (char *) &cur[1]; strcpy(cur->name, req[i].name); cur->arg = cur->name + name_len; memcpy(cur->arg, req[i].arg, arg_len); cur = (struct sysctl_req *) (((char *) cur) + total_len); } fd = open_proc(PROC_SELF, "ns"); if (fd < 0) return -1; ret = userns_call(__userns_sysctl_op, 0, userns_req, MAX_UNSFD_MSG_SIZE, fd); close(fd); return ret; }