diff --git a/sys/sys/proc.h b/sys/sys/proc.h index fb97913..4949f98 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -168,6 +168,7 @@ struct p_sched; struct proc; struct procdesc; struct racct; +struct sbuf; struct sleepqueue; struct td_sched; struct thread; @@ -843,6 +844,10 @@ int p_canwait(struct thread *td, struct proc *p); struct pargs *pargs_alloc(int len); void pargs_drop(struct pargs *pa); void pargs_hold(struct pargs *pa); +int proc_getargv(struct thread *td, struct proc *p, struct sbuf *sb, + size_t nchr); +int proc_getenvv(struct thread *td, struct proc *p, struct sbuf *sb, + size_t nchr); void procinit(void); void proc_linkup0(struct proc *p, struct thread *td); void proc_linkup(struct proc *p, struct thread *td); diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index 1e879f5..99ea342 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -559,6 +559,8 @@ SYSCTL_ALLOWED_TYPES(UINT64, uint64_t *a; unsigned long long *b; ); #define KERN_PROC_VMMAP 32 /* VM map entries for process */ #define KERN_PROC_FILEDESC 33 /* File descriptors for process */ #define KERN_PROC_GROUPS 34 /* process groups */ +#define KERN_PROC_ENV 35 /* get environment */ +#define KERN_PROC_AUXV 36 /* get ELF auxiliary vector */ /* * KERN_IPC identifiers diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 998e7ca..ef4055a 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -49,6 +50,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -66,6 +68,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include + #ifdef DDB #include #endif @@ -1356,6 +1360,218 @@ pargs_drop(struct pargs *pa) pargs_free(pa); } +static int +proc_read_mem(struct thread *td, struct proc *p, vm_offset_t offset, void* buf, + size_t len) +{ + struct iovec iov; + struct uio uio; + + iov.iov_base = (caddr_t)buf; + iov.iov_len = len; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = offset; + uio.uio_resid = (ssize_t)len; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_READ; + uio.uio_td = td; + + return (proc_rwmem(p, &uio)); +} + +#define PROC_AUXV_MAX 256 /* Limit on auxv size. */ + +enum proc_vector_type { + PROC_ARG, + PROC_ENV, + PROC_AUX, +}; + +#ifdef COMPAT_FREEBSD32 +static int +get_proc_vector32(struct thread *td, struct proc *p, char ***proc_vectorp, + size_t *vsizep, enum proc_vector_type type) +{ + struct freebsd32_ps_strings pss; + Elf32_Auxinfo aux; + vm_offset_t vptr, ptr; + uint32_t *proc_vector32; + char **proc_vector; + size_t vsize, size; + int i, error; + + error = proc_read_mem(td, p, (vm_offset_t)(p->p_sysent->sv_psstrings), + &pss, sizeof(pss)); + if (error != 0) + return (error); + switch (type) { + case PROC_ARG: + vptr = (vm_offset_t)PTRIN(pss.ps_argvstr); + vsize = pss.ps_nargvstr; + size = vsize * sizeof(int32_t); + break; + case PROC_ENV: + vptr = (vm_offset_t)PTRIN(pss.ps_envstr); + vsize = pss.ps_nenvstr; + size = vsize * sizeof(int32_t); + break; + case PROC_AUX: + vptr = ptr = (vm_offset_t)PTRIN(pss.ps_envstr) + + (pss.ps_nenvstr + 1) * sizeof(int32_t); + for (i = 0; i < PROC_AUXV_MAX; i++) { + error = proc_read_mem(td, p, ptr, &aux, sizeof(aux)); + if (error != 0) + return (error); + if (aux.a_type == AT_NULL) + break; + ptr += sizeof(aux); + } + if (aux.a_type != AT_NULL) { + *proc_vectorp = NULL; + *vsizep = 0; + return (0); + } + vsize = i + 1; + size = vsize * sizeof(aux); + break; + default: + KASSERT(0, ("Wrong proc vector type: %d", type)); + } + proc_vector32 = malloc(size, M_TEMP, M_WAITOK); + error = proc_read_mem(td, p, vptr, proc_vector32, size); + if (error != 0) + goto done; + if (type == PROC_AUX) { + *proc_vectorp = (char **)proc_vector32; + *vsizep = vsize; + return (0); + } + proc_vector = malloc(vsize * sizeof(char *), M_TEMP, M_WAITOK); + for (i = 0; i < (int)vsize; i++) + proc_vector[i] = PTRIN(proc_vector32[i]); + *proc_vectorp = proc_vector; + *vsizep = vsize; +done: + free(proc_vector32, M_TEMP); + return (error); +} +#endif + +static int +get_proc_vector(struct thread *td, struct proc *p, char ***proc_vectorp, + size_t *vsizep, enum proc_vector_type type) +{ + struct ps_strings pss; + Elf_Auxinfo aux; + vm_offset_t vptr, ptr; + char **proc_vector; + size_t vsize, size; + int error, i; + +#ifdef COMPAT_FREEBSD32 + if (SV_PROC_FLAG(p, SV_ILP32) != 0) + return (get_proc_vector32(td, p, proc_vectorp, vsizep, type)); +#endif + error = proc_read_mem(td, p, (vm_offset_t)(p->p_sysent->sv_psstrings), + &pss, sizeof(pss)); + if (error != 0) + return (error); + switch (type) { + case PROC_ARG: + vptr = (vm_offset_t)pss.ps_argvstr; + vsize = pss.ps_nargvstr; + size = vsize * sizeof(char *); + break; + case PROC_ENV: + vptr = (vm_offset_t)pss.ps_envstr; + vsize = pss.ps_nenvstr; + size = vsize * sizeof(char *); + break; + case PROC_AUX: + vptr = ptr = (vm_offset_t)pss.ps_envstr + (pss.ps_nenvstr + 1) + * sizeof(char *); + for (i = 0; i < PROC_AUXV_MAX; i++) { + error = proc_read_mem(td, p, ptr, &aux, sizeof(aux)); + if (error != 0) + return (error); + if (aux.a_type == AT_NULL) + break; + ptr += sizeof(aux); + } + if (aux.a_type != AT_NULL) { + *proc_vectorp = NULL; + *vsizep = 0; + return (0); + } + vsize = i + 1; + size = vsize * sizeof(aux); + break; + default: + KASSERT(0, ("Wrong proc vector type: %d", type)); + } + proc_vector = malloc(size, M_TEMP, M_WAITOK); + error = proc_read_mem(td, p, vptr, proc_vector, size); + if (error != 0) { + free(proc_vector, M_TEMP); + return (error); + } + *proc_vectorp = proc_vector; + *vsizep = vsize; + + return (0); +} + +#define GET_PS_STRINGS_CHUNK_SZ 256 /* Chunk size (bytes) for ps_strings operations. */ + +static int +get_ps_strings(struct thread *td, struct proc *p, struct sbuf *sb, + enum proc_vector_type type, size_t nchr) +{ + vm_offset_t offset; + size_t done, len, vsize; + int error, i; + char **proc_vector; + char pss_string[GET_PS_STRINGS_CHUNK_SZ]; + + error = get_proc_vector(td, p, &proc_vector, &vsize, type); + if (error != 0) + return (error); + for (done = 0, i = 0; i < (int)vsize && done < nchr; i++) { + for (offset = (vm_offset_t)proc_vector[i]; ; + offset += GET_PS_STRINGS_CHUNK_SZ) { + error = proc_read_mem(td, p, offset, pss_string, + sizeof(pss_string)); + if (error != 0) + goto done; + len = strnlen(pss_string, GET_PS_STRINGS_CHUNK_SZ); + if (done + len >= nchr) + len = nchr - done - 1; + sbuf_bcat(sb, pss_string, len); + if (len != GET_PS_STRINGS_CHUNK_SZ) + break; + done += GET_PS_STRINGS_CHUNK_SZ; + } + sbuf_bcat(sb, "", 1); + done += len + 1; + } +done: + free(proc_vector, M_TEMP); + return (error); +} + +int +proc_getargv(struct thread *td, struct proc *p, struct sbuf *sb, size_t nchr) +{ + return (get_ps_strings(curthread, p, sb, PROC_ARG, nchr)); +} + +int +proc_getenvv(struct thread *td, struct proc *p, struct sbuf *sb, size_t nchr) +{ + return (get_ps_strings(curthread, p, sb, PROC_ENV, nchr)); +} + /* * This sysctl allows a process to retrieve the argument list or process * title for another process without groping around in the address space @@ -1369,7 +1585,8 @@ sysctl_kern_proc_args(SYSCTL_HANDLER_ARGS) u_int namelen = arg2; struct pargs *newpa, *pa; struct proc *p; - int error = 0; + struct sbuf sb; + int error = 0, error2; if (namelen != 1) return (EINVAL); @@ -1389,11 +1606,24 @@ sysctl_kern_proc_args(SYSCTL_HANDLER_ARGS) } pa = p->p_args; - pargs_hold(pa); - PROC_UNLOCK(p); - if (pa != NULL) + if (pa != NULL) { + pargs_hold(pa); + PROC_UNLOCK(p); error = SYSCTL_OUT(req, pa->ar_args, pa->ar_length); - pargs_drop(pa); + pargs_drop(pa); + } else if ((p->p_flag & (P_WEXIT | P_SYSTEM)) == 0) { + _PHOLD(p); + PROC_UNLOCK(p); + sbuf_new_for_sysctl(&sb, NULL, GET_PS_STRINGS_CHUNK_SZ, req); + error = proc_getargv(curthread, p, &sb, req->oldlen); + error2 = sbuf_finish(&sb); + PRELE(p); + sbuf_delete(&sb); + if (error == 0 && error2 != 0) + error = error2; + } else { + PROC_UNLOCK(p); + } if (error != 0 || req->newptr == NULL) return (error); @@ -1414,6 +1644,95 @@ sysctl_kern_proc_args(SYSCTL_HANDLER_ARGS) } /* + * This sysctl allows a process to retrieve environment of another process. + */ +static int +sysctl_kern_proc_env(SYSCTL_HANDLER_ARGS) +{ + int *name = (int*) arg1; + u_int namelen = arg2; + struct proc *p; + struct sbuf sb; + int error, error2; + + if (namelen != 1) + return (EINVAL); + + p = pfind((pid_t)name[0]); + if (p == NULL) + return (ESRCH); + if (p->p_flag & P_WEXIT) { + PROC_UNLOCK(p); + return (ESRCH); + } + if ((error = p_candebug(curthread, p)) != 0) { + PROC_UNLOCK(p); + return (error); + } + if (p->p_flag & P_SYSTEM) { + PROC_UNLOCK(p); + return (0); + } + _PHOLD(p); + PROC_UNLOCK(p); + sbuf_new_for_sysctl(&sb, NULL, GET_PS_STRINGS_CHUNK_SZ, req); + error = proc_getenvv(curthread, p, &sb, req->oldlen); + error2 = sbuf_finish(&sb); + PRELE(p); + sbuf_delete(&sb); + return (error != 0 ? error : error2); +} + +/* + * This sysctl allows a process to retrieve ELF auxiliary vector of + * another process. + */ +static int +sysctl_kern_proc_auxv(SYSCTL_HANDLER_ARGS) +{ + int *name = (int*) arg1; + u_int namelen = arg2; + struct proc *p; + size_t vsize; + char **auxv; + int error; + + if (namelen != 1) + return (EINVAL); + + p = pfind((pid_t)name[0]); + if (p == NULL) + return (ESRCH); + if (p->p_flag & P_WEXIT) { + PROC_UNLOCK(p); + return (ESRCH); + } + if ((error = p_cansee(curthread, p)) != 0) { + PROC_UNLOCK(p); + return (error); + } + if (p->p_flag & P_SYSTEM) { + PROC_UNLOCK(p); + return (0); + } + _PHOLD(p); + PROC_UNLOCK(p); + error = get_proc_vector(curthread, p, &auxv, &vsize, PROC_AUX); + PRELE(p); + if (error == 0) { +#ifdef COMPAT_FREEBSD32 + if (SV_PROC_FLAG(p, SV_ILP32) != 0) + error = SYSCTL_OUT(req, auxv, vsize * + sizeof(Elf32_Auxinfo)); + else +#endif + error = SYSCTL_OUT(req, auxv, vsize * sizeof(Elf_Auxinfo)); + free(auxv, M_TEMP); + } + return (error); +} + +/* * This sysctl allows a process to retrieve the path of the executable for * itself or another process. */ @@ -2026,6 +2345,14 @@ static SYSCTL_NODE(_kern_proc, KERN_PROC_ARGS, args, CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE, sysctl_kern_proc_args, "Process argument list"); +static SYSCTL_NODE(_kern_proc, KERN_PROC_ENV, env, + CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE, + sysctl_kern_proc_env, "Process environment"); + +static SYSCTL_NODE(_kern_proc, KERN_PROC_AUXV, auxv, + CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE, + sysctl_kern_proc_auxv, "Process ELF auxiliary vector"); + static SYSCTL_NODE(_kern_proc, KERN_PROC_PATHNAME, pathname, CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_kern_proc_pathname, "Process executable path"); diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c index 8832d3d..2f8ec1a 100644 --- a/sys/compat/linprocfs/linprocfs.c +++ b/sys/compat/linprocfs/linprocfs.c @@ -919,150 +919,6 @@ linprocfs_doprocroot(PFS_FILL_ARGS) return (0); } -#define MAX_ARGV_STR 512 /* Max number of argv-like strings */ -#define UIO_CHUNK_SZ 256 /* Max chunk size (bytes) for uiomove */ - -static int -linprocfs_doargv(struct thread *td, struct proc *p, struct sbuf *sb, - void (*resolver)(const struct ps_strings, u_long *, int *)) -{ - struct iovec iov; - struct uio tmp_uio; - struct ps_strings pss; - int ret, i, n_elements, elm_len; - u_long addr, pbegin; - char **env_vector, *envp; - char env_string[UIO_CHUNK_SZ]; -#ifdef COMPAT_FREEBSD32 - struct freebsd32_ps_strings pss32; - uint32_t *env_vector32; -#endif - -#define UIO_HELPER(uio, iov, base, len, cnt, offset, sz, flg, rw, td) \ -do { \ - iov.iov_base = (caddr_t)(base); \ - iov.iov_len = (len); \ - uio.uio_iov = &(iov); \ - uio.uio_iovcnt = (cnt); \ - uio.uio_offset = (off_t)(offset); \ - uio.uio_resid = (sz); \ - uio.uio_segflg = (flg); \ - uio.uio_rw = (rw); \ - uio.uio_td = (td); \ -} while (0) - - env_vector = malloc(sizeof(char *) * MAX_ARGV_STR, M_TEMP, M_WAITOK); - -#ifdef COMPAT_FREEBSD32 - env_vector32 = NULL; - if (SV_PROC_FLAG(p, SV_ILP32) != 0) { - env_vector32 = malloc(sizeof(*env_vector32) * MAX_ARGV_STR, - M_TEMP, M_WAITOK); - elm_len = sizeof(int32_t); - envp = (char *)env_vector32; - - UIO_HELPER(tmp_uio, iov, &pss32, sizeof(pss32), 1, - (off_t)(p->p_sysent->sv_psstrings), - sizeof(pss32), UIO_SYSSPACE, UIO_READ, td); - ret = proc_rwmem(p, &tmp_uio); - if (ret != 0) - goto done; - pss.ps_argvstr = PTRIN(pss32.ps_argvstr); - pss.ps_nargvstr = pss32.ps_nargvstr; - pss.ps_envstr = PTRIN(pss32.ps_envstr); - pss.ps_nenvstr = pss32.ps_nenvstr; - } else { -#endif - elm_len = sizeof(char *); - envp = (char *)env_vector; - - UIO_HELPER(tmp_uio, iov, &pss, sizeof(pss), 1, - (off_t)(p->p_sysent->sv_psstrings), - sizeof(pss), UIO_SYSSPACE, UIO_READ, td); - ret = proc_rwmem(p, &tmp_uio); - if (ret != 0) - goto done; -#ifdef COMPAT_FREEBSD32 - } -#endif - - /* Get the array address and the number of elements */ - resolver(pss, &addr, &n_elements); - - /* Consistent with lib/libkvm/kvm_proc.c */ - if (n_elements > MAX_ARGV_STR) { - ret = E2BIG; - goto done; - } - - UIO_HELPER(tmp_uio, iov, envp, n_elements * elm_len, 1, - (vm_offset_t)(addr), iov.iov_len, UIO_SYSSPACE, UIO_READ, td); - ret = proc_rwmem(p, &tmp_uio); - if (ret != 0) - goto done; -#ifdef COMPAT_FREEBSD32 - if (env_vector32 != NULL) { - for (i = 0; i < n_elements; i++) - env_vector[i] = PTRIN(env_vector32[i]); - } -#endif - - /* Now we can iterate through the list of strings */ - for (i = 0; i < n_elements; i++) { - pbegin = (vm_offset_t)env_vector[i]; - for (;;) { - UIO_HELPER(tmp_uio, iov, env_string, sizeof(env_string), - 1, pbegin, iov.iov_len, UIO_SYSSPACE, UIO_READ, td); - ret = proc_rwmem(p, &tmp_uio); - if (ret != 0) - goto done; - - if (!strvalid(env_string, UIO_CHUNK_SZ)) { - /* - * We didn't find the end of the string. - * Add the string to the buffer and move - * the pointer. But do not allow strings - * of unlimited length. - */ - sbuf_bcat(sb, env_string, UIO_CHUNK_SZ); - if (sbuf_len(sb) >= ARG_MAX) { - ret = E2BIG; - goto done; - } - pbegin += UIO_CHUNK_SZ; - } else { - sbuf_cat(sb, env_string); - break; - } - } - sbuf_bcat(sb, "", 1); - } -#undef UIO_HELPER - -done: - free(env_vector, M_TEMP); -#ifdef COMPAT_FREEBSD32 - free(env_vector32, M_TEMP); -#endif - return (ret); -} - -static void -ps_string_argv(const struct ps_strings ps, u_long *addr, int *n) -{ - - *addr = (u_long) ps.ps_argvstr; - *n = ps.ps_nargvstr; -} - -static void -ps_string_env(const struct ps_strings ps, u_long *addr, int *n) -{ - - *addr = (u_long) ps.ps_envstr; - *n = ps.ps_nenvstr; -} - /* * Filler function for proc/pid/cmdline */ @@ -1090,9 +946,15 @@ linprocfs_doproccmdline(PFS_FILL_ARGS) PROC_UNLOCK(p); return (0); } + + if ((p->p_flag & (P_WEXIT | P_SYSTEM)) != 0) { + PROC_UNLOCK(p); + return (0); + } + PROC_UNLOCK(p); - ret = linprocfs_doargv(td, p, sb, ps_string_argv); + ret = proc_getargv(td, p, sb, ARG_MAX); return (ret); } @@ -1118,9 +980,15 @@ linprocfs_doprocenviron(PFS_FILL_ARGS) PROC_UNLOCK(p); return (0); } + + if ((p->p_flag & (P_WEXIT | P_SYSTEM)) != 0) { + PROC_UNLOCK(p); + return (0); + } + PROC_UNLOCK(p); - ret = linprocfs_doargv(td, p, sb, ps_string_env); + ret = proc_getenvv(td, p, sb, ARG_MAX); return (ret); }