source: neercs/trunk/src/mytrace.c @ 3000

Last change on this file since 3000 was 3000, checked in by Pascal Terjan, 13 years ago
  • Fail early when /proc/<PID>/environ can not be read
  • Property svn:eol-style set to native
File size: 16.7 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2008 Pascal Terjan
4 *            (c) 2008 Sam Hocevar <sam@zoy.org>
5 *                All Rights Reserved
6 *
7 *  $Id$
8 *
9 *  This program is free software. It comes without any warranty, to
10 *  the extent permitted by applicable law. You can redistribute it
11 *  and/or modify it under the terms of the Do What The Fuck You Want
12 *  To Public License, Version 2, as published by Sam Hocevar. See
13 *  http://sam.zoy.org/wtfpl/COPYING for more details.
14 */
15
16#include "config.h"
17
18#include <errno.h>
19#include <fcntl.h>
20#include <limits.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#if defined USE_GRAB
26#   include <sys/ptrace.h>
27#   include <sys/stat.h>
28#   include <sys/syscall.h>
29#   include <sys/user.h>
30#   include <sys/wait.h>
31#endif
32
33#include "neercs.h"
34#include "mytrace.h"
35
36#if defined(__APPLE__)
37
38struct mytrace* mytrace_attach(long int pid)
39{
40    errno = ENOSYS;
41    return NULL;
42}
43
44struct mytrace* mytrace_fork(struct mytrace *t)
45{
46    errno = ENOSYS;
47    return NULL;
48}
49
50int mytrace_detach(struct mytrace *t)
51{
52    errno = ENOSYS;
53    return -1;
54}
55
56long mytrace_getpid(struct mytrace *t)
57{
58    errno = ENOSYS;
59    return -1;
60}
61
62int mytrace_open(struct mytrace *t, char const *path, int mode)
63{
64    errno = ENOSYS;
65    return -1;
66}
67
68int mytrace_write(struct mytrace *t, int fd, char const *data, size_t len)
69{
70    errno = ENOSYS;
71    return -1;
72}
73
74int mytrace_close(struct mytrace *t, int fd)
75{
76    errno = ENOSYS;
77    return -1;
78}
79
80int mytrace_dup2(struct mytrace *t, int oldfd, int newfd)
81{
82    errno = ENOSYS;
83    return -1;
84}
85
86int mytrace_setpgid(struct mytrace *t, long pid, long pgid)
87{
88    errno = ENOSYS;
89    return -1;
90}
91
92int mytrace_setsid(struct mytrace *t)
93{
94    errno = ENOSYS;
95    return -1;
96}
97
98int mytrace_kill(struct mytrace *t, long pid, int sig)
99{
100    errno = ENOSYS;
101    return -1;
102}
103
104int mytrace_exit(struct mytrace *t, int status)
105{
106    errno = ENOSYS;
107    return -1;
108}
109
110int mytrace_exec(struct mytrace *t, char const *command)
111{
112    errno = ENOSYS;
113    return -1;
114}
115
116#else
117
118#if defined USE_GRAB
119static int memcpy_from_target(struct mytrace *t,
120                              char* dest, long src, size_t n);
121static int memcpy_into_target(struct mytrace *t,
122                              long dest, char const *src, size_t n);
123static long remote_syscall(struct mytrace *t, long call,
124                           long arg1, long arg2, long arg3);
125#   if defined DEBUG
126static void print_registers(pid_t pid);
127#   else
128#       define print_registers(x) do {} while(0)
129#   endif
130#endif
131
132#define X(x) #x
133#define STRINGIFY(x) X(x)
134
135#define SYSCALL_X86     0x80cd  /* CD 80 = int $0x80 */
136#define SYSCALL_X86_NEW 0xf3eb  /* EB F3 = jmp <__kernel_vsyscall+0x3> */
137#define SYSENTER        0x340f  /* 0F 34 = sysenter */
138#define SYSCALL_AMD64   0x050fL /* 0F 05 = syscall */
139
140#if defined __x86_64__
141#   define RAX rax
142#   define RBX rbx
143#   define RCX rcx
144#   define RDX rdx
145#   define RSP rsp
146#   define RBP rbp
147#   define RIP rip
148#   define RDI rdi
149#   define RSI rsi
150#   define FMT "%016lx"
151#else
152#   define RAX eax
153#   define RBX ebx
154#   define RCX ecx
155#   define RDX edx
156#   define RSP esp
157#   define RBP ebp
158#   define RIP eip
159#   define RDI edi
160#   define RSI esi
161#   define FMT "%08lx"
162#endif
163
164#define MYCALL_OPEN     0
165#define MYCALL_CLOSE    1
166#define MYCALL_WRITE    2
167#define MYCALL_DUP2     3
168#define MYCALL_SETPGID  4
169#define MYCALL_SETSID   5
170#define MYCALL_KILL     6
171#define MYCALL_FORK     7
172#define MYCALL_EXIT     8
173#define MYCALL_EXECVE   9
174
175#if defined __x86_64__
176/* from unistd_32.h on an amd64 system */
177int syscalls32[] = { 5, 6, 4, 63, 57, 66, 37, 2, 1, 11 };
178int syscalls64[] =
179#else
180int syscalls32[] =
181#endif
182    { SYS_open, SYS_close, SYS_write, SYS_dup2, SYS_setpgid, SYS_setsid,
183      SYS_kill, SYS_fork, SYS_exit, SYS_execve };
184
185char const *syscallnames[] =
186    { "open", "close", "write", "dup2", "setpgid", "setsid", "kill", "fork",
187      "exit", "execve" };
188
189struct mytrace
190{
191    pid_t pid, child;
192};
193
194struct mytrace* mytrace_attach(long int pid)
195{
196#if defined USE_GRAB
197    struct mytrace *t;
198    int status;
199
200    if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
201    {
202        perror("ptrace_attach");
203        return NULL;
204    }
205    if(waitpid(pid, &status, 0) < 0)
206    {
207        perror("waitpid");
208        return NULL;
209    }
210    if(!WIFSTOPPED(status))
211    {
212        fprintf(stderr, "traced process was not stopped\n");
213        ptrace(PTRACE_DETACH, pid, 0, 0);
214        return NULL;
215    }
216
217    t = malloc(sizeof(struct mytrace));
218    t->pid = pid;
219    t->child = 0;
220
221    return t;
222#else
223    errno = ENOSYS;
224    return NULL;
225#endif
226}
227
228struct mytrace* mytrace_fork(struct mytrace *t)
229{
230#if defined USE_GRAB
231    struct mytrace *child;
232
233    ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEFORK);
234    remote_syscall(t, MYCALL_FORK, 0, 0, 0);
235    waitpid(t->child, NULL, 0);
236
237    child = malloc(sizeof(struct mytrace));
238    child->pid = t->child;
239    child->child = 0;
240
241    return child;
242#else
243    errno = ENOSYS;
244    return NULL;
245#endif
246}
247
248int mytrace_detach(struct mytrace *t)
249{
250#if defined USE_GRAB
251    ptrace(PTRACE_DETACH, t->pid, 0, 0);
252    free(t);
253
254    return 0;
255#else
256    errno = ENOSYS;
257    return -1;
258#endif
259}
260
261long mytrace_getpid(struct mytrace *t)
262{
263#if defined USE_GRAB
264    return t->pid;
265#else
266    errno = ENOSYS;
267    return -1;
268#endif
269}
270
271int mytrace_open(struct mytrace *t, char const *path, int mode)
272{
273#if defined USE_GRAB
274    char backup_data[4096];
275    struct user_regs_struct regs;
276    size_t size = strlen(path) + 1;
277    int ret;
278
279    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
280    {
281        fprintf(stderr, "PTRACE_GETREGS failed\n");
282        return errno;
283    }
284
285    /* Backup the data that we will use */
286    if(memcpy_from_target(t, backup_data, regs.RSP, size) < 0)
287        return -1;
288
289    memcpy_into_target(t, regs.RSP, path, size);
290
291    ret = remote_syscall(t, MYCALL_OPEN, regs.RSP, O_RDWR, 0755);
292
293    /* Restore the data */
294    memcpy_into_target(t, regs.RSP, backup_data, size);
295
296    if(ret < 0)
297    {
298        errno = ret;
299        return -1;
300    }
301
302    return ret;
303#else
304    errno = ENOSYS;
305    return -1;
306#endif
307}
308
309int mytrace_close(struct mytrace *t, int fd)
310{
311#if defined USE_GRAB
312    return remote_syscall(t, MYCALL_CLOSE, fd, 0, 0);
313#else
314    errno = ENOSYS;
315    return -1;
316#endif
317}
318
319int mytrace_write(struct mytrace *t, int fd, char const *data, size_t len)
320{
321#if defined USE_GRAB
322    struct user_regs_struct regs;
323    char *backup_data;
324    int ret;
325
326    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
327    {
328        fprintf(stderr, "PTRACE_GETREGS failed\n");
329        return errno;
330    }
331
332    backup_data = malloc(len);
333
334    /* Backup the data that we will use */
335    if(memcpy_from_target(t, backup_data, regs.RSP, len) < 0)
336        return -1;
337
338    memcpy_into_target(t, regs.RSP, data, len);
339
340    ret = remote_syscall(t, MYCALL_WRITE, fd, regs.RSP, len);
341
342    /* Restore the data */
343    memcpy_into_target(t, regs.RSP, backup_data, len);
344
345    if(ret < 0)
346    {
347        errno = ret;
348        return -1;
349    }
350
351    return ret;
352#else
353    errno = ENOSYS;
354    return -1;
355#endif
356}
357
358int mytrace_dup2(struct mytrace *t, int oldfd, int newfd)
359{
360#if defined USE_GRAB
361    return remote_syscall(t, MYCALL_DUP2, oldfd, newfd, 0);
362#else
363    errno = ENOSYS;
364    return -1;
365#endif
366}
367
368int mytrace_setpgid(struct mytrace *t, long pid, long pgid)
369{
370#if defined USE_GRAB
371    return remote_syscall(t, MYCALL_SETPGID, pid, pgid, 0);
372#else
373    errno = ENOSYS;
374    return -1;
375#endif
376}
377
378int mytrace_setsid(struct mytrace *t)
379{
380#if defined USE_GRAB
381    return remote_syscall(t, MYCALL_SETSID, 0, 0, 0);
382#else
383    errno = ENOSYS;
384    return -1;
385#endif
386}
387
388int mytrace_kill(struct mytrace *t, long pid, int sig)
389{
390#if defined USE_GRAB
391    return remote_syscall(t, MYCALL_KILL, pid, sig, 0);
392#else
393    errno = ENOSYS;
394    return -1;
395#endif
396}
397
398int mytrace_exit(struct mytrace *t, int status)
399{
400#if defined USE_GRAB
401    ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEEXIT);
402    return remote_syscall(t, MYCALL_EXIT, status, 0, 0);
403#else
404    errno = ENOSYS;
405    return -1;
406#endif
407}
408
409int mytrace_exec(struct mytrace *t, char const *command)
410{
411#if defined USE_GRAB
412    struct user_regs_struct regs;
413    char *env;
414    char envpath[PATH_MAX+1];
415    ssize_t envsize = 32*1024;
416    int ret, fd, l;
417    ssize_t r;
418
419    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
420    {
421        fprintf(stderr, "PTRACE_GETREGS failed\n");
422        return errno;
423    }
424
425    env = malloc(envsize);
426    if (!env)
427        return -1;
428
429    snprintf(envpath, PATH_MAX, "/proc/%d/environ", t->pid);
430
431    fd = open(envpath, O_RDONLY);
432    if (fd == -1)
433        return -1;
434    r = read(fd, env, envsize);
435    close(fd);
436    if (r == -1)
437        return -1;
438    while (r == envsize)
439    {
440        free(env);
441        env = malloc(envsize);
442        if (!env)
443            return -1;
444        fd = open(envpath, O_RDONLY);
445        r = read(fd, env, envsize);
446        close(fd);
447        if (r == -1)
448            return -1;
449    }
450    l = strlen(command)+1;
451    memcpy_into_target(t, regs.RSP, command, l);
452    memcpy_into_target(t, regs.RSP+l, env, envsize);
453    free(env);
454    ret = remote_syscall(t, MYCALL_EXECVE, regs.RSP, 0, regs.RSP+l);
455
456    if(ret < 0)
457    {
458        errno = ret;
459        return -1;
460    }
461
462    return ret;
463#else
464    errno = ENOSYS;
465    return -1;
466#endif
467}
468
469/*
470 * XXX: the following functions are local
471 */
472
473#if defined USE_GRAB
474static int memcpy_from_target(struct mytrace *t,
475                              char* dest, long src, size_t n)
476{
477    static int const align = sizeof(long) - 1;
478
479    while(n)
480    {
481        long data;
482        size_t todo = sizeof(long) - (src & align);
483
484        if(n < todo)
485            todo = n;
486
487        data = ptrace(PTRACE_PEEKTEXT, t->pid, src - (src & align), 0);
488        if(errno)
489        {
490            perror("ptrace_peektext");
491            return -1;
492        }
493        memcpy(dest, (char *)&data + (src & align), todo);
494
495        dest += todo;
496        src += todo;
497        n -= todo;
498    }
499
500    return 0;
501}
502
503static int memcpy_into_target(struct mytrace *t,
504                              long dest, char const *src, size_t n)
505{
506    static int const align = sizeof(long) - 1;
507
508    while(n)
509    {
510        long data;
511        size_t todo = sizeof(long) - (dest & align);
512
513        if(n < todo)
514            todo = n;
515        if(todo != sizeof(long))
516        {
517            data = ptrace(PTRACE_PEEKTEXT, t->pid, dest - (dest & align), 0);
518            if(errno)
519            {
520                perror("ptrace_peektext");
521                return -1;
522            }
523        }
524
525        memcpy((char *)&data + (dest & align), src, todo);
526        ptrace(PTRACE_POKETEXT, t->pid, dest - (dest & align), data);
527        if(errno)
528        {
529            perror("ptrace_poketext");
530            return -1;
531        }
532
533        src += todo;
534        dest += todo;
535        n -= todo;
536    }
537
538    return 0;
539}
540
541static long remote_syscall(struct mytrace *t, long call,
542                           long arg1, long arg2, long arg3)
543{
544    /* Method for remote syscall:
545     *  - wait until the traced application exits from a syscall
546     *  - save registers
547     *  - rewind eip/rip to point on the syscall instruction
548     *  - single step: execute syscall instruction
549     *  - retrieve resulting registers
550     *  - restore registers */
551    struct user_regs_struct regs, oldregs;
552    long oinst;
553    int bits;
554    int offset = 2;
555
556    if(call < 0 || call >= (long)(sizeof(syscallnames)/sizeof(*syscallnames)))
557    {
558        fprintf(stderr, "unknown remote syscall %li\n", call);
559        return -1;
560    }
561
562    debug("remote syscall %s(%lu, %lu, %lu)",
563          syscallnames[call], arg1, arg2, arg3);
564
565    print_registers(t->pid);
566
567#if defined __x86_64__
568    bits = 64;
569#else
570    bits = 32;
571#endif
572
573    for(;;)
574    {
575        if(ptrace(PTRACE_GETREGS, t->pid, NULL, &oldregs) < 0)
576        {
577            fprintf(stderr, "PTRACE_GETREGS failed\n");
578            return -1;
579        }
580
581        oinst = ptrace(PTRACE_PEEKTEXT, t->pid, oldregs.RIP - 2, 0) & 0xffff;
582        fprintf(stderr, "%lx\n", oinst);
583
584#if defined __x86_64__
585        if(oinst == SYSCALL_AMD64)
586            break;
587        if(oinst == SYSCALL_X86 || oinst == SYSCALL_X86_NEW)
588        {
589            bits = 32;
590            break;
591        }
592#else
593        if(oinst == SYSCALL_X86 || oinst == SYSCALL_X86_NEW)
594            break;
595#endif
596
597        if(ptrace(PTRACE_SYSCALL, t->pid, NULL, 0) < 0)
598        {
599            perror("ptrace_syscall (1)");
600            return -1;
601        }
602        waitpid(t->pid, NULL, 0);
603        if(ptrace(PTRACE_SYSCALL, t->pid, NULL, 0) < 0)
604        {
605            perror("ptrace_syscall (2)");
606            return -1;
607        }
608        waitpid(t->pid, NULL, 0);
609    }
610
611    print_registers(t->pid);
612
613    if(oinst == SYSCALL_X86_NEW)
614    {
615        /*  Get back to sysenter */
616        while((ptrace(PTRACE_PEEKTEXT, t->pid, oldregs.RIP - offset, 0) & 0xffff) != 0x340f)
617            offset++;
618        oldregs.RBP = oldregs.RSP;
619    }
620
621    regs = oldregs;
622    regs.RIP = regs.RIP - offset;
623#if defined __x86_64__
624    if(bits == 64)
625    {
626        regs.RAX = syscalls64[call];
627        regs.RDI = arg1;
628        regs.RSI = arg2;
629        regs.RDX = arg3;
630    }
631    else
632#endif
633    {
634        regs.RAX = syscalls32[call];
635        regs.RBX = arg1;
636        regs.RCX = arg2;
637        regs.RDX = arg3;
638    }
639
640    if(ptrace(PTRACE_SETREGS, t->pid, NULL, &regs) < 0)
641    {
642        fprintf(stderr, "PTRACE_SETREGS failed\n");
643        return -1;
644    }
645
646    for(;;)
647    {
648        int status;
649
650        print_registers(t->pid);
651
652        if(ptrace(PTRACE_SINGLESTEP, t->pid, NULL, NULL) < 0)
653        {
654            fprintf(stderr, "PTRACE_SINGLESTEP failed\n");
655            return -1;
656        }
657        waitpid(t->pid, &status, 0);
658
659        if(WIFEXITED(status))
660            return 0;
661
662        if(!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP)
663            continue;
664
665        /* Fuck Linux: there is no macro for this */
666        switch((status >> 16) & 0xffff)
667        {
668        case PTRACE_EVENT_FORK:
669            if(ptrace(PTRACE_GETEVENTMSG, t->pid, 0, &t->child) < 0)
670            {
671                fprintf(stderr, "PTRACE_GETEVENTMSG failed\n");
672                return -1;
673            }
674            debug("PTRACE_GETEVENTMSG %d", t->child);
675            continue;
676        case PTRACE_EVENT_EXIT:
677            debug("PTRACE_EVENT_EXIT");
678            /* The process is about to exit, don't do anything else */
679            return 0;
680        }
681
682        break;
683    }
684
685    print_registers(t->pid);
686
687    if(ptrace(PTRACE_GETREGS, t->pid, NULL, &regs) < 0)
688    {
689        fprintf(stderr, "PTRACE_GETREGS failed\n");
690        return -1;
691    }
692
693    if(ptrace(PTRACE_SETREGS, t->pid, NULL, &oldregs) < 0)
694    {
695        fprintf(stderr, "PTRACE_SETREGS failed\n");
696        return -1;
697    }
698    print_registers(t->pid);
699
700    debug("syscall %s returned %ld", syscallnames[call], regs.RAX);
701
702    if((long)regs.RAX < 0)
703    {
704        errno = -(long)regs.RAX;
705        perror("syscall");
706        return -1;
707    }
708
709    return regs.RAX;
710}
711
712/* For debugging purposes only. Prints register and stack information. */
713#if defined DEBUG
714static void print_registers(pid_t pid)
715{
716    union { long int l; unsigned char data[sizeof(long int)]; } inst;
717    struct user_regs_struct regs;
718    int i;
719
720    if(ptrace(PTRACE_GETREGS, pid, NULL, &regs) < 0)
721    {
722        perror("ptrace_getregs");
723        exit(errno);
724    }
725
726    fprintf(stderr, "  / %s: "FMT"   ", STRINGIFY(RAX), regs.RAX);
727    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RBX), regs.RBX);
728    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RCX), regs.RCX);
729    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RDX), regs.RDX);
730    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RDI), regs.RDI);
731    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RSI), regs.RSI);
732    fprintf(stderr, "  | %s: "FMT"   ", STRINGIFY(RSP), regs.RSP);
733    fprintf(stderr, "%s: "FMT"\n", STRINGIFY(RIP), regs.RIP);
734
735    inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP - 4, 0);
736    fprintf(stderr, "  | code: ... %02x %02x %02x %02x <---> ",
737            inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
738    inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP, 0);
739    fprintf(stderr, "%02x %02x %02x %02x ...\n",
740            inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
741
742    fprintf(stderr, "  \\ stack: ... ");
743    for(i = -16; i < 24; i += sizeof(long))
744    {
745        inst.l = ptrace(PTRACE_PEEKDATA, pid, regs.RSP + i, 0);
746#if defined __x86_64__
747        fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x ",
748                inst.data[0], inst.data[1], inst.data[2], inst.data[3],
749                inst.data[4], inst.data[5], inst.data[6], inst.data[7]);
750#else
751        fprintf(stderr, "%02x %02x %02x %02x ",
752                inst.data[0], inst.data[1], inst.data[2], inst.data[3]);
753#endif
754        if(i == 0)
755            fprintf(stderr, "[%s] ", STRINGIFY(RSP));
756    }
757    fprintf(stderr, "...\n");
758}
759#endif /* DEBUG */
760
761#endif /* USE_GRAB */
762
763#endif /* ! defined(__APPLE__) */
Note: See TracBrowser for help on using the repository browser.