1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-01 08:27:59 +00:00

In linprocfs_doargv():

- handle compat32 processes;
- remove the checks for copied in addresses to belong into valid
  usermode range, proc_rwmem() does this;
- simplify loop reading single string, limit the total amount of strings
  collected by ARG_MAX bytes;
- correctly add '\0' at the end of each copied string;
- fix style.

In linprocfs_doprocenviron():
- unlock the process before calling copyin code [1]. The process is held
  by pseudofs.

In linprocfs_doproccmdline:
- use linprocfs_doargv() to handle !curproc case for which p_args is not cached.

Reported by:		plulnet [1]
Tested by:		pluknet
Approved by:		des (linprocfs maintainer, previous
				version of the patch)
MFC after:		3 weeks
This commit is contained in:
Konstantin Belousov 2010-09-28 11:32:17 +00:00
parent 15d98d9738
commit 6c82a991c3
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=213246

View File

@ -61,6 +61,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/ptrace.h>
#include <sys/resourcevar.h>
#include <sys/sbuf.h>
#include <sys/sem.h>
@ -96,6 +97,10 @@ __FBSDID("$FreeBSD$");
#include <machine/md_var.h>
#endif /* __i386__ || __amd64__ */
#ifdef COMPAT_FREEBSD32
#include <compat/freebsd32/freebsd32_util.h>
#endif
#ifdef COMPAT_LINUX32 /* XXX */
#include <machine/../linux32/linux.h>
#else
@ -886,78 +891,24 @@ linprocfs_doprocroot(PFS_FILL_ARGS)
return (0);
}
/*
* Filler function for proc/pid/cmdline
*/
static int
linprocfs_doproccmdline(PFS_FILL_ARGS)
{
struct ps_strings pstr;
char **ps_argvstr;
int error, i;
/*
* If we are using the ps/cmdline caching, use that. Otherwise
* revert back to the old way which only implements full cmdline
* for the currept process and just p->p_comm for all other
* processes.
* Note that if the argv is no longer available, we deliberately
* don't fall back on p->p_comm or return an error: the authentic
* Linux behaviour is to return zero-length in this case.
*/
PROC_LOCK(p);
if (p->p_args && p_cansee(td, p) == 0) {
sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length);
PROC_UNLOCK(p);
} else if (p != td->td_proc) {
PROC_UNLOCK(p);
sbuf_printf(sb, "%.*s", MAXCOMLEN, p->p_comm);
} else {
PROC_UNLOCK(p);
error = copyin((void *)p->p_sysent->sv_psstrings, &pstr,
sizeof(pstr));
if (error)
return (error);
if (pstr.ps_nargvstr > ARG_MAX)
return (E2BIG);
ps_argvstr = malloc(pstr.ps_nargvstr * sizeof(char *),
M_TEMP, M_WAITOK);
error = copyin((void *)pstr.ps_argvstr, ps_argvstr,
pstr.ps_nargvstr * sizeof(char *));
if (error) {
free(ps_argvstr, M_TEMP);
return (error);
}
for (i = 0; i < pstr.ps_nargvstr; i++) {
sbuf_copyin(sb, ps_argvstr[i], 0);
sbuf_printf(sb, "%c", '\0');
}
free(ps_argvstr, M_TEMP);
}
return (0);
}
extern int proc_rwmem(struct proc *p, struct uio *uio);
#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 *))
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, found_end;
u_long addr;
char* env_vector[MAX_ARGV_STR];
int ret, i, n_elements, elm_len;
u_long addr, pbegin;
char **env_vector, *envp;
char env_string[UIO_CHUNK_SZ];
char *pbegin;
#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 { \
@ -972,62 +923,108 @@ do { \
uio.uio_td = (td); \
} while (0)
UIO_HELPER(tmp_uio, iov, &pss, sizeof(struct ps_strings), 1,
(off_t)(p->p_sysent->sv_psstrings), sizeof(struct ps_strings),
UIO_SYSSPACE, UIO_READ, td);
env_vector = malloc(sizeof(char *) * MAX_ARGV_STR, M_TEMP, M_WAITOK);
ret = proc_rwmem(p, &tmp_uio);
if (ret != 0)
return ret;
#ifdef COMPAT_FREEBSD32
env_vector32 = NULL;
if ((p->p_sysent->sv_flags & 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 || (u_long)addr < VM_MIN_ADDRESS ||
(u_long)addr >= VM_MAXUSER_ADDRESS) {
/* What error code should we return? */
return 0;
if (n_elements > MAX_ARGV_STR) {
ret = E2BIG;
goto done;
}
UIO_HELPER(tmp_uio, iov, env_vector, MAX_ARGV_STR, 1,
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)
return ret;
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++) {
found_end = 0;
pbegin = env_vector[i];
while(!found_end) {
UIO_HELPER(tmp_uio, iov, env_string, sizeof(env_string), 1,
(vm_offset_t) pbegin, iov.iov_len, UIO_SYSSPACE,
UIO_READ, td);
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)
return ret;
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
*/
sbuf_bcat(sb, env_string, UIO_CHUNK_SZ);
pbegin = &(*pbegin) + 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 {
found_end = 1;
sbuf_cat(sb, env_string);
break;
}
}
sbuf_printf(sb, "%s", env_string);
sbuf_bcat(sb, "", 1);
}
#undef UIO_HELPER
return (0);
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
@ -1038,6 +1035,30 @@ ps_string_env(const struct ps_strings ps, u_long *addr, int *n)
*n = ps.ps_nenvstr;
}
/*
* Filler function for proc/pid/cmdline
*/
static int
linprocfs_doproccmdline(PFS_FILL_ARGS)
{
int ret;
PROC_LOCK(p);
if ((ret = p_cansee(td, p)) != 0) {
PROC_UNLOCK(p);
return (ret);
}
if (p->p_args != NULL) {
sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length);
PROC_UNLOCK(p);
return (0);
}
PROC_UNLOCK(p);
ret = linprocfs_doargv(td, p, sb, ps_string_argv);
return (ret);
}
/*
* Filler function for proc/pid/environ
*/
@ -1047,14 +1068,13 @@ linprocfs_doprocenviron(PFS_FILL_ARGS)
int ret;
PROC_LOCK(p);
if ((ret = p_cansee(td, p)) != 0) {
PROC_UNLOCK(p);
return ret;
return (ret);
}
PROC_UNLOCK(p);
ret = linprocfs_doargv(td, p, sb, ps_string_env);
PROC_UNLOCK(p);
return (ret);
}