#include #include #include #include #include #include #include #include #include "config.h" #include "backtrace.h" int lt_audit_parse_init(struct lt_config_shared *cfg); int lt_audit_parse(); extern FILE *lt_audit_in; #define SYMBOL_TAB_SIZE 100 static struct hsearch_data symbol_tab; static int symbol_tab_init = 0; struct lt_error_sym* lt_error_sym_get(struct lt_config_audit *cfg, const char *name) { struct lt_error_sym *sym; ENTRY e, *ep; if (!symbol_tab_init) return NULL; PRINT_VERBOSE(cfg, 1, "request for <%s>\n", name); e.key = (char*) name; hsearch_r(e, FIND, &ep, &symbol_tab); if (!ep) { PRINT_VERBOSE(cfg, 1, "failed to find <%s>\n", name); return NULL; } sym = (struct lt_error_sym*) ep->data; PRINT_VERBOSE(cfg, 1, "found %p <%s>\n", sym, sym->name); return sym; } extern __thread int pipe_fd; static void share_info(struct lt_config_audit *cfg, long ret, int keep, struct timeval *tv, struct link_map *lr, const La_regs *inregs) { void *ret_addr; #define LT_MAXMSG 100 char text[LT_MAXMSG]; lt_error_get_retaddr(cfg, &ret_addr, inregs); ret_addr -= lr->l_addr; if (keep) snprintf(text, LT_MAXMSG, "ERROR SIMULATION no change, ret %p", ret_addr); else snprintf(text, LT_MAXMSG, "ERROR SIMULATION inserted %ld, ret %p", ret, ret_addr); if (lt_sh(cfg, pipe)) { char buf[LT_FIFO_MSG_MAXLEN]; int len; len = lt_fifo_mtext_get(cfg->sh, buf, tv, text); /* TODO erro handling */ lt_fifo_send(cfg->sh, pipe_fd, buf, len); } else lt_out_text(cfg->sh, tv, syscall(SYS_gettid), text); dump_backtrace(cfg->sh, 0); } static struct lt_config_shared *ss_cfg; static void sig_segv_handler(int sig) { char text[LT_MAXMSG]; struct timeval tv; gettimeofday(&tv, NULL); snprintf(text, LT_MAXMSG, "ERROR SIMULATION got SIGSEGV"); if (lt_sh(ss_cfg, pipe)) { char buf[LT_FIFO_MSG_MAXLEN]; int len; len = lt_fifo_mtext_get(ss_cfg, buf, &tv, text); /* TODO erro handling */ lt_fifo_send(ss_cfg, pipe_fd, buf, len); } else lt_out_text(ss_cfg->sh, &tv, syscall(SYS_gettid), text); dump_backtrace(ss_cfg, 0); exit(-1); } static int install_sigsegv(struct lt_config_shared *cfg) { struct sigaction act; bzero(&act, sizeof(act)); act.sa_handler = sig_segv_handler; ss_cfg = cfg; return sigaction(SIGSEGV, &act, NULL); } static __thread unsigned long automated_sym_cnt = 0; int lt_error_sym_exit(struct lt_config_audit *cfg, const char *symname, struct lt_symbol *sym, struct timeval *tv, struct link_map *lr, const La_regs *inregs, La_retval *outregs, long *info) { struct lt_error_sym *esym; struct lt_error_config *cfg_err = cfg->error_config; int keep = 0; int handle_sigsegv; long ret; /* we're not interested in this symbol */ if (!sym || !sym->error) return -1; if (cfg_err->type == LT_ERROR_RUN_TYPE_AUTO) { struct lt_error_sym *esym; PRINT_VERBOSE(cfg, 1, "automated_sym_cnt %d, cfg_err->sym_cnt %d\n", automated_sym_cnt, cfg_err->sym_cnt); if (automated_sym_cnt >= cfg_err->sym_cnt) return -1; esym = &cfg_err->sym[automated_sym_cnt]; ret = esym->ret; keep = esym->keep; handle_sigsegv = esym->handle_sigsegv; *info = automated_sym_cnt++; } else { esym = sym->error; PRINT_VERBOSE(cfg, 1, "symbol %s, call %lu, n %ld\n", sym->name, esym->call_configured, cfg_err->n); /* we are not interested in this call number */ if (esym->call_current++ != esym->call_configured) return -1; ret = esym->ret; handle_sigsegv = esym->handle_sigsegv; } if (keep) PRINT_VERBOSE(cfg, 1, "keeping the return value\n", ret); else PRINT_VERBOSE(cfg, 1, "changing retval to %lu\n", ret); /* The fun begins right now.. let's change the return * value for the symbol ... */ if (!keep) lt_error_set_retval(cfg, ret, outregs); /* and share some info about the sinner */ share_info(cfg, ret, keep, tv, lr, inregs); if (handle_sigsegv && install_sigsegv(cfg->sh)) PRINT_VERBOSE(cfg, 1, "failed to install SIGSEGV handler\n"); return 0; } static int symbol_add(struct lt_config_audit *cfg, struct lt_error_config *cfg_err, struct lt_error_sym *sym) { ENTRY e, *ep; PRINT_VERBOSE(cfg, 1, "symbol %s, ret %ld\n", sym->name.symbol, sym->ret); /* should be safe, since only one thread * is doing the initialization */ if (!symbol_tab_init) { if (!hcreate_r(SYMBOL_TAB_SIZE, &symbol_tab)) { perror("failed to create hash table"); return -1; } symbol_tab_init = 1; PRINT_VERBOSE(cfg, 1, "symbol table initialized\n"); } e.key = sym->name.symbol; if (hsearch_r(e, FIND, &ep, &symbol_tab)) { PRINT_VERBOSE(cfg, 1, "symbol %s already in the table\n", sym->name.symbol); /* This is a bug in normal processing, * but completelly ok in automated mode. */ return cfg_err->type == LT_ERROR_RUN_TYPE_AUTO? 0 : -1; } e.key = sym->name.symbol; e.data = sym; if (!hsearch_r(e, ENTER, &ep, &symbol_tab)) { perror("hsearch_r failed"); free(sym); PRINT_VERBOSE(cfg, 3, "reached the error symbol limit %d\n", SYMBOL_TAB_SIZE); return -1; } PRINT_VERBOSE(cfg, 1, "ok\n"); return 0; } static void display_error_symbols(struct lt_config_audit *cfg, char *names, int size) { char *p; PRINT_VERBOSE(cfg, 1, "START size %d\n", size); while(size) { PRINT_VERBOSE(cfg, 1, "name %s\n", names); p = memchr(names, 0, size); size -= ++p - names; names = p; } PRINT_VERBOSE(cfg, 1, "END\n"); } int lt_error_init(struct lt_config_audit *cfg) { struct lt_error_config *cfg_err = cfg->error_config; int sym_cnt = cfg_err->sym_cnt, i, ok = 1; char *names; lt_sh(cfg, error_sim) = 0; names = (char*) (((void*) &cfg_err->sym) + (sym_cnt * sizeof(struct lt_error_sym))); display_error_symbols(cfg, names, cfg_err->names_size); for(i = 0; i < sym_cnt; i++) { struct lt_error_sym *sym; sym = &cfg_err->sym[i]; PRINT_VERBOSE(cfg, 1, "symbol index %d, name %s\n", sym->name.index, names + sym->name.index); sym->name.symbol = names + sym->name.index; if (symbol_add(cfg, cfg_err, sym)) { ok = 0; break; } } if (!ok) { if (symbol_tab_init) hdestroy_r(&symbol_tab); return -1; } lt_sh(cfg, error_sim) = 1; return 0; } int lt_error_config_read(struct lt_config_audit *cfg, int fd) { int size; struct lt_error_config *cfg_err, cfg_err_st; ssize_t n; n = read(fd, &cfg_err_st, sizeof(cfg_err_st)); if (n != sizeof(cfg_err_st)) { perror("read failed"); return -1; } size = cfg_err_st.sym_cnt * sizeof(struct lt_error_sym); size += cfg_err_st.names_size; PRINT_VERBOSE(cfg, 1, "symbols count %d, names_size %d, total size %d\n", cfg_err_st.sym_cnt, cfg_err_st.names_size, size); cfg_err = malloc(sizeof(*cfg_err) + size); if (!cfg_err) { perror("malloc failed"); return -1; } *cfg_err = cfg_err_st; if (size != read(fd, &cfg_err->sym, size)) { PRINT_VERBOSE(cfg, 1, "failed: no error symbols defined\n"); return -1; } cfg->error_config = cfg_err; return 0; }