From d4e3c8994919d1310611421b9e5a6f1044081c0b Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Jan 10 2014 19:31:38 +0000 Subject: Fix crashing in calls through untraced slots in prelinked PPC64 binaries --- diff --git a/ltrace-0.7.91-breakpoint-on_install.patch b/ltrace-0.7.91-breakpoint-on_install.patch new file mode 100644 index 0000000..15959a3 --- /dev/null +++ b/ltrace-0.7.91-breakpoint-on_install.patch @@ -0,0 +1,81 @@ +From 56134ff5442bee4e128b189bb86cfc97dcb6f60a Mon Sep 17 00:00:00 2001 +From: Petr Machata +Date: Fri, 10 Jan 2014 20:05:15 +0100 +Subject: [PATCH 1/2] Add a new per-breakpoint callback on_install + +--- + breakpoint.h | 9 ++++++++- + breakpoints.c | 11 ++++++++++- + 2 files changed, 18 insertions(+), 2 deletions(-) + +diff --git a/breakpoint.h b/breakpoint.h +index 95964a8..c36f673 100644 +--- a/breakpoint.h ++++ b/breakpoint.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2012,2013,2014 Petr Machata, Red Hat Inc. + * Copyright (C) 2009 Juan Cespedes + * + * This program is free software; you can redistribute it and/or +@@ -46,6 +46,7 @@ + struct bp_callbacks { + void (*on_hit)(struct breakpoint *bp, struct process *proc); + void (*on_continue)(struct breakpoint *bp, struct process *proc); ++ void (*on_install)(struct breakpoint *bp, struct process *proc); + void (*on_retract)(struct breakpoint *bp, struct process *proc); + + /* Create a new breakpoint that should handle return from the +@@ -84,6 +85,12 @@ void breakpoint_on_continue(struct breakpoint *bp, struct process *proc); + * the instruction underneath it). */ + void breakpoint_on_retract(struct breakpoint *bp, struct process *proc); + ++/* Call ON_INSTALL handler of BP, if any is set. This should be ++ * called after the breakpoint is enabled for the first time, not ++ * every time it's enabled (such as after stepping over a site of a ++ * temporarily disabled breakpoint). */ ++void breakpoint_on_install(struct breakpoint *bp, struct process *proc); ++ + /* Call GET_RETURN_BP handler of BP, if any is set. If none is set, + * call CREATE_DEFAULT_RETURN_BP to obtain one. */ + int breakpoint_get_return_bp(struct breakpoint **ret, +diff --git a/breakpoints.c b/breakpoints.c +index 947cb71..c3fa275 100644 +--- a/breakpoints.c ++++ b/breakpoints.c +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2006,2007,2011,2012,2013 Petr Machata, Red Hat Inc. ++ * Copyright (C) 2006,2007,2011,2012,2013,2014 Petr Machata, Red Hat Inc. + * Copyright (C) 2009 Juan Cespedes + * Copyright (C) 1998,2001,2002,2003,2007,2008,2009 Juan Cespedes + * Copyright (C) 2006 Ian Wienand +@@ -85,6 +85,14 @@ breakpoint_on_retract(struct breakpoint *bp, struct process *proc) + (bp->cbs->on_retract)(bp, proc); + } + ++void ++breakpoint_on_install(struct breakpoint *bp, struct process *proc) ++{ ++ assert(bp != NULL); ++ if (bp->cbs != NULL && bp->cbs->on_install != NULL) ++ (bp->cbs->on_install)(bp, proc); ++} ++ + int + breakpoint_get_return_bp(struct breakpoint **ret, + struct breakpoint *bp, struct process *proc) +@@ -229,6 +237,7 @@ breakpoint_turn_on(struct breakpoint *bp, struct process *proc) + if (bp->enabled == 1) { + assert(proc->pid != 0); + enable_breakpoint(proc, bp); ++ breakpoint_on_install(bp, proc); + } + return 0; + } +-- +1.7.6.5 + diff --git a/ltrace-0.7.91-ppc64-unprelink.patch b/ltrace-0.7.91-ppc64-unprelink.patch new file mode 100644 index 0000000..a5929e3 --- /dev/null +++ b/ltrace-0.7.91-ppc64-unprelink.patch @@ -0,0 +1,221 @@ +From a0093ca43cf40d7e5f6cebeb64156062d2de46d9 Mon Sep 17 00:00:00 2001 +From: Petr Machata +Date: Fri, 10 Jan 2014 20:06:51 +0100 +Subject: [PATCH 2/2] Don't crash untraced calls via PLT in prelinked PPC64 + binaries + +In prelinked binaries, ltrace has to unprelinks PLT slots in order to +catch calls done through PLT. This makes the calls done through these +slots invalid, because the special first PLT slot is not initialized, +and dynamic linker SIGSEGVs because of this. Ltrace relies on +arranging breakpoints such that the dynamic linker is not actually +entered, and moves PC around itself to simulate the effects of a call +through PLT. + +Originally, arch_elf_add_plt_entry was called only for symbols that +were actually traced. Later this was changed and it's now called for +all PLT entries, and the resulting candidate list is filtered +afterwards. This gives backends a chance to rename the symbol, as is +useful with IRELATIVE PLT calls, where symbol name may not be +available at all. But the PPC backend was never updated to reflect +this, and unresolved all symbols for which arch_elf_add_plt_entry was +called, thus rendering _all_ PLT slots invalid, even those that +weren't later procted by breakpoints. Thus calls done through any +untraced slots failed. + +This patch fixes this problem by deferring the unprelinking of PLT +slots into the on_install hook of breakpoints. +--- + sysdeps/linux-gnu/ppc/arch.h | 21 ++++++++- + sysdeps/linux-gnu/ppc/plt.c | 94 +++++++++++++++++++++++++++++++++-------- + 2 files changed, 94 insertions(+), 21 deletions(-) + +diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h +index 2add3b8..bf9b5dc 100644 +--- a/sysdeps/linux-gnu/ppc/arch.h ++++ b/sysdeps/linux-gnu/ppc/arch.h +@@ -1,6 +1,6 @@ + /* + * This file is part of ltrace. +- * Copyright (C) 2012,2013 Petr Machata ++ * Copyright (C) 2012,2013,2014 Petr Machata + * Copyright (C) 2006 Paul Gilliam + * Copyright (C) 2002,2004 Juan Cespedes + * +@@ -87,12 +87,29 @@ enum ppc64_plt_type { + /* Very similar to PPC_PLT_UNRESOLVED, but for JMP_IREL + * slots. */ + PPC_PLT_IRELATIVE, ++ ++ /* Transitional state before the breakpoint is enabled. */ ++ PPC_PLT_NEED_UNRESOLVE, + }; + + #define ARCH_HAVE_LIBRARY_SYMBOL_DATA ++struct ppc_unresolve_data; + struct arch_library_symbol_data { + enum ppc64_plt_type type; +- GElf_Addr resolved_value; ++ ++ /* State Contents ++ * ++ * PPC_DEFAULT N/A ++ * PPC64_PLT_STUB N/A ++ * PPC_PLT_UNRESOLVED PLT entry address. ++ * PPC_PLT_IRELATIVE Likewise. ++ * PPC_PLT_RESOLVED The original value the slot was resolved to. ++ * PPC_PLT_NEED_UNRESOLVE DATA. ++ */ ++ union { ++ GElf_Addr resolved_value; ++ struct ppc_unresolve_data *data; ++ }; + + /* Address of corresponding slot in .plt. */ + GElf_Addr plt_slot_addr; +diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c +index 8715da6..332daa8 100644 +--- a/sysdeps/linux-gnu/ppc/plt.c ++++ b/sysdeps/linux-gnu/ppc/plt.c +@@ -679,6 +679,14 @@ arch_elf_add_func_entry(struct process *proc, struct ltelf *lte, + return PLT_OK; + } + ++struct ppc_unresolve_data { ++ struct ppc_unresolve_data *self; /* A canary. */ ++ GElf_Addr plt_entry_addr; ++ GElf_Addr plt_slot_addr; ++ GElf_Addr plt_slot_value; ++ bool is_irelative; ++}; ++ + enum plt_status + arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + const char *a_name, GElf_Rela *rela, size_t ndx, +@@ -778,28 +786,23 @@ arch_elf_add_plt_entry(struct process *proc, struct ltelf *lte, + && (plt_slot_value == plt_entry_addr || plt_slot_value == 0)) { + libsym->arch.type = PPC_PLT_UNRESOLVED; + libsym->arch.resolved_value = plt_entry_addr; +- + } else { +- /* Unresolve the .plt slot. If the binary was +- * prelinked, this makes the code invalid, because in +- * case of prelinked binary, the dynamic linker +- * doesn't update .plt[0] and .plt[1] with addresses +- * of the resover. But we don't care, we will never +- * need to enter the resolver. That just means that +- * we have to un-un-resolve this back before we +- * detach. */ +- +- if (unresolve_plt_slot(proc, plt_slot_addr, plt_entry_addr) < 0) { +- library_symbol_destroy(libsym); ++ /* Mark the symbol for later unresolving. We may not ++ * do this right away, as this is called by ltrace ++ * core for all symbols, and only later filtered. We ++ * only unresolve the symbol before the breakpoint is ++ * enabled. */ ++ ++ libsym->arch.type = PPC_PLT_NEED_UNRESOLVE; ++ libsym->arch.data = malloc(sizeof *libsym->arch.data); ++ if (libsym->arch.data == NULL) + goto fail2; +- } + +- if (! is_irelative) { +- mark_as_resolved(libsym, plt_slot_value); +- } else { +- libsym->arch.type = PPC_PLT_IRELATIVE; +- libsym->arch.resolved_value = plt_entry_addr; +- } ++ libsym->arch.data->self = libsym->arch.data; ++ libsym->arch.data->plt_entry_addr = plt_entry_addr; ++ libsym->arch.data->plt_slot_addr = plt_slot_addr; ++ libsym->arch.data->plt_slot_value = plt_slot_value; ++ libsym->arch.data->is_irelative = is_irelative; + } + + *ret = libsym; +@@ -999,6 +1002,7 @@ ppc_plt_bp_continue(struct breakpoint *bp, struct process *proc) + return; + + case PPC64_PLT_STUB: ++ case PPC_PLT_NEED_UNRESOLVE: + /* These should never hit here. */ + break; + } +@@ -1050,6 +1054,52 @@ ppc_plt_bp_retract(struct breakpoint *bp, struct process *proc) + } + } + ++static void ++ppc_plt_bp_install(struct breakpoint *bp, struct process *proc) ++{ ++ /* This should not be an artificial breakpoint. */ ++ struct library_symbol *libsym = bp->libsym; ++ if (libsym == NULL) ++ libsym = bp->arch.irel_libsym; ++ assert(libsym != NULL); ++ ++ if (libsym->arch.type == PPC_PLT_NEED_UNRESOLVE) { ++ /* Unresolve the .plt slot. If the binary was ++ * prelinked, this makes the code invalid, because in ++ * case of prelinked binary, the dynamic linker ++ * doesn't update .plt[0] and .plt[1] with addresses ++ * of the resover. But we don't care, we will never ++ * need to enter the resolver. That just means that ++ * we have to un-un-resolve this back before we ++ * detach. */ ++ ++ struct ppc_unresolve_data *data = libsym->arch.data; ++ libsym->arch.data = NULL; ++ assert(data->self == data); ++ ++ GElf_Addr plt_slot_addr = data->plt_slot_addr; ++ GElf_Addr plt_slot_value = data->plt_slot_value; ++ GElf_Addr plt_entry_addr = data->plt_entry_addr; ++ ++ if (unresolve_plt_slot(proc, plt_slot_addr, ++ plt_entry_addr) == 0) { ++ if (! data->is_irelative) { ++ mark_as_resolved(libsym, plt_slot_value); ++ } else { ++ libsym->arch.type = PPC_PLT_IRELATIVE; ++ libsym->arch.resolved_value = plt_entry_addr; ++ } ++ } else { ++ fprintf(stderr, "Couldn't unresolve %s@%p. Not tracing" ++ " this symbol.\n", ++ breakpoint_name(bp), bp->addr); ++ proc_remove_breakpoint(proc, bp); ++ } ++ ++ free(data); ++ } ++} ++ + int + arch_library_init(struct library *lib) + { +@@ -1080,6 +1130,11 @@ arch_library_symbol_init(struct library_symbol *libsym) + void + arch_library_symbol_destroy(struct library_symbol *libsym) + { ++ if (libsym->arch.type == PPC_PLT_NEED_UNRESOLVE) { ++ assert(libsym->arch.data->self == libsym->arch.data); ++ free(libsym->arch.data); ++ libsym->arch.data = NULL; ++ } + } + + int +@@ -1115,6 +1170,7 @@ arch_breakpoint_init(struct process *proc, struct breakpoint *bp) + static struct bp_callbacks cbs = { + .on_continue = ppc_plt_bp_continue, + .on_retract = ppc_plt_bp_retract, ++ .on_install = ppc_plt_bp_install, + }; + breakpoint_set_callbacks(bp, &cbs); + +-- +1.7.6.5 + diff --git a/ltrace.spec b/ltrace.spec index f46e132..15b00dc 100644 --- a/ltrace.spec +++ b/ltrace.spec @@ -1,7 +1,7 @@ Summary: Tracks runtime library calls from dynamically linked executables Name: ltrace Version: 0.7.91 -Release: 3%{?dist} +Release: 4%{?dist} URL: http://ltrace.alioth.debian.org/ License: GPLv2+ Group: Development/Debuggers @@ -24,6 +24,12 @@ Patch1: ltrace-0.7.91-account_execl.patch # Fix for a regression in tracing across fork. Upstream patch. Patch2: ltrace-0.7.91-ppc64-fork.patch +# Fix crashing a prelinked PPC64 binary which makes PLT calls through +# slots that ltrace doesn't trace. +# https://bugzilla.redhat.com/show_bug.cgi?id=1051221 +Patch3: ltrace-0.7.91-breakpoint-on_install.patch +Patch4: ltrace-0.7.91-ppc64-unprelink.patch + %description Ltrace is a debugging program which runs a specified command until the command exits. While the command is executing, ltrace intercepts and @@ -39,6 +45,8 @@ execution of processes. %patch0 -p1 %patch1 -p1 %patch2 -p1 +%patch3 -p1 +%patch4 -p1 %build %configure --docdir=%{?_pkgdocdir}%{!?_pkgdocdir:%{_docdir}/%{name}-%{version}} @@ -63,6 +71,12 @@ echo ====================TESTING END===================== %{_datadir}/ltrace %changelog +* Fri Jan 10 2014 Petr Machata - 0.7.91-4 +- Fix crashing a prelinked PPC64 binary which makes PLT calls through + slots that ltrace doesn't trace. + (ltrace-0.7.91-breakpoint-on_install.patch, + ltrace-0.7.91-ppc64-unprelink.patch) + * Thu Jan 9 2014 Petr Machata - 0.7.91-3 - Fix a problem in tracing across fork on PPC64 (ltrace-0.7.91-ppc64-fork.patch)