Print this page
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/fs/proc/prargv.c
+++ new/usr/src/uts/common/fs/proc/prargv.c
1 1 /*
2 2 * This file and its contents are supplied under the terms of the
3 3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 4 * You may only use this file in accordance with the terms of version
5 5 * 1.0 of the CDDL.
6 6 *
7 7 * A full copy of the text of the CDDL should have accompanied this
8 8 * source. A copy of the CDDL is also available via the Internet at
9 9 * http://www.illumos.org/license/CDDL.
10 10 */
11 11
12 12 /*
13 13 * Copyright 2015, Joyent, Inc.
14 14 */
15 15
16 16 #include <sys/types.h>
17 17 #include <sys/sunddi.h>
18 18 #include <sys/proc.h>
19 19 #include <sys/procfs.h>
20 20 #include <sys/sysmacros.h>
21 21 #include <vm/as.h>
22 22
23 23 /*
24 24 * Safely read a contiguous region of memory from 'addr' in the address space
25 25 * of a particular process into the supplied kernel buffer (*buf, sz).
26 26 * Partially mapped regions will result in a partial read terminating at the
27 27 * first hole in the address space. The number of bytes actually read is
28 28 * returned to the caller via 'rdsz'.
29 29 */
30 30 int
31 31 prreadbuf(proc_t *p, uintptr_t ustart, uint8_t *buf, size_t sz, size_t *rdsz)
32 32 {
33 33 int error = 0;
34 34 size_t rem = sz;
35 35 off_t pos = 0;
36 36
37 37 if (rdsz != NULL)
38 38 *rdsz = 0;
39 39
40 40 while (rem != 0) {
41 41 uintptr_t addr = ustart + pos;
42 42 size_t len = MIN(rem, PAGESIZE - (addr & PAGEOFFSET));
43 43
44 44 if ((error = uread(p, buf + pos, len, addr)) != 0) {
45 45 if (error == ENXIO) {
46 46 /*
47 47 * ENXIO from uread() indicates that the page
48 48 * does not exist. This will simply be a
49 49 * partial read.
50 50 */
51 51 error = 0;
52 52 }
53 53 break;
54 54 }
55 55
56 56 rem -= len;
57 57 pos += len;
58 58 }
59 59
60 60 if (rdsz != NULL)
61 61 *rdsz = pos;
62 62
63 63 return (error);
64 64 }
65 65
66 66 /*
67 67 * Attempt to read the argument vector (argv) from this process. The caller
68 68 * must hold the p_lock mutex, and have marked the process P_PR_LOCK (e.g. via
69 69 * prlock or lx_prlock).
70 70 *
71 71 * The caller must provide a buffer (buf, buflen). We will concatenate each
72 72 * argument string (including the NUL terminator) into this buffer. The number
73 73 * of characters written to this buffer (including the final NUL terminator)
74 74 * will be stored in 'slen'.
75 75 */
76 76 int
77 77 prreadargv(proc_t *p, char *buf, size_t bufsz, size_t *slen)
78 78 {
79 79 int error;
80 80 user_t *up;
81 81 struct as *as;
82 82 size_t pos = 0;
83 83 caddr_t *argv = NULL;
84 84 size_t argvsz = 0;
85 85 int i;
86 86
87 87 VERIFY(MUTEX_HELD(&p->p_lock));
88 88 VERIFY(p->p_proc_flag & P_PR_LOCK);
89 89
90 90 up = PTOU(p);
91 91 as = p->p_as;
92 92
93 93 if ((p->p_flag & SSYS) || as == &kas || up->u_argv == NULL) {
94 94 /*
95 95 * Return the regular psargs string to the caller.
96 96 */
97 97 bcopy(up->u_psargs, buf, MIN(bufsz, sizeof (up->u_psargs)));
98 98 buf[bufsz - 1] = '\0';
99 99 *slen = strlen(buf) + 1;
100 100
101 101 return (0);
102 102 }
103 103
104 104 /*
105 105 * Allocate space to store argv array.
106 106 */
107 107 argvsz = up->u_argc * (p->p_model == DATAMODEL_ILP32 ?
108 108 sizeof (caddr32_t) : sizeof (caddr_t));
109 109 argv = kmem_alloc(argvsz, KM_SLEEP);
110 110
111 111 /*
112 112 * Extract the argv array from the target process. Drop p_lock
113 113 * while we do I/O to avoid deadlock with the clock thread.
114 114 */
115 115 mutex_exit(&p->p_lock);
116 116 if ((error = prreadbuf(p, up->u_argv, (uint8_t *)argv, argvsz,
117 117 NULL)) != 0) {
118 118 kmem_free(argv, argvsz);
119 119 mutex_enter(&p->p_lock);
120 120 VERIFY(p->p_proc_flag & P_PR_LOCK);
121 121 return (-1);
122 122 }
123 123
124 124 /*
125 125 * Read each argument string from the pointers in the argv array.
126 126 */
127 127 pos = 0;
128 128 for (i = 0; i < up->u_argc; i++) {
129 129 size_t rdsz, trysz;
130 130 uintptr_t arg;
131 131 off_t j;
132 132 boolean_t found_nul;
133 133 boolean_t do_retry = B_TRUE;
134 134
135 135 #ifdef _SYSCALL32_IMPL
136 136 if (p->p_model == DATAMODEL_ILP32) {
137 137 arg = (uintptr_t)((caddr32_t *)argv)[i];
138 138 } else {
139 139 arg = (uintptr_t)argv[i];
140 140 }
141 141 #else
142 142 arg = (uintptr_t)argv[i];
143 143 #endif
144 144
145 145 /*
146 146 * Stop trying to read arguments if we reach a NULL
147 147 * pointer in the vector.
148 148 */
149 149 if (arg == NULL)
150 150 break;
151 151
152 152 /*
153 153 * Stop reading if we have read the maximum length
154 154 * we can return to the user.
155 155 */
156 156 if (pos >= bufsz)
157 157 break;
158 158
159 159 /*
160 160 * Initially we try a short read, on the assumption that
161 161 * most individual argument strings are less than 80
162 162 * characters long.
163 163 */
164 164 if ((trysz = MIN(80, bufsz - pos - 1)) < 80) {
165 165 /*
166 166 * We don't have room in the target buffer for even
167 167 * an entire short read, so there is no need to retry
168 168 * with a longer read.
169 169 */
170 170 do_retry = B_FALSE;
171 171 }
172 172
173 173 retry:
174 174 /*
175 175 * Read string data for this argument. Leave room
176 176 * in the buffer for a final NUL terminator.
177 177 */
178 178 if ((error = prreadbuf(p, arg, (uint8_t *)&buf[pos], trysz,
179 179 &rdsz)) != 0) {
180 180 /*
181 181 * There was a problem reading this string
182 182 * from the process. Give up.
183 183 */
184 184 break;
185 185 }
186 186
187 187 /*
188 188 * Find the NUL terminator.
189 189 */
190 190 found_nul = B_FALSE;
191 191 for (j = 0; j < rdsz; j++) {
192 192 if (buf[pos + j] == '\0') {
193 193 found_nul = B_TRUE;
194 194 break;
195 195 }
196 196 }
197 197
198 198 if (!found_nul && do_retry) {
199 199 /*
200 200 * We did not find a NUL terminator, but this
201 201 * was a first pass short read. Try once more
202 202 * with feeling.
203 203 */
204 204 trysz = bufsz - pos - 1;
205 205 do_retry = B_FALSE;
206 206 goto retry;
207 207 }
208 208
209 209 /*
210 210 * Commit the string we read to the buffer.
211 211 */
212 212 pos += j + 1;
213 213 if (!found_nul && pos < bufsz) {
214 214 /*
215 215 * A NUL terminator was not found; add one.
216 216 */
217 217 buf[pos++] = '\0';
218 218 }
219 219 }
220 220
221 221 /*
222 222 * Ensure the entire string is NUL-terminated.
223 223 */
224 224 buf[bufsz - 1] = '\0';
225 225
226 226 mutex_enter(&p->p_lock);
227 227 VERIFY(p->p_proc_flag & P_PR_LOCK);
228 228 kmem_free(argv, argvsz);
229 229
230 230 /*
231 231 * If the operation was a success, return the copied string length
232 232 * to the caller.
233 233 */
234 234 *slen = (error == 0) ? pos : 0;
235 235
236 236 return (error);
237 237 }
238 238
239 239 /*
240 240 * Similar to prreadargv except reads the env vector. This is slightly more
241 241 * complex because there is no count for the env vector that corresponds to
242 242 * u_argc.
243 243 */
244 244 int
245 245 prreadenvv(proc_t *p, char *buf, size_t bufsz, size_t *slen)
246 246 {
247 247 int error;
248 248 user_t *up;
249 249 struct as *as;
250 250 size_t pos = 0;
251 251 caddr_t *envp = NULL;
252 252 uintptr_t tmpp = NULL;
253 253 size_t envpsz = 0, rdsz = 0;
254 254 int i;
255 255 int cnt, bound;
256 256
257 257 VERIFY(MUTEX_HELD(&p->p_lock));
258 258 VERIFY(p->p_proc_flag & P_PR_LOCK);
259 259
260 260 up = PTOU(p);
261 261 as = p->p_as;
262 262
263 263 if ((p->p_flag & SSYS) || as == &kas || up->u_envp == NULL) {
264 264 /*
265 265 * Return empty string.
266 266 */
267 267 buf[0] = '\0';
268 268 *slen = 1;
269 269
270 270 return (0);
271 271 }
272 272
273 273 /*
274 274 * Drop p_lock while we do I/O to avoid deadlock with the clock thread.
275 275 */
276 276 mutex_exit(&p->p_lock);
277 277
278 278 /*
279 279 * We first have to count how many env entries we have. This is
280 280 * somewhat painful. We extract the env entries from the target process
281 281 * one entry at a time. Stop trying to read env entries if we reach a
282 282 * NULL pointer in the vector or hit our upper bound (which we take
283 283 * as the bufsz/4) to ensure we don't run off.
284 284 */
285 285 rdsz = (p->p_model == DATAMODEL_ILP32 ?
286 286 sizeof (caddr32_t) : sizeof (caddr_t));
287 287 bound = (int)(bufsz / 4);
288 288 for (cnt = 0, tmpp = up->u_envp; cnt < bound; cnt++, tmpp += rdsz) {
289 289 caddr_t tmp = NULL;
290 290
291 291 if ((error = prreadbuf(p, tmpp, (uint8_t *)&tmp, rdsz,
292 292 NULL)) != 0) {
293 293 mutex_enter(&p->p_lock);
294 294 VERIFY(p->p_proc_flag & P_PR_LOCK);
295 295 return (-1);
296 296 }
297 297
298 298 if (tmp == NULL)
299 299 break;
300 300 }
301 301 if (cnt == 0) {
302 302 /* Return empty string. */
303 303 buf[0] = '\0';
304 304 *slen = 1;
305 305 mutex_enter(&p->p_lock);
306 306 VERIFY(p->p_proc_flag & P_PR_LOCK);
307 307 return (0);
308 308 }
309 309
310 310 /*
311 311 * Allocate space to store env array.
312 312 */
313 313 envpsz = cnt * (p->p_model == DATAMODEL_ILP32 ?
314 314 sizeof (caddr32_t) : sizeof (caddr_t));
315 315 envp = kmem_alloc(envpsz, KM_SLEEP);
316 316
317 317 /*
318 318 * Extract the env array from the target process.
319 319 */
320 320 if ((error = prreadbuf(p, up->u_envp, (uint8_t *)envp, envpsz,
321 321 NULL)) != 0) {
322 322 kmem_free(envp, envpsz);
323 323 mutex_enter(&p->p_lock);
324 324 VERIFY(p->p_proc_flag & P_PR_LOCK);
325 325 return (-1);
326 326 }
327 327
328 328 /*
329 329 * Read each env string from the pointers in the env array.
330 330 */
331 331 pos = 0;
332 332 for (i = 0; i < cnt; i++) {
333 333 size_t rdsz, trysz;
334 334 uintptr_t ev;
335 335 off_t j;
336 336 boolean_t found_nul;
337 337 boolean_t do_retry = B_TRUE;
338 338
339 339 #ifdef _SYSCALL32_IMPL
340 340 if (p->p_model == DATAMODEL_ILP32) {
341 341 ev = (uintptr_t)((caddr32_t *)envp)[i];
342 342 } else {
343 343 ev = (uintptr_t)envp[i];
344 344 }
345 345 #else
346 346 ev = (uintptr_t)envp[i];
347 347 #endif
348 348
349 349 /*
350 350 * Stop trying to read env entries if we reach a NULL
351 351 * pointer in the vector.
352 352 */
353 353 if (ev == NULL)
354 354 break;
355 355
356 356 /*
357 357 * Stop reading if we have read the maximum length
358 358 * we can return to the user.
359 359 */
360 360 if (pos >= bufsz)
361 361 break;
362 362
363 363 /*
364 364 * Initially we try a short read, on the assumption that
365 365 * most individual env strings are less than 80
366 366 * characters long.
367 367 */
368 368 if ((trysz = MIN(80, bufsz - pos - 1)) < 80) {
369 369 /*
370 370 * We don't have room in the target buffer for even
371 371 * an entire short read, so there is no need to retry
372 372 * with a longer read.
373 373 */
374 374 do_retry = B_FALSE;
375 375 }
376 376
377 377 retry:
378 378 /*
379 379 * Read string data for this env var. Leave room
380 380 * in the buffer for a final NUL terminator.
381 381 */
382 382 if ((error = prreadbuf(p, ev, (uint8_t *)&buf[pos], trysz,
383 383 &rdsz)) != 0) {
384 384 /*
385 385 * There was a problem reading this string
386 386 * from the process. Give up.
387 387 */
388 388 break;
389 389 }
390 390
391 391 /*
392 392 * Find the NUL terminator.
393 393 */
394 394 found_nul = B_FALSE;
395 395 for (j = 0; j < rdsz; j++) {
396 396 if (buf[pos + j] == '\0') {
397 397 found_nul = B_TRUE;
398 398 break;
399 399 }
400 400 }
401 401
402 402 if (!found_nul && do_retry) {
403 403 /*
404 404 * We did not find a NUL terminator, but this
405 405 * was a first pass short read. Try once more
406 406 * with feeling.
407 407 */
408 408 trysz = bufsz - pos - 1;
409 409 do_retry = B_FALSE;
410 410 goto retry;
411 411 }
412 412
413 413 /*
414 414 * Commit the string we read to the buffer.
415 415 */
416 416 pos += j + 1;
417 417 if (!found_nul && pos < bufsz) {
418 418 /*
419 419 * A NUL terminator was not found; add one.
420 420 */
421 421 buf[pos++] = '\0';
422 422 }
423 423 }
424 424
425 425 /*
426 426 * Ensure the entire string is NUL-terminated.
427 427 */
428 428 buf[bufsz - 1] = '\0';
429 429
430 430 mutex_enter(&p->p_lock);
431 431 VERIFY(p->p_proc_flag & P_PR_LOCK);
432 432 kmem_free(envp, envpsz);
433 433
434 434 /*
435 435 * If the operation was a success, return the copied string length
436 436 * to the caller.
437 437 */
438 438 *slen = (error == 0) ? pos : 0;
439 439
440 440 return (error);
441 441 }
|
↓ open down ↓ |
441 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX