Fix crashing in calls through untraced slots in prelinked PPC64 binaries
This commit is contained in:
parent
2128805bc3
commit
d4e3c89949
81
ltrace-0.7.91-breakpoint-on_install.patch
Normal file
81
ltrace-0.7.91-breakpoint-on_install.patch
Normal file
@ -0,0 +1,81 @@
|
||||
From 56134ff5442bee4e128b189bb86cfc97dcb6f60a Mon Sep 17 00:00:00 2001
|
||||
From: Petr Machata <pmachata@redhat.com>
|
||||
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
|
||||
|
||||
221
ltrace-0.7.91-ppc64-unprelink.patch
Normal file
221
ltrace-0.7.91-ppc64-unprelink.patch
Normal file
@ -0,0 +1,221 @@
|
||||
From a0093ca43cf40d7e5f6cebeb64156062d2de46d9 Mon Sep 17 00:00:00 2001
|
||||
From: Petr Machata <pmachata@redhat.com>
|
||||
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
|
||||
|
||||
16
ltrace.spec
16
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 <pmachata@redhat.com> - 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 <pmachata@redhat.com> - 0.7.91-3
|
||||
- Fix a problem in tracing across fork on PPC64
|
||||
(ltrace-0.7.91-ppc64-fork.patch)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user