When getting the program argument or environement string, we previously

assumed that all the strings were stored in a row, separated by NUL chars,
 at the address pointed bu argv[0] (or envp[0]).

This was wrong: if the program changed argvs[0], we still read the
first string correctly, but the next strings did contain unexpected data.

The fix: read the whole argv (or envp) array, then copy the string one by
one, using their addresses in argv (or agrp)
This commit is contained in:
manu 2006-09-10 05:46:02 +00:00
parent ed679e385b
commit bdfbd98ac1
1 changed files with 74 additions and 50 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: init_sysctl.c,v 1.83 2006/09/08 20:58:57 elad Exp $ */
/* $NetBSD: init_sysctl.c,v 1.84 2006/09/10 05:46:02 manu Exp $ */
/*-
* Copyright (c) 2003 The NetBSD Foundation, Inc.
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: init_sysctl.c,v 1.83 2006/09/08 20:58:57 elad Exp $");
__KERNEL_RCSID(0, "$NetBSD: init_sysctl.c,v 1.84 2006/09/10 05:46:02 manu Exp $");
#include "opt_sysv.h"
#include "opt_multiprocessor.h"
@ -2334,13 +2334,13 @@ sysctl_kern_proc_args(SYSCTLFN_ARGS)
{
struct ps_strings pss;
struct proc *p;
size_t len, upper_bound, xlen, i;
size_t len, i;
struct uio auio;
struct iovec aiov;
vaddr_t argv;
pid_t pid;
int nargv, type, error;
char *arg;
char **argv = NULL;
char *tmp;
struct vmspace *vmspace;
vaddr_t psstr_addr;
@ -2468,41 +2468,56 @@ sysctl_kern_proc_args(SYSCTLFN_ARGS)
return (EINVAL);
}
auio.uio_offset = (off_t)(unsigned long)tmp;
aiov.iov_base = &argv;
aiov.iov_len = sizeof(argv);
#ifdef COMPAT_NETBSD32
if (p->p_flag & P_32)
len = sizeof(netbsd32_charp) * nargv;
else
#endif
len = sizeof(char *) * nargv;
argv = malloc(len, M_TEMP, M_WAITOK);
aiov.iov_base = argv;
aiov.iov_len = len;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_resid = sizeof(argv);
auio.uio_offset = (off_t)(unsigned long)tmp;
auio.uio_resid = len;
auio.uio_rw = UIO_READ;
UIO_SETUP_SYSSPACE(&auio);
error = uvm_io(&vmspace->vm_map, &auio);
if (error)
goto done;
#ifdef COMPAT_NETBSD32
/*
* Here we get a 32 bit pointer that has to be converted,
* otherwise we get garbage in the 32 higher bits
* Now copy each string.
*/
if (p->p_flag & P_32)
argv = (vaddr_t)NETBSD32PTR64(argv);
#endif
len = 0; /* bytes written to user buffer */
for (i = 0; i < nargv; i++) {
int finished = 0;
vaddr_t base;
size_t xlen;
int j;
#ifdef COMPAT_NETBSD32
if (p->p_flag & P_32) {
netbsd32_charp *argv32;
argv32 = (netbsd32_charp *)argv;
base = (vaddr_t)NETBSD32PTR64(argv32[i]);
} else
#endif
base = (vaddr_t)argv[i];
while (!finished) {
xlen = PAGE_SIZE - (base & PAGE_MASK);
/*
* Now copy in the actual argument vector, one page at a time,
* since we don't know how long the vector is (though, we do
* know how many NUL-terminated strings are in the vector).
*/
len = 0;
upper_bound = *oldlenp;
for (; nargv != 0 && len < upper_bound; len += xlen) {
aiov.iov_base = arg;
aiov.iov_len = PAGE_SIZE;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = argv + len;
xlen = PAGE_SIZE - ((argv + len) & PAGE_MASK);
auio.uio_offset = base;
auio.uio_resid = xlen;
auio.uio_rw = UIO_READ;
UIO_SETUP_SYSSPACE(&auio);
@ -2510,30 +2525,39 @@ sysctl_kern_proc_args(SYSCTLFN_ARGS)
if (error)
goto done;
for (i = 0; i < xlen && nargv != 0; i++) {
if (arg[i] == '\0')
nargv--; /* one full string */
/* Look for the end of the string */
for (j = 0; j < xlen; j++) {
if (arg[j] == '\0') {
xlen = j + 1;
finished = 1;
break;
}
}
/*
* Make sure we don't copyout past the end of the user's
* buffer.
*/
if (len + i > upper_bound)
i = upper_bound - len;
/* Check for user buffer overflow */
if (len + xlen > *oldlenp) {
finished = 1;
if (len > *oldlenp)
xlen = 0;
else
xlen = *oldlenp - len;
}
error = copyout(arg, (char *)oldp + len, i);
/* Copyout the page */
error = copyout(arg, (char *)oldp + len, xlen);
if (error)
break;
goto done;
if (nargv == 0) {
len += i;
break;
len += xlen;
base += xlen;
}
}
*oldlenp = len;
done:
if (argv != NULL)
free(argv, M_TEMP);
uvmspace_free(vmspace);
free(arg, M_TEMP);