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