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