diff options
Diffstat (limited to 'src/audit.c')
-rw-r--r-- | src/audit.c | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/src/audit.c b/src/audit.c new file mode 100644 index 0000000..d50a01f --- /dev/null +++ b/src/audit.c @@ -0,0 +1,287 @@ +/* + Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com> + + This file is part of the latrace. + + The latrace is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The latrace is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the latrace (file COPYING). If not, see + <http://www.gnu.org/licenses/>. +*/ + + +#include <link.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <bits/wordsize.h> +#include <gnu/lib-names.h> +#include <stdlib.h> + +#include "config.h" + + +extern struct lt_config_audit cfg; + +static __thread int pipe_fd = 0; +static __thread int flow_below_stack = 0; + + +static int check_names(char *name, char **ptr) +{ + char *n; + + for(n = *ptr; n; n = *(++ptr)) { + if (strstr(name, n)) { + PRINT_VERBOSE(cfg.sh.verbose, 2, + "return %d for name %s\n", 1, name); + return 1; + } + } + + PRINT_VERBOSE(cfg.sh.verbose, 2, "return %d for name %s\n", + 0, name); + return 0; +} + +static int check_flow_below(const char *symname, int in) +{ + int ret = flow_below_stack; + + if (check_names((char*) symname, cfg.flow_below)) + in ? ret = ++flow_below_stack : flow_below_stack--; + + return ret; +} + +static int sym_entry(const char *symname, char *lib_from, char *lib_to, + La_regs *regs) +{ + int argret; + char *argbuf = "", *argdbuf = ""; + + PRINT_VERBOSE(cfg.sh.verbose, 2, "%s@%s\n", symname, lib_to); + + if (cfg.flow_below_cnt && !check_flow_below(symname, 1)) + return 0; + + argret = cfg.sh.args_enabled ? + lt_args_sym_entry(&cfg.sh, (char*) symname, regs, &argbuf, &argdbuf) : -1; + + if (cfg.sh.pipe) { + char buf[FIFO_MSG_MAXLEN]; + int len; + + if (!pipe_fd) + pipe_fd = lt_fifo_create(&cfg, cfg.dir); + + len = lt_fifo_msym_get(&cfg, buf, FIFO_MSG_TYPE_ENTRY, + (char*) symname, lib_to, argbuf, argdbuf); + + return lt_fifo_send(&cfg, pipe_fd, buf, len); + } + + cfg.sh.indent_depth++; + + lt_out_entry(&cfg.sh, symname, lib_to, + argbuf, argdbuf); + + if (!argret) { + free(argbuf); + if (cfg.sh.args_detailed && (*argdbuf)) + free(argdbuf); + } + + return 0; +} + +static int sym_exit(const char *symname, char *lib_from, char *lib_to, + const La_regs *inregs, La_retval *outregs) +{ + int argret; + char *argbuf = "", *argdbuf = ""; + + PRINT_VERBOSE(cfg.sh.verbose, 2, "%s@%s\n", symname, lib_to); + + if (cfg.flow_below_cnt && !check_flow_below(symname, 0)) + return 0; + + argret = cfg.sh.args_enabled ? + lt_args_sym_exit(&cfg.sh, (char*) symname, + (La_regs*) inregs, outregs, &argbuf, &argdbuf) : -1; + + if (cfg.sh.pipe) { + char buf[FIFO_MSG_MAXLEN]; + int len; + + len = lt_fifo_msym_get(&cfg, buf, FIFO_MSG_TYPE_EXIT, + (char*) symname, lib_to, argbuf, argdbuf); + + return lt_fifo_send(&cfg, pipe_fd, buf, len); + } + + lt_out_exit(&cfg.sh, symname, lib_from, + argbuf, argdbuf); + + cfg.sh.indent_depth--; + + if (!argret) { + free(argbuf); + + if (cfg.sh.args_detailed && (*argdbuf)) + free(argdbuf); + } + + return 0; +} + +static int check_pid() +{ + pid_t pid = getpid(); + + PRINT_VERBOSE(cfg.sh.verbose, 1, "tid = %d, cfg tid = %d\n", + pid, cfg.sh.pid); + + if (pid != cfg.sh.pid) + return -1; + + return 0; +} + +#define CHECK_PID(ret) \ +do { \ + if (cfg.sh.not_follow_fork && \ + check_pid()) \ + return ret; \ +} while(0) + +unsigned int la_version(unsigned int v) +{ + return v; +} + +unsigned int la_objopen(struct link_map *l, Lmid_t a, uintptr_t *cookie) +{ + char *name = l->l_name; + + if (!cfg.init_ok) + return 0; + + if (!name) + return 0; + + /* executable itself */ + if (!(*name)) + return LA_FLG_BINDTO | LA_FLG_BINDFROM; + + /* audit all as default */ + if ((!cfg.libs_to_cnt) && + (!cfg.libs_from_cnt) && + (!cfg.libs_both_cnt)) + return LA_FLG_BINDTO | LA_FLG_BINDFROM; + + if (check_names(name, cfg.libs_to)) + return LA_FLG_BINDTO; + + if (check_names(name, cfg.libs_from)) + return LA_FLG_BINDFROM; + + if (check_names(name, cfg.libs_both)) + return LA_FLG_BINDTO | LA_FLG_BINDFROM; + + /* wrong library name specified ? */ + return 0; +} + +static unsigned int la_symbind(const char *symname) +{ + unsigned int flags = 0; + + if (cfg.symbols_cnt) { + flags = LA_SYMB_NOPLTENTER; + if (check_names((char*) symname, cfg.symbols)) + flags = 0; + } + + return flags; +} + +void la_activity(uintptr_t *cookie, unsigned int act) +{ + PRINT_VERBOSE(cfg.sh.verbose, 2, "entry\n"); +} + +char* la_objsearch(const char *name, uintptr_t *cookie, unsigned int flag) +{ + if (flag == LA_SER_ORIG) + return (char*) name; + + return lt_objsearch(&cfg, name, cookie, flag); +} + +void la_preinit(uintptr_t *__cookie) +{ + PRINT_VERBOSE(cfg.sh.verbose, 2, "entry\n"); +} + +unsigned int la_objclose(uintptr_t *__cookie) +{ + PRINT_VERBOSE(cfg.sh.verbose, 2, "entry\n"); + return 0; +} + +uintptr_t la_symbind32(Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, const char *symname) +{ + *flags = la_symbind(symname); + return sym->st_value; +} + +uintptr_t la_symbind64(Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, const char *symname) +{ + *flags = la_symbind(symname); + return sym->st_value; +} + +ElfW(Addr) +pltenter(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, La_regs *regs, unsigned int *flags, + const char *symname, long int *framesizep) +{ + struct link_map *lr = (struct link_map*) *refcook; + struct link_map *ld = (struct link_map*) *defcook; + + CHECK_PID(sym->st_value); + + sym_entry(symname, lr ? lr->l_name : NULL, ld ? ld->l_name : NULL, regs); + *framesizep = cfg.sh.framesize; + return sym->st_value; +} + +unsigned int pltexit(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, const La_regs *inregs, La_retval *outregs, + const char *symname) +{ + struct link_map *lr = (struct link_map*) *refcook; + struct link_map *ld = (struct link_map*) *defcook; + + CHECK_PID(0); + + sym_exit(symname, lr ? lr->l_name : NULL, ld ? ld->l_name : NULL, inregs, outregs); + return 0; +} |