Add new sysctls, KERN_PROC_ENV and KERN_PROC_AUXV, to return
environment strings and ELF auxiliary vectors from a process stack. Make sysctl_kern_proc_args to read not cached arguments from the process stack. Export proc_getargv() and proc_getenvv() so they can be reused by procfs and linprocfs. Suggested by: kib Reviewed by: kib Discussed with: kib, rwatson, jilles Tested by: pho MFC after: 2 weeks
This commit is contained in:
parent
c0ba290b5f
commit
c5cfcb1c19
@ -41,6 +41,8 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/elf.h>
|
||||
#include <sys/exec.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/limits.h>
|
||||
#include <sys/lock.h>
|
||||
@ -50,6 +52,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/refcount.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/sysent.h>
|
||||
@ -1358,6 +1361,289 @@ 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));
|
||||
}
|
||||
|
||||
static int
|
||||
proc_read_string(struct thread *td, struct proc *p, const char *sptr, char *buf,
|
||||
size_t len)
|
||||
{
|
||||
size_t i;
|
||||
int error;
|
||||
|
||||
error = proc_read_mem(td, p, (vm_offset_t)sptr, buf, len);
|
||||
/*
|
||||
* Reading the chunk may validly return EFAULT if the string is shorter
|
||||
* than the chunk and is aligned at the end of the page, assuming the
|
||||
* next page is not mapped. So if EFAULT is returned do a fallback to
|
||||
* one byte read loop.
|
||||
*/
|
||||
if (error == EFAULT) {
|
||||
for (i = 0; i < len; i++, buf++, sptr++) {
|
||||
error = proc_read_mem(td, p, (vm_offset_t)sptr, buf, 1);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (*buf == '\0')
|
||||
break;
|
||||
}
|
||||
error = 0;
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
#define PROC_AUXV_MAX 256 /* Safety 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;
|
||||
if (vsize > ARG_MAX)
|
||||
return (ENOEXEC);
|
||||
size = vsize * sizeof(int32_t);
|
||||
break;
|
||||
case PROC_ENV:
|
||||
vptr = (vm_offset_t)PTRIN(pss.ps_envstr);
|
||||
vsize = pss.ps_nenvstr;
|
||||
if (vsize > ARG_MAX)
|
||||
return (ENOEXEC);
|
||||
size = vsize * sizeof(int32_t);
|
||||
break;
|
||||
case PROC_AUX:
|
||||
vptr = (vm_offset_t)PTRIN(pss.ps_envstr) +
|
||||
(pss.ps_nenvstr + 1) * sizeof(int32_t);
|
||||
if (vptr % 4 != 0)
|
||||
return (ENOEXEC);
|
||||
for (ptr = vptr, 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)
|
||||
return (ENOEXEC);
|
||||
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;
|
||||
if (vsize > ARG_MAX)
|
||||
return (ENOEXEC);
|
||||
size = vsize * sizeof(char *);
|
||||
break;
|
||||
case PROC_ENV:
|
||||
vptr = (vm_offset_t)pss.ps_envstr;
|
||||
vsize = pss.ps_nenvstr;
|
||||
if (vsize > ARG_MAX)
|
||||
return (ENOEXEC);
|
||||
size = vsize * sizeof(char *);
|
||||
break;
|
||||
case PROC_AUX:
|
||||
/*
|
||||
* The aux array is just above env array on the stack. Check
|
||||
* that the address is naturally aligned.
|
||||
*/
|
||||
vptr = (vm_offset_t)pss.ps_envstr + (pss.ps_nenvstr + 1)
|
||||
* sizeof(char *);
|
||||
#if __ELF_WORD_SIZE == 64
|
||||
if (vptr % sizeof(uint64_t) != 0)
|
||||
#else
|
||||
if (vptr % sizeof(uint32_t) != 0)
|
||||
#endif
|
||||
return (ENOEXEC);
|
||||
/*
|
||||
* We count the array size reading the aux vectors from the
|
||||
* stack until AT_NULL vector is returned. So (to keep the code
|
||||
* simple) we read the process stack twice: the first time here
|
||||
* to find the size and the second time when copying the vectors
|
||||
* to the allocated proc_vector.
|
||||
*/
|
||||
for (ptr = vptr, 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 the PROC_AUXV_MAX entries are iterated over, and we have
|
||||
* not reached AT_NULL, it is most likely we are reading wrong
|
||||
* data: either the process doesn't have auxv array or data has
|
||||
* been modified. Return the error in this case.
|
||||
*/
|
||||
if (aux.a_type != AT_NULL)
|
||||
return (ENOEXEC);
|
||||
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);
|
||||
if (proc_vector == NULL)
|
||||
return (ENOMEM);
|
||||
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)
|
||||
{
|
||||
size_t done, len, vsize;
|
||||
int error, i;
|
||||
char **proc_vector, *sptr;
|
||||
char pss_string[GET_PS_STRINGS_CHUNK_SZ];
|
||||
|
||||
PROC_ASSERT_HELD(p);
|
||||
|
||||
/*
|
||||
* We are not going to read more than 2 * (PATH_MAX + ARG_MAX) bytes.
|
||||
*/
|
||||
if (nchr > 2 * (PATH_MAX + ARG_MAX))
|
||||
nchr = 2 * (PATH_MAX + ARG_MAX);
|
||||
|
||||
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++) {
|
||||
/*
|
||||
* The program may have scribbled into its argv array, e.g. to
|
||||
* remove some arguments. If that has happened, break out
|
||||
* before trying to read from NULL.
|
||||
*/
|
||||
if (proc_vector[i] == NULL)
|
||||
break;
|
||||
for (sptr = proc_vector[i]; ; sptr += GET_PS_STRINGS_CHUNK_SZ) {
|
||||
error = proc_read_string(td, p, sptr, 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
|
||||
@ -1371,7 +1657,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);
|
||||
@ -1391,11 +1678,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);
|
||||
|
||||
@ -1415,6 +1715,95 @@ sysctl_kern_proc_args(SYSCTL_HANDLER_ARGS)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) != 0) {
|
||||
PROC_UNLOCK(p);
|
||||
return (ESRCH);
|
||||
}
|
||||
if ((error = p_candebug(curthread, p)) != 0) {
|
||||
PROC_UNLOCK(p);
|
||||
return (error);
|
||||
}
|
||||
if ((p->p_flag & P_SYSTEM) != 0) {
|
||||
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) != 0) {
|
||||
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.
|
||||
@ -2035,6 +2424,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");
|
||||
|
||||
|
@ -168,6 +168,7 @@ struct p_sched;
|
||||
struct proc;
|
||||
struct procdesc;
|
||||
struct racct;
|
||||
struct sbuf;
|
||||
struct sleepqueue;
|
||||
struct td_sched;
|
||||
struct thread;
|
||||
@ -844,6 +845,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);
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user