xref: /illumos-gate/usr/src/lib/libkvm/common/kvm_getcmd.c (revision 90221f9148b67fdc90178b67f9600b7bd4e3bc7c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <kvm.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <kvm.h>
35 #include <strings.h>
36 #include <sys/types32.h>
37 
38 #define	_SYSCALL32
39 
40 /*
41  * VERSION FOR MACHINES WITH STACKS GROWING DOWNWARD IN MEMORY
42  *
43  * On program entry, the top of the stack frame looks like this:
44  *
45  * hi:	|-----------------------|
46  *	|	unspecified	|
47  *	|-----------------------|+
48  *	|	   :		| \
49  *	|  arg and env strings	|  > no more than NCARGS bytes
50  *	|	   :		| /
51  *	|-----------------------|+
52  *	|	unspecified	|
53  *	|-----------------------|
54  *	| null auxiliary vector	|
55  *	|-----------------------|
56  *	|   auxiliary vector	|
57  *	|   (2-word entries)	|
58  *	|	   :		|
59  *	|-----------------------|
60  *	|	(char *)0	|
61  *	|-----------------------|
62  *	|  ptrs to env strings	|
63  *	|	   :		|
64  *	|-----------------------|
65  *	|	(char *)0	|
66  *	|-----------------------|
67  *	|  ptrs to arg strings	|
68  *	|   (argc = # of ptrs)	|
69  *	|	   :		|
70  *	|-----------------------|
71  *	|	  argc		|
72  * low:	|-----------------------|
73  */
74 
75 #define	RoundUp(v, t)	(((v) + sizeof (t) - 1) & ~(sizeof (t) - 1))
76 
77 static int
78 kvm_getcmd32(kvm_t *kd,
79     struct proc *p, struct user *u, char ***arg, char ***env)
80 {
81 #if defined(_LP64) || defined(lint)
82 	size_t size32;
83 	void *stack32;
84 	int i, argc, envc;
85 	int auxc = 0;
86 	size_t asize, esize;
87 	char **argv = NULL;
88 	char **envp = NULL;
89 	size_t strpoolsz;
90 	int aptrcount;
91 	int eptrcount;
92 	caddr_t stackp;
93 	ptrdiff_t reloc;
94 	char *str;
95 
96 	/*
97 	 * Bring the entire stack into memory first, size it
98 	 * as an LP64 user stack, then allocate and copy into
99 	 * the buffer(s) to be returned to the caller.
100 	 */
101 	size32 = (size_t)p->p_usrstack - (size_t)u->u_argv;
102 	if ((stack32 = malloc(size32)) == NULL)
103 		return (-1);
104 	if (kvm_uread(kd, (uintptr_t)u->u_argv, stack32, size32) != size32) {
105 		free(stack32);
106 		return (-1);
107 	}
108 
109 	/*
110 	 * Find the interesting sizes of a 32-bit stack.
111 	 */
112 	argc = u->u_argc;
113 	stackp = (caddr_t)stack32 + ((1 + argc) * sizeof (caddr32_t));
114 
115 	for (envc = 0; *(caddr32_t *)stackp; envc++) {
116 		stackp += sizeof (caddr32_t);
117 		if ((stackp - (caddr_t)stack32) >= size32) {
118 			free(stack32);
119 			return (-1);
120 		}
121 	}
122 
123 	if (u->u_auxv[0].a_type != AT_NULL) {
124 		stackp += sizeof (caddr32_t);
125 		for (auxc = 0; *(int32_t *)stackp; auxc++) {
126 			stackp += 2 * sizeof (caddr32_t);
127 			if ((stackp - (caddr_t)stack32) >= size32) {
128 				free(stack32);
129 				return (-1);
130 			}
131 		}
132 		auxc++;		/* terminating AT_NULL record */
133 	}
134 
135 	/*
136 	 * Compute the sizes of the stuff we're going to allocate or copy.
137 	 */
138 	eptrcount = (envc + 1) + 2 * auxc;
139 	aptrcount = (argc + 1) + eptrcount;
140 	strpoolsz = size32 - aptrcount * sizeof (caddr32_t);
141 
142 	asize = aptrcount * sizeof (uintptr_t) + RoundUp(strpoolsz, uintptr_t);
143 	if (arg && (argv = calloc(1, asize + sizeof (uintptr_t))) == NULL) {
144 		free(stack32);
145 		return (-1);
146 	}
147 
148 	esize = eptrcount * sizeof (uintptr_t) + RoundUp(strpoolsz, uintptr_t);
149 	if (env && (envp = calloc(1, esize + sizeof (uintptr_t))) == NULL) {
150 		if (argv)
151 			free(argv);
152 		free(stack32);
153 		return (-1);
154 	}
155 
156 	/*
157 	 * Walk up the 32-bit stack, filling in the 64-bit argv and envp
158 	 * as we go.
159 	 */
160 	stackp = (caddr_t)stack32;
161 
162 	/*
163 	 * argument vector
164 	 */
165 	if (argv) {
166 		for (i = 0; i < argc; i++) {
167 			argv[i] = (char *)(uintptr_t)(*(caddr32_t *)stackp);
168 			stackp += sizeof (caddr32_t);
169 		}
170 		argv[argc] = 0;
171 		stackp += sizeof (caddr32_t);
172 	} else
173 		stackp += (1 + argc) * sizeof (caddr32_t);
174 
175 	/*
176 	 * environment
177 	 */
178 	if (envp) {
179 		for (i = 0; i < envc; i++) {
180 			envp[i] = (char *)(uintptr_t)(*(caddr32_t *)stackp);
181 			stackp += sizeof (caddr32_t);
182 		}
183 		envp[envc] = 0;
184 		stackp += sizeof (caddr32_t);
185 	} else
186 		stackp += (1 + envc) * sizeof (caddr32_t);
187 
188 	/*
189 	 * auxiliary vector (skip it..)
190 	 */
191 	stackp += auxc * (sizeof (int32_t) + sizeof (uint32_t));
192 
193 	/*
194 	 * Copy the string pool, untranslated
195 	 */
196 	if (argv)
197 		(void) memcpy(argv + aptrcount, (void *)stackp, strpoolsz);
198 	if (envp)
199 		(void) memcpy(envp + eptrcount, (void *)stackp, strpoolsz);
200 
201 	free(stack32);
202 
203 	/*
204 	 * Relocate the pointers to point at the newly allocated space.
205 	 * Use the same algorithms as kvm_getcmd to handle naughty
206 	 * changes to the argv and envp arrays.
207 	 */
208 	if (argv) {
209 		char *argv_null = (char *)argv + asize;
210 
211 		reloc = (char *)(argv + aptrcount) - (char *)
212 		    ((caddr_t)u->u_argv + aptrcount * sizeof (caddr32_t));
213 
214 		for (i = 0; i < argc; i++)
215 			if (argv[i] != NULL) {
216 				str = (argv[i] += reloc);
217 				if (str < (char *)argv ||
218 				    str >= (char *)argv + asize)
219 					argv[i] = argv_null;
220 			}
221 
222 		*arg = argv;
223 	}
224 
225 	if (envp) {
226 		char *envp_null = (char *)envp + esize;
227 		char *last_str;
228 
229 		reloc = (char *)(envp + eptrcount) - (char *)
230 		    ((caddr_t)u->u_envp + eptrcount * sizeof (caddr32_t));
231 
232 		last_str = (char *)((size_t)u->u_argv +
233 		    (1 + argc) * sizeof (caddr32_t) + reloc);
234 		if (last_str < (char *)envp ||
235 		    last_str >= (char *)envp + esize)
236 			last_str = envp_null;
237 
238 		for (i = 0; i < envc; i++) {
239 			str = (envp[i] += reloc);
240 			if (str < (char *)envp ||
241 			    str >= (char *)envp + esize) {
242 				if (last_str != envp_null)
243 					envp[i] = (char *)((size_t)last_str +
244 					    strlen(last_str) + 1);
245 				else
246 					envp[i] = envp_null;
247 			}
248 			last_str = envp[i];
249 		}
250 		*env = envp;
251 	}
252 #endif	/* _LP64 || lint */
253 	return (0);
254 }
255 
256 /*
257  * reconstruct an argv-like argument list from the target process
258  */
259 int
260 kvm_getcmd(kvm_t *kd,
261     struct proc *proc, struct user *u, char ***arg, char ***env)
262 {
263 	size_t asize;
264 	size_t esize;
265 	size_t offset;
266 	int i;
267 	int argc;
268 	char **argv = NULL;
269 	char **envp = NULL;
270 	char *str;
271 	char *last_str;
272 	char *argv_null;	/* Known null in the returned argv */
273 	char *envp_null;	/* Known null in the returned envp */
274 
275 	if (proc->p_flag & SSYS)	/* system process */
276 		return (-1);
277 
278 	/*
279 	 * Protect against proc structs found by kvm_nextproc()
280 	 * while the kernel was doing a fork(). Such a proc struct
281 	 * may have p_usrstack set but a still zeroed uarea.
282 	 * We wouldn't want to unecessarily allocate 4GB memory ...
283 	 */
284 	if (u->u_argv == NULL || u->u_envp == NULL)
285 		return (-1);
286 
287 	/*
288 	 * If this is a 32-bit process running on a 64-bit system,
289 	 * then the stack is laid out using ILP32 pointers, not LP64.
290 	 * To minimize potential confusion, we blow it up to "LP64
291 	 * shaped" right here.
292 	 */
293 	if (proc->p_model != DATAMODEL_NATIVE &&
294 	    proc->p_model == DATAMODEL_ILP32)
295 		return (kvm_getcmd32(kd, proc, u, arg, env));
296 
297 	/*
298 	 * Space for the stack, from the argument vector.  An additional
299 	 * word is added to guarantee a NULL word terminates the buffer.
300 	 */
301 	if (arg) {
302 		asize = (size_t)proc->p_usrstack - (size_t)u->u_argv;
303 		if ((argv = malloc(asize + sizeof (uintptr_t))) == NULL)
304 			return (-1);
305 		argv_null = (char *)argv + asize;
306 		*(uintptr_t *)argv_null = 0;
307 	}
308 
309 	/*
310 	 * Space for the stack, from the environment vector.  An additional
311 	 * word is added to guarantee a NULL word terminates the buffer.
312 	 */
313 	if (env) {
314 		esize = (size_t)proc->p_usrstack - (size_t)u->u_envp;
315 		if ((envp = malloc(esize + sizeof (uintptr_t))) == NULL) {
316 			if (argv)
317 				free(argv);
318 			return (-1);
319 		}
320 		envp_null = (char *)envp + esize;
321 		*(uintptr_t *)envp_null = 0;
322 	}
323 
324 	argc = u->u_argc;
325 
326 	if (argv) {
327 		/* read the whole initial stack */
328 		if (kvm_uread(kd,
329 		    (uintptr_t)u->u_argv, argv, asize) != asize) {
330 			free(argv);
331 			if (envp)
332 				free(envp);
333 			return (-1);
334 		}
335 		argv[argc] = 0;
336 		if (envp) {
337 			/*
338 			 * Copy it to the malloc()d space for the envp array
339 			 */
340 			(void) memcpy(envp, &argv[argc + 1], esize);
341 		}
342 	} else if (envp) {
343 		/* read most of the initial stack (excluding argv) */
344 		if (kvm_uread(kd,
345 		    (uintptr_t)u->u_envp, envp, esize) != esize) {
346 			free(envp);
347 			return (-1);
348 		}
349 	}
350 
351 	/*
352 	 * Relocate and sanity check the argv array.  Entries which have
353 	 * been explicity nulled are left that way.  Entries which have
354 	 * been replaced are pointed to a null string.  Well behaved apps
355 	 * don't do any of this.
356 	 */
357 	if (argv) {
358 		/* relocate the argv[] addresses */
359 		offset = (char *)argv - (char *)u->u_argv;
360 		for (i = 0; i < argc; i++) {
361 			if (argv[i] != NULL) {
362 				str = (argv[i] += offset);
363 				if (str < (char *)argv ||
364 				    str >= (char *)argv + asize)
365 					argv[i] = argv_null;
366 			}
367 		}
368 		argv[i] = NULL;
369 		*arg = argv;
370 	}
371 
372 	/*
373 	 * Relocate and sanity check the envp array.  A null entry indicates
374 	 * the end of the environment.  Entries which point outside of the
375 	 * initial stack are replaced with what must have been the initial
376 	 * value based on the known ordering of the string table by the
377 	 * kernel.  If stack corruption prevents the calculation of the
378 	 * location of an initial string value, a pointer to a null string
379 	 * is returned.  To return a null pointer would prematurely terminate
380 	 * the list.  Well behaved apps do set pointers outside of the
381 	 * initial stack via the putenv(3C) library routine.
382 	 */
383 	if (envp) {
384 
385 		/*
386 		 * Determine the start of the environment strings as one
387 		 * past the last argument string.
388 		 */
389 		offset = (char *)envp - (char *)u->u_envp;
390 
391 		if (kvm_uread(kd,
392 		    (uintptr_t)u->u_argv + (argc - 1) * sizeof (char **),
393 		    &last_str, sizeof (last_str)) != sizeof (last_str))
394 			last_str = envp_null;
395 		else {
396 			last_str += offset;
397 			if (last_str < (char *)envp ||
398 			    last_str >= (char *)envp + esize)
399 				last_str = envp_null;
400 		}
401 
402 		/*
403 		 * Relocate the envp[] addresses, while ensuring that we
404 		 * don't return bad addresses.
405 		 */
406 		for (i = 0; envp[i] != NULL; i++) {
407 			str = (envp[i] += offset);
408 			if (str < (char *)envp || str >= (char *)envp + esize) {
409 				if (last_str != envp_null)
410 					envp[i] = last_str +
411 					    strlen(last_str) + 1;
412 				else
413 					envp[i] = envp_null;
414 			}
415 			last_str = envp[i];
416 		}
417 		envp[i] = NULL;
418 		*env = envp;
419 	}
420 
421 	return (0);
422 }
423