diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libkvm/common/kvm_getcmd.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libkvm/common/kvm_getcmd.c')
-rw-r--r-- | usr/src/lib/libkvm/common/kvm_getcmd.c | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/usr/src/lib/libkvm/common/kvm_getcmd.c b/usr/src/lib/libkvm/common/kvm_getcmd.c new file mode 100644 index 0000000000..66b3d5c70d --- /dev/null +++ b/usr/src/lib/libkvm/common/kvm_getcmd.c @@ -0,0 +1,422 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <kvm.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <kvm.h> +#include <strings.h> +#include <sys/types32.h> + +#define _SYSCALL32 + +/* + * VERSION FOR MACHINES WITH STACKS GROWING DOWNWARD IN MEMORY + * + * On program entry, the top of the stack frame looks like this: + * + * hi: |-----------------------| + * | unspecified | + * |-----------------------|+ + * | : | \ + * | arg and env strings | > no more than NCARGS bytes + * | : | / + * |-----------------------|+ + * | unspecified | + * |-----------------------| + * | null auxiliary vector | + * |-----------------------| + * | auxiliary vector | + * | (2-word entries) | + * | : | + * |-----------------------| + * | (char *)0 | + * |-----------------------| + * | ptrs to env strings | + * | : | + * |-----------------------| + * | (char *)0 | + * |-----------------------| + * | ptrs to arg strings | + * | (argc = # of ptrs) | + * | : | + * |-----------------------| + * | argc | + * low: |-----------------------| + */ + +#define RoundUp(v, t) (((v) + sizeof (t) - 1) & ~(sizeof (t) - 1)) + +static int +kvm_getcmd32(kvm_t *kd, + struct proc *p, struct user *u, char ***arg, char ***env) +{ +#if defined(_LP64) || defined(lint) + size_t size32; + void *stack32; + int i, argc, envc; + int auxc = 0; + size_t asize, esize; + char **argv = NULL; + char **envp = NULL; + size_t strpoolsz; + int aptrcount; + int eptrcount; + caddr_t stackp; + ptrdiff_t reloc; + char *str; + + /* + * Bring the entire stack into memory first, size it + * as an LP64 user stack, then allocate and copy into + * the buffer(s) to be returned to the caller. + */ + size32 = (size_t)p->p_usrstack - (size_t)u->u_argv; + if ((stack32 = malloc(size32)) == NULL) + return (-1); + if (kvm_uread(kd, (uintptr_t)u->u_argv, stack32, size32) != size32) { + free(stack32); + return (-1); + } + + /* + * Find the interesting sizes of a 32-bit stack. + */ + argc = u->u_argc; + stackp = (caddr_t)stack32 + ((1 + argc) * sizeof (caddr32_t)); + + for (envc = 0; *(caddr32_t *)stackp; envc++) { + stackp += sizeof (caddr32_t); + if ((stackp - (caddr_t)stack32) >= size32) { + free(stack32); + return (-1); + } + } + + if (u->u_auxv[0].a_type != AT_NULL) { + stackp += sizeof (caddr32_t); + for (auxc = 0; *(int32_t *)stackp; auxc++) { + stackp += 2 * sizeof (caddr32_t); + if ((stackp - (caddr_t)stack32) >= size32) { + free(stack32); + return (-1); + } + } + auxc++; /* terminating AT_NULL record */ + } + + /* + * Compute the sizes of the stuff we're going to allocate or copy. + */ + eptrcount = (envc + 1) + 2 * auxc; + aptrcount = (argc + 1) + eptrcount; + strpoolsz = size32 - aptrcount * sizeof (caddr32_t); + + asize = aptrcount * sizeof (uintptr_t) + RoundUp(strpoolsz, uintptr_t); + if (arg && (argv = calloc(1, asize + sizeof (uintptr_t))) == NULL) { + free(stack32); + return (-1); + } + + esize = eptrcount * sizeof (uintptr_t) + RoundUp(strpoolsz, uintptr_t); + if (env && (envp = calloc(1, esize + sizeof (uintptr_t))) == NULL) { + if (argv) + free(argv); + free(stack32); + return (-1); + } + + /* + * Walk up the 32-bit stack, filling in the 64-bit argv and envp + * as we go. + */ + stackp = (caddr_t)stack32; + + /* + * argument vector + */ + if (argv) { + for (i = 0; i < argc; i++) { + argv[i] = (char *)(uintptr_t)(*(caddr32_t *)stackp); + stackp += sizeof (caddr32_t); + } + argv[argc] = 0; + stackp += sizeof (caddr32_t); + } else + stackp += (1 + argc) * sizeof (caddr32_t); + + /* + * environment + */ + if (envp) { + for (i = 0; i < envc; i++) { + envp[i] = (char *)(uintptr_t)(*(caddr32_t *)stackp); + stackp += sizeof (caddr32_t); + } + envp[envc] = 0; + stackp += sizeof (caddr32_t); + } else + stackp += (1 + envc) * sizeof (caddr32_t); + + /* + * auxiliary vector (skip it..) + */ + stackp += auxc * (sizeof (int32_t) + sizeof (uint32_t)); + + /* + * Copy the string pool, untranslated + */ + if (argv) + (void) memcpy(argv + aptrcount, (void *)stackp, strpoolsz); + if (envp) + (void) memcpy(envp + eptrcount, (void *)stackp, strpoolsz); + + free(stack32); + + /* + * Relocate the pointers to point at the newly allocated space. + * Use the same algorithms as kvm_getcmd to handle naughty + * changes to the argv and envp arrays. + */ + if (argv) { + char *argv_null = (char *)argv + asize; + + reloc = (char *)(argv + aptrcount) - (char *) + ((caddr_t)u->u_argv + aptrcount * sizeof (caddr32_t)); + + for (i = 0; i < argc; i++) + if (argv[i] != NULL) { + str = (argv[i] += reloc); + if (str < (char *)argv || + str >= (char *)argv + asize) + argv[i] = argv_null; + } + + *arg = argv; + } + + if (envp) { + char *envp_null = (char *)envp + esize; + char *last_str; + + reloc = (char *)(envp + eptrcount) - (char *) + ((caddr_t)u->u_envp + eptrcount * sizeof (caddr32_t)); + + last_str = (char *)((size_t)u->u_argv + + (1 + argc) * sizeof (caddr32_t) + reloc); + if (last_str < (char *)envp || + last_str >= (char *)envp + esize) + last_str = envp_null; + + for (i = 0; i < envc; i++) { + str = (envp[i] += reloc); + if (str < (char *)envp || + str >= (char *)envp + esize) { + if (last_str != envp_null) + envp[i] = (char *)((size_t)last_str + + strlen(last_str) + 1); + else + envp[i] = envp_null; + } + last_str = envp[i]; + } + *env = envp; + } +#endif /* _LP64 || lint */ + return (0); +} + +/* + * reconstruct an argv-like argument list from the target process + */ +int +kvm_getcmd(kvm_t *kd, + struct proc *proc, struct user *u, char ***arg, char ***env) +{ + size_t asize; + size_t esize; + size_t offset; + int i; + int argc; + char **argv = NULL; + char **envp = NULL; + char *str; + char *last_str; + char *argv_null; /* Known null in the returned argv */ + char *envp_null; /* Known null in the returned envp */ + + if (proc->p_flag & SSYS) /* system process */ + return (-1); + + /* + * Protect against proc structs found by kvm_nextproc() + * while the kernel was doing a fork(). Such a proc struct + * may have p_usrstack set but a still zeroed uarea. + * We wouldn't want to unecessarily allocate 4GB memory ... + */ + if (u->u_argv == NULL || u->u_envp == NULL) + return (-1); + + /* + * If this is a 32-bit process running on a 64-bit system, + * then the stack is laid out using ILP32 pointers, not LP64. + * To minimize potential confusion, we blow it up to "LP64 + * shaped" right here. + */ + if (proc->p_model != DATAMODEL_NATIVE && + proc->p_model == DATAMODEL_ILP32) + return (kvm_getcmd32(kd, proc, u, arg, env)); + + /* + * Space for the stack, from the argument vector. An additional + * word is added to guarantee a NULL word terminates the buffer. + */ + if (arg) { + asize = (size_t)proc->p_usrstack - (size_t)u->u_argv; + if ((argv = malloc(asize + sizeof (uintptr_t))) == NULL) + return (-1); + argv_null = (char *)argv + asize; + *(uintptr_t *)argv_null = 0; + } + + /* + * Space for the stack, from the environment vector. An additional + * word is added to guarantee a NULL word terminates the buffer. + */ + if (env) { + esize = (size_t)proc->p_usrstack - (size_t)u->u_envp; + if ((envp = malloc(esize + sizeof (uintptr_t))) == NULL) { + if (argv) + free(argv); + return (-1); + } + envp_null = (char *)envp + esize; + *(uintptr_t *)envp_null = 0; + } + + argc = u->u_argc; + + if (argv) { + /* read the whole initial stack */ + if (kvm_uread(kd, + (uintptr_t)u->u_argv, argv, asize) != asize) { + free(argv); + if (envp) + free(envp); + return (-1); + } + argv[argc] = 0; + if (envp) { + /* + * Copy it to the malloc()d space for the envp array + */ + (void) memcpy(envp, &argv[argc + 1], esize); + } + } else if (envp) { + /* read most of the initial stack (excluding argv) */ + if (kvm_uread(kd, + (uintptr_t)u->u_envp, envp, esize) != esize) { + free(envp); + return (-1); + } + } + + /* + * Relocate and sanity check the argv array. Entries which have + * been explicity nulled are left that way. Entries which have + * been replaced are pointed to a null string. Well behaved apps + * don't do any of this. + */ + if (argv) { + /* relocate the argv[] addresses */ + offset = (char *)argv - (char *)u->u_argv; + for (i = 0; i < argc; i++) { + if (argv[i] != NULL) { + str = (argv[i] += offset); + if (str < (char *)argv || + str >= (char *)argv + asize) + argv[i] = argv_null; + } + } + argv[i] = NULL; + *arg = argv; + } + + /* + * Relocate and sanity check the envp array. A null entry indicates + * the end of the environment. Entries which point outside of the + * initial stack are replaced with what must have been the initial + * value based on the known ordering of the string table by the + * kernel. If stack corruption prevents the calculation of the + * location of an initial string value, a pointer to a null string + * is returned. To return a null pointer would prematurely terminate + * the list. Well behaved apps do set pointers outside of the + * initial stack via the putenv(3C) library routine. + */ + if (envp) { + + /* + * Determine the start of the environment strings as one + * past the last argument string. + */ + offset = (char *)envp - (char *)u->u_envp; + + if (kvm_uread(kd, + (uintptr_t)u->u_argv + (argc - 1) * sizeof (char **), + &last_str, sizeof (last_str)) != sizeof (last_str)) + last_str = envp_null; + else { + last_str += offset; + if (last_str < (char *)envp || + last_str >= (char *)envp + esize) + last_str = envp_null; + } + + /* + * Relocate the envp[] addresses, while ensuring that we + * don't return bad addresses. + */ + for (i = 0; envp[i] != NULL; i++) { + str = (envp[i] += offset); + if (str < (char *)envp || str >= (char *)envp + esize) { + if (last_str != envp_null) + envp[i] = last_str + + strlen(last_str) + 1; + else + envp[i] = envp_null; + } + last_str = envp[i]; + } + envp[i] = NULL; + *env = envp; + } + + return (0); +} |