On Fri, Sep 24, 2010 at 04:24:18PM +0400, pluknet wrote: > 2010/9/24 Kostik Belousov <[email protected]>: > > On Fri, Sep 24, 2010 at 02:17:29PM +0400, pluknet wrote: > >> On 16 September 2010 11:56, Dag-Erling Smorgrav <[email protected]> wrote: > >> > Author: des > >> > Date: Thu Sep 16 07:56:34 2010 > >> > New Revision: 212723 > >> > URL: http://svn.freebsd.org/changeset/base/212723 > >> > > >> > Log: > >> > Implement proc/$$/environment. > >> > > >> [...] > >> > >> > /* > >> > * Filler function for proc/pid/environ > >> > */ > >> > static int > >> > linprocfs_doprocenviron(PFS_FILL_ARGS) > >> > { > >> > + int ret; > >> > > >> > - sbuf_printf(sb, "doprocenviron\n%c", '\0'); > >> > - return (0); > >> > + PROC_LOCK(p); > >> > >> With this change I observe the following sleepable after non-sleepable: > >> > [LOR there] > >> > >> > >> > + > >> > + if ((ret = p_cansee(td, p)) != 0) { > >> > + PROC_UNLOCK(p); > >> > + return ret; > >> > + } > >> > + > >> > + ret = linprocfs_doargv(td, p, sb, ps_string_env); > >> > + PROC_UNLOCK(p); > >> > + return (ret); > >> > } > > > > This is easy to fix, isn't it ? But there seems to be much more nits. > > > > First, allocating 512 * sizeof(char *)-byte object on the stack is not > > good. > > > > Second, the initialization of iov_len for reading the array > > of string pointers misses '* sizeof(char *)'. > > > > And third (probably fatal) is the lack of checks that the end of > > array and each string fits into the user portion of the map. I do not > > see why addr that already has u_long type is casted to u_long. Also, > > VM_MIN_ADDRESS, VM_MAXUSER_ADDRESS constants are for the native host > > FreeBSD ABI, they may differ from the target process limits. > > > > Thanks for quick reaction. > > As for the latter, something doesn't quite work here. > I see EFAULT against i386 process running on amd64.
Right, the COMPAT_FREEBSD32 case should properly convert both
struct ps_strings and array of pointers to the host structures.
So this is fourth issue.
diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c
index c7fe158..db837e2 100644
--- a/sys/compat/linprocfs/linprocfs.c
+++ b/sys/compat/linprocfs/linprocfs.c
@@ -96,6 +96,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
@@ -951,13 +955,14 @@ linprocfs_doargv(struct thread *td, struct proc *p,
struct sbuf *sb,
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, found_end;
+ u_long addr, pbegin;
+ char **env_vector;
char env_string[UIO_CHUNK_SZ];
- char *pbegin;
-
-
+#ifdef COMPAT_FREEBSD32
+ struct freebsd32_ps_strings pss32;
+ uint32_t ptr;
+#endif
#define UIO_HELPER(uio, iov, base, len, cnt, offset, sz, flg, rw, td)
\
do { \
@@ -971,63 +976,112 @@ do {
\
uio.uio_rw = (rw); \
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);
-
- ret = proc_rwmem(p, &tmp_uio);
- if (ret != 0)
- return ret;
+#define VALID_USER_ADDR(addr)
\
+ ((addr) >= p->p_sysent->sv_minuser && (addr) < p->p_sysent->sv_maxuser)
+
+ env_vector = malloc(sizeof(char *) * MAX_ARGV_STR, M_TEMP, M_WAITOK);
+
+#ifdef COMPAT_FREEBSD32
+ if ((p->p_sysent->sv_flags & SV_ILP32) != 0) {
+ 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;
+
+ elm_len = MAX_ARGV_STR * sizeof(int32_t);
+ } else {
+#endif
+ 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;
+
+ elm_len = MAX_ARGV_STR * sizeof(char *);
+#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 || !VALID_USER_ADDR(addr) ||
+ !VALID_USER_ADDR(addr + elm_len)) {
+ ret = EFAULT;
+ goto done;
}
- UIO_HELPER(tmp_uio, iov, env_vector, MAX_ARGV_STR, 1,
- (vm_offset_t)(addr), iov.iov_len, UIO_SYSSPACE, UIO_READ, td);
+#ifdef COMPAT_FREEBSD32
+ if ((p->p_sysent->sv_flags & SV_ILP32) != 0) {
+ for (i = 0; i < MAX_ARGV_STR; i++) {
+ UIO_HELPER(tmp_uio, iov, &ptr, sizeof(ptr), 1,
+ (vm_offset_t)(addr + i * sizeof(uint32_t)),
+ iov.iov_len, UIO_SYSSPACE, UIO_READ, td);
+ ret = proc_rwmem(p, &tmp_uio);
+ if (ret != 0)
+ goto done;
+ env_vector[i] = PTRIN(ptr);
+ }
+ } else {
+#endif
+ UIO_HELPER(tmp_uio, iov, env_vector, 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
+ }
+#endif
- ret = proc_rwmem(p, &tmp_uio);
- if (ret != 0)
- return ret;
/* 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) {
+ pbegin = (vm_offset_t)env_vector[i];
+ while (!found_end) {
+ if (!VALID_USER_ADDR(pbegin) ||
+ !VALID_USER_ADDR(pbegin + sizeof(env_string))) {
+ ret = EFAULT;
+ goto done;
+ }
UIO_HELPER(tmp_uio, iov, env_string, sizeof(env_string), 1,
- (vm_offset_t) pbegin, iov.iov_len, UIO_SYSSPACE,
- UIO_READ, td);
+ pbegin, iov.iov_len, UIO_SYSSPACE, UIO_READ, td);
- ret = proc_rwmem(p, &tmp_uio);
- if (ret != 0)
- return ret;
+ ret = proc_rwmem(p, &tmp_uio);
+ if (ret != 0) {
+ ret = 0;
+ goto done;
+ }
- if (!strvalid(env_string, UIO_CHUNK_SZ)) {
+ if (!strvalid(env_string, UIO_CHUNK_SZ)) {
/*
- * We didn't find the end of the string
+ * We didn't find the end of the string.
* Add the string to the buffer and move
- * the pointer
+ * the pointer.
*/
sbuf_bcat(sb, env_string, UIO_CHUNK_SZ);
- pbegin = &(*pbegin) + UIO_CHUNK_SZ;
- } else {
+ pbegin += UIO_CHUNK_SZ;
+ } else
found_end = 1;
- }
}
sbuf_printf(sb, "%s", env_string);
}
+#undef VALID_USER_ADDR
#undef UIO_HELPER
- return (0);
+done:
+ free(env_vector, M_TEMP);
+ return (ret);
}
static void
@@ -1052,9 +1106,11 @@ linprocfs_doprocenviron(PFS_FILL_ARGS)
PROC_UNLOCK(p);
return ret;
}
+ _PHOLD(p);
+ PROC_UNLOCK(p);
ret = linprocfs_doargv(td, p, sb, ps_string_env);
- PROC_UNLOCK(p);
+ PRELE(p);
return (ret);
}
pgpFqHhzHhOnI.pgp
Description: PGP signature
