Bug Summary

File:attach.c
Location:line 279, column 22
Description:The left operand of '==' is a garbage value

Annotated Source Code

1/*
2 * Copyright (C) 2011 by Nelson Elhage
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22#include <sys/types.h>
23#include <dirent.h>
24#include <sys/syscall.h>
25#include <sys/mman.h>
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <fcntl.h>
30#include <errno(*__errno_location ()).h>
31#include <termios.h>
32#include <sys/ioctl.h>
33#include <sys/wait.h>
34#include <signal.h>
35#include <limits.h>
36#include <time.h>
37#include <sys/time.h>
38#include <sys/stat.h>
39
40#include "ptrace.h"
41#include "reptyr.h"
42
43#define TASK_COMM_LENGTH16 16
44struct proc_stat {
45 pid_t pid;
46 char comm[TASK_COMM_LENGTH16+1];
47 char state;
48 pid_t ppid, sid, pgid;
49 dev_t ctty;
50};
51
52#define do_syscall(child, name, a0, a1, a2, a3, a4, a5)ptrace_remote_syscall((child), ptrace_syscall_numbers((child)
)->nr_name, a0, a1, a2, a3, a4, a5)
\
53 ptrace_remote_syscall((child), ptrace_syscall_numbers((child))->nr_##name, \
54 a0, a1, a2, a3, a4, a5)
55
56int parse_proc_stat(int statfd, struct proc_stat *out) {
57 char buf[1024];
58 int n;
59 unsigned dev;
60 lseek(statfd, 0, SEEK_SET0);
61 if (read(statfd, buf, sizeof buf) < 0)
10
Taking true branch
62 return errno(*__errno_location ());
63 n = sscanf(buf, "%d (%16[^)]) %c %d %d %d %u",
64 &out->pid, out->comm,
65 &out->state, &out->ppid, &out->sid,
66 &out->pgid, &dev);
67 if (n == EOF(-1))
68 return errno(*__errno_location ());
69 if (n != 7) {
70 return EINVAL22;
71 }
72 out->ctty = dev;
73 return 0;
74}
75
76int read_proc_stat(pid_t pid, struct proc_stat *out) {
77 char stat_path[PATH_MAX4096];
78 int statfd;
79 int err;
80
81 snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid);
82 statfd = open(stat_path, O_RDONLY00);
83 if (statfd < 0) {
84 error("Unable to open %s: %s", stat_path, strerror(errno(*__errno_location ())));
85 return -statfd;
86 }
87
88 err = parse_proc_stat(statfd, out);
89 close(statfd);
90 return err;
91}
92
93static void do_unmap(struct ptrace_child *child, child_addr_t addr, unsigned long len) {
94 if (addr == (unsigned long)-1)
95 return;
96 do_syscall(child, munmap, addr, len, 0, 0, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child)
)->nr_munmap, addr, len, 0, 0, 0, 0)
;
97}
98
99int *get_child_tty_fds(struct ptrace_child *child, int statfd, int *count) {
100 struct proc_stat child_status;
101 struct stat tty_st, console_st, st;
102 char buf[PATH_MAX4096];
103 int n = 0, allocated = 0;
104 int *fds = NULL((void*)0);
105 DIR *dir;
106 struct dirent *d;
107 int *tmp = NULL((void*)0);
108
109 debug("Looking up fds for tty in child.");
110 if ((child->error = parse_proc_stat(statfd, &child_status)))
111 return NULL((void*)0);
112
113 debug("Resolved child tty: %x", (unsigned)child_status.ctty);
114
115 if (stat("/dev/tty", &tty_st) < 0) {
116 child->error = errno(*__errno_location ());
117 error("Unable to stat /dev/tty");
118 return NULL((void*)0);
119 }
120
121 if (stat("/dev/console", &console_st) < 0) {
122 child->error = errno(*__errno_location ());
123 error("Unable to stat /dev/console");
124 return NULL((void*)0);
125 }
126
127 snprintf(buf, sizeof buf, "/proc/%d/fd/", child->pid);
128 if ((dir = opendir(buf)) == NULL((void*)0))
129 return NULL((void*)0);
130 while ((d = readdir(dir)) != NULL((void*)0)) {
131 if (d->d_name[0] == '.') continue;
132 snprintf(buf, sizeof buf, "/proc/%d/fd/%s", child->pid, d->d_name);
133 if (stat(buf, &st) < 0)
134 continue;
135
136 if (st.st_rdev == child_status.ctty
137 || st.st_rdev == tty_st.st_rdev
138 || st.st_rdev == console_st.st_rdev) {
139 if (n == allocated) {
140 allocated = allocated ? 2 * allocated : 2;
141 tmp = realloc(fds, allocated * sizeof *tmp);
142 if (tmp == NULL((void*)0)) {
143 child->error = errno(*__errno_location ());
144 error("Unable to allocate memory for fd array.");
145 free(fds);
146 fds = NULL((void*)0);
147 goto out;
148 }
149 fds = tmp;
150 }
151 debug("Found an alias for the tty: %s", d->d_name);
152 fds[n++] = atoi(d->d_name);
153 }
154 }
155 out:
156 *count = n;
157 closedir(dir);
158 return fds;
159}
160
161void move_process_group(struct ptrace_child *child, pid_t from, pid_t to) {
162 DIR *dir;
163 struct dirent *d;
164 pid_t pid;
165 char *p;
166 int err;
167
168 if ((dir = opendir("/proc/")) == NULL((void*)0))
169 return;
170
171 while ((d = readdir(dir)) != NULL((void*)0)) {
172 if (d->d_name[0] == '.') continue;
173 pid = strtol(d->d_name, &p, 10);
174 if (*p) continue;
175 if (getpgid(pid) == from) {
176 debug("Change pgid for pid %d", pid);
177 err = do_syscall(child, setpgid, pid, to, 0, 0, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child)
)->nr_setpgid, pid, to, 0, 0, 0, 0)
;
178 if (err < 0)
179 error(" failed: %s", strerror(-err));
180 }
181 }
182 closedir(dir);
183}
184
185int do_setsid(struct ptrace_child *child) {
186 int err = 0;
187 struct ptrace_child dummy;
188
189 err = do_syscall(child, fork, 0, 0, 0, 0, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child)
)->nr_fork, 0, 0, 0, 0, 0, 0)
;
190 if (err < 0)
191 return err;
192
193 debug("Forked a child: %ld", child->forked_pid);
194
195 err = ptrace_finish_attach(&dummy, child->forked_pid);
196 if (err < 0)
197 goto out_kill;
198
199 dummy.state = ptrace_after_syscall;
200 memcpy(&dummy.user, &child->user, sizeof child->user);
201 if (ptrace_restore_regs(&dummy)) {
202 err = dummy.error;
203 goto out_kill;
204 }
205
206 err = do_syscall(&dummy, setpgid, 0, 0, 0, 0, 0, 0)ptrace_remote_syscall((&dummy), ptrace_syscall_numbers((&
dummy))->nr_setpgid, 0, 0, 0, 0, 0, 0)
;
207 if (err < 0) {
208 error("Failed to setpgid: %s", strerror(-err));
209 goto out_kill;
210 }
211
212 move_process_group(child, child->pid, dummy.pid);
213
214 err = do_syscall(child, setsid, 0, 0, 0, 0, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child)
)->nr_setsid, 0, 0, 0, 0, 0, 0)
;
215 if (err < 0) {
216 error("Failed to setsid: %s", strerror(-err));
217 move_process_group(child, dummy.pid, child->pid);
218 goto out_kill;
219 }
220
221 debug("Did setsid()");
222
223 out_kill:
224 kill(dummy.pid, SIGKILL9);
225 ptrace_detach_child(&dummy);
226 ptrace_wait(&dummy);
227 do_syscall(child, wait4, dummy.pid, 0, WNOHANG, 0, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child)
)->nr_wait4, dummy.pid, 0, 1, 0, 0, 0)
;
228 return err;
229}
230
231int ignore_hup(struct ptrace_child *child, unsigned long scratch_page) {
232 int err;
233 if (ptrace_syscall_numbers(child)->nr_signal != -1) {
234 err = do_syscall(child, signal, SIGHUP, (unsigned long)SIG_IGN, 0, 0, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child)
)->nr_signal, 1, (unsigned long)((__sighandler_t) 1), 0, 0
, 0, 0)
;
235 } else {
236 struct sigaction act = {
237 .sa_handler__sigaction_handler.sa_handler = SIG_IGN((__sighandler_t) 1),
238 };
239 err = ptrace_memcpy_to_child(child, scratch_page,
240 &act, sizeof act);
241 if (err < 0)
242 return err;
243 err = do_syscall(child, rt_sigaction,ptrace_remote_syscall((child), ptrace_syscall_numbers((child)
)->nr_rt_sigaction, 1, scratch_page, 0, 8, 0, 0)
244 SIGHUP, scratch_page,ptrace_remote_syscall((child), ptrace_syscall_numbers((child)
)->nr_rt_sigaction, 1, scratch_page, 0, 8, 0, 0)
245 0, 8, 0, 0)ptrace_remote_syscall((child), ptrace_syscall_numbers((child)
)->nr_rt_sigaction, 1, scratch_page, 0, 8, 0, 0)
;
246 }
247 return err;
248}
249
250/*
251 * Wait for the specific pid to enter state 'T', or stopped. We have to pull the
252 * /proc file rather than attaching with ptrace() and doing a wait() because
253 * half the point of this exercise is for the process's real parent (the shell)
254 * to see the TSTP.
255 *
256 * In case the process is masking or ignoring SIGTSTP, we time out after a
257 * second and continue with the attach -- it'll still work mostly right, you
258 * just won't get the old shell back.
259 */
260void wait_for_stop(pid_t pid, int fd) {
261 struct timeval start, now;
262 struct timespec sleep;
263 struct proc_stat st;
264
265 gettimeofday(&start, NULL((void*)0));
266 while (1) {
7
Loop condition is true. Entering loop body
267 gettimeofday(&now, NULL((void*)0));
268 if ((now.tv_sec > start.tv_sec && now.tv_usec > start.tv_usec)
8
Taking false branch
269 || (now.tv_sec - start.tv_sec > 1)) {
270 error("Timed out waiting for child stop.");
271 break;
272 }
273 /*
274 * If anything goes wrong reading or parsing the stat node, just give
275 * up.
276 */
277 if (parse_proc_stat(fd, &st))
9
Calling 'parse_proc_stat'
11
Returning from 'parse_proc_stat'
12
Taking false branch
278 break;
279 if (st.state == 'T')
13
The left operand of '==' is a garbage value
280 break;
281
282 sleep.tv_sec = 0;
283 sleep.tv_nsec = 10000000;
284 nanosleep(&sleep, NULL((void*)0));
285 }
286}
287
288int copy_tty_state(pid_t pid, const char *pty) {
289 char buf[PATH_MAX4096];
290 int fd, err = EINVAL22;
291 struct termios tio;
292 int i;
293
294 for (i = 0; i < 3 && err; i++) {
295 err = 0;
296 snprintf(buf, sizeof buf, "/proc/%d/fd/%d", pid, i);
297
298 if ((fd = open(buf, O_RDONLY00)) < 0) {
299 err = -fd;
300 continue;
301 }
302
303 if (!isatty(fd)) {
304 err = ENOTTY25;
305 goto retry;
306 }
307
308 if (tcgetattr(fd, &tio) < 0) {
309 err = -errno(*__errno_location ());
310 }
311 retry:
312 close(fd);
313 }
314
315 if (err)
316 return err;
317
318 if ((fd = open(pty, O_RDONLY00)) < 0)
319 return -errno(*__errno_location ());
320
321 if (tcsetattr(fd, TCSANOW0, &tio) < 0)
322 err = errno(*__errno_location ());
323 close(fd);
324 return -err;
325}
326
327int check_pgroup(pid_t target) {
328 pid_t pg;
329 DIR *dir;
330 struct dirent *d;
331 pid_t pid;
332 char *p;
333 int err = 0;
334 struct proc_stat pid_stat;
335
336 debug("Checking for problematic process group members...");
337
338 pg = getpgid(target);
339 if (pg < 0) {
340 error("Unable to get pgid (does process %d exist?)", (int)target);
341 return pg;
342 }
343
344 if ((dir = opendir("/proc/")) == NULL((void*)0))
345 return errno(*__errno_location ());
346
347 while ((d = readdir(dir)) != NULL((void*)0)) {
348 if (d->d_name[0] == '.') continue;
349 pid = strtol(d->d_name, &p, 10);
350 if (*p) continue;
351 if (pid == target) continue;
352 if (getpgid(pid) == pg) {
353 /*
354 * We are actually being somewhat overly-conservative here
355 * -- if pid is a child of target, and has not yet called
356 * execve(), reptyr's setpgid() strategy may suffice. That
357 * is a fairly rare case, and annoying to check for, so
358 * for now let's just bail out.
359 */
360 if ((err = read_proc_stat(pid, &pid_stat))) {
361 memcpy(pid_stat.comm, "???", 4);
362 }
363 error("Process %d (%.*s) shares %d's process group. Unable to attach.\n"
364 "(This most commonly means that %d has a suprocesses).",
365 (int)pid, TASK_COMM_LENGTH16, pid_stat.comm, (int)target, (int)target);
366 err = EINVAL22;
367 goto out;
368 }
369 }
370 out:
371 closedir(dir);
372 return err;
373}
374
375int attach_child(pid_t pid, const char *pty, int force_stdio) {
376 struct ptrace_child child;
377 unsigned long scratch_page = -1;
378 int *child_tty_fds = NULL((void*)0), n_fds, child_fd, statfd;
379 int i;
380 int err = 0;
381 long page_size = sysconf(_SC_PAGE_SIZE_SC_PAGESIZE);
382 char stat_path[PATH_MAX4096];
383 long mmap_syscall;
384
385 if ((err = check_pgroup(pid))) {
1
Assuming 'err' is zero
2
Taking false branch
386 return err;
387 }
388
389 if ((err = copy_tty_state(pid, pty))) {
3
Taking false branch
390 if (err == ENOTTY25 && !force_stdio) {
391 error("Target is not connected to a terminal.\n"
392 " Use -s to force attaching anyways.");
393 return err;
394 }
395 }
396
397 snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid);
398 statfd = open(stat_path, O_RDONLY00);
399 if (statfd < 0) {
4
Assuming 'statfd' is >= 0
5
Taking false branch
400 error("Unable to open %s: %s", stat_path, strerror(errno(*__errno_location ())));
401 return -statfd;
402 }
403
404 kill(pid, SIGTSTP20);
405 wait_for_stop(pid, statfd);
6
Calling 'wait_for_stop'
406
407 if (ptrace_attach_child(&child, pid)) {
408 err = child.error;
409 goto out_cont;
410 }
411
412 if (ptrace_advance_to_state(&child, ptrace_at_syscall)) {
413 err = child.error;
414 goto out_detach;
415 }
416 if (ptrace_save_regs(&child)) {
417 err = child.error;
418 goto out_detach;
419 }
420
421 mmap_syscall = ptrace_syscall_numbers(&child)->nr_mmap2;
422 if (mmap_syscall == -1)
423 mmap_syscall = ptrace_syscall_numbers(&child)->nr_mmap;
424 scratch_page = ptrace_remote_syscall(&child, mmap_syscall, 0,
425 page_size, PROT_READ0x1|PROT_WRITE0x2,
426 MAP_ANONYMOUS0x20|MAP_PRIVATE0x02, 0, 0);
427
428 if (scratch_page > (unsigned long)-1000) {
429 err = -(signed long)scratch_page;
430 goto out_unmap;
431 }
432
433 debug("Allocated scratch page: %lx", scratch_page);
434
435 if (force_stdio) {
436 child_tty_fds = malloc(3 * sizeof(int));
437 if (!child_tty_fds) {
438 err = ENOMEM12;
439 goto out_unmap;
440 }
441 n_fds = 3;
442 child_tty_fds[0] = 0;
443 child_tty_fds[1] = 1;
444 child_tty_fds[2] = 2;
445 } else {
446 child_tty_fds = get_child_tty_fds(&child, statfd, &n_fds);
447 if (!child_tty_fds) {
448 err = child.error;
449 goto out_unmap;
450 }
451 }
452
453 if (ptrace_memcpy_to_child(&child, scratch_page, pty, strlen(pty)+1)) {
454 err = child.error;
455 error("Unable to memcpy the pty path to child.");
456 goto out_free_fds;
457 }
458
459 child_fd = do_syscall(&child, open,ptrace_remote_syscall((&child), ptrace_syscall_numbers((&
child))->nr_open, scratch_page, 02|0400, 0, 0, 0, 0)
460 scratch_page, O_RDWR|O_NOCTTY,ptrace_remote_syscall((&child), ptrace_syscall_numbers((&
child))->nr_open, scratch_page, 02|0400, 0, 0, 0, 0)
461 0, 0, 0, 0)ptrace_remote_syscall((&child), ptrace_syscall_numbers((&
child))->nr_open, scratch_page, 02|0400, 0, 0, 0, 0)
;
462 if (child_fd < 0) {
463 err = child_fd;
464 error("Unable to open the tty in the child.");
465 goto out_free_fds;
466 }
467
468 debug("Opened the new tty in the child: %d", child_fd);
469
470 err = ignore_hup(&child, scratch_page);
471 if (err < 0)
472 goto out_close;
473
474 err = do_syscall(&child, getsid, 0, 0, 0, 0, 0, 0)ptrace_remote_syscall((&child), ptrace_syscall_numbers((&
child))->nr_getsid, 0, 0, 0, 0, 0, 0)
;
475 if (err != child.pid) {
476 debug("Target is not a session leader, attempting to setsid.");
477 err = do_setsid(&child);
478 } else {
479 do_syscall(&child, ioctl, child_tty_fds[0], TIOCNOTTY, 0, 0, 0, 0)ptrace_remote_syscall((&child), ptrace_syscall_numbers((&
child))->nr_ioctl, child_tty_fds[0], 0x5422, 0, 0, 0, 0)
;
480 }
481 if (err < 0)
482 goto out_close;
483
484 err = do_syscall(&child, ioctl, child_fd, TIOCSCTTY, 0, 0, 0, 0)ptrace_remote_syscall((&child), ptrace_syscall_numbers((&
child))->nr_ioctl, child_fd, 0x540E, 0, 0, 0, 0)
;
485 if (err < 0) {
486 error("Unable to set controlling terminal.");
487 goto out_close;
488 }
489
490 debug("Set the controlling tty");
491
492 for (i = 0; i < n_fds; i++)
493 do_syscall(&child, dup2, child_fd, child_tty_fds[i], 0, 0, 0, 0)ptrace_remote_syscall((&child), ptrace_syscall_numbers((&
child))->nr_dup2, child_fd, child_tty_fds[i], 0, 0, 0, 0)
;
494
495
496 err = 0;
497
498 out_close:
499 do_syscall(&child, close, child_fd, 0, 0, 0, 0, 0)ptrace_remote_syscall((&child), ptrace_syscall_numbers((&
child))->nr_close, child_fd, 0, 0, 0, 0, 0)
;
500 out_free_fds:
501 free(child_tty_fds);
502
503 out_unmap:
504 do_unmap(&child, scratch_page, page_size);
505
506 ptrace_restore_regs(&child);
507 out_detach:
508 ptrace_detach_child(&child);
509
510 if (err == 0) {
511 kill(child.pid, SIGSTOP19);
512 wait_for_stop(child.pid, statfd);
513 }
514 kill(child.pid, SIGWINCH28);
515 out_cont:
516 kill(child.pid, SIGCONT18);
517 close(statfd);
518
519 return err < 0 ? -err : err;
520}