Package: ltrace
Version: 0.5.3-2.1
Severity: important
Tags: patch
Info:
All the following code reference refers to the latest ltrace version (git tag
0.6.0) but we also tested it on various ltrace debian versions (x86/amd64).
Abstract:
The execution of an int3 instruction immediately following a call instruction
will crash ltrace with a SIGSEGV.
Detail:
ltrace initially scans the list of imported library symbols of the monitored
process (function breakpoints.c:breakpoints_init). Then it adds all of them
to the dictionary of Breakpoint struct (function
breakpoints.c:insert_breakpoint).
struct Breakpoint {
void * addr;
unsigned char orig_value[BREAKPOINT_LENGTH];
int enabled;
struct library_symbol * libsym;
}
ltrace set a first breakpoint on the call instruction and another one
immediately after it, in order to interrupt the execution both when
executing the call instruction and just after returning from the called
function so that it can get the function return value. The former breakpoint
address is added to the dictionary when a library call symbol is found,
whereas the latter breakpoint address is added to the dictionary just before
the call instruction is executed. The latter is, as the former, a Breakpoint
structure. Differently from the former, the Breakpoint structure is not
fully initialized: addr is initialized with the address immediately
following the call instruction whereas libsym is set to NULL because it has
no symbol value. ltrace then continues these steps till the end of the
process.
The real problem arises when a int3 is placed into the original code
adjacent to the end of a call instruction. Specifically, ltrace sets a first
breakpoint on the address of the call instruction and another one on the
instruction following the call. Both these breakpoints are added to the
dictionary, but ltrace never deletes the Breakpoint structure identifying
the second one. Therefore, when an int3 placed into the code just after a
call, for example:
...
foobar(foo, bar);
__asm__ __volatile__ ( "int3" );
...
is executed, ltrace intercepts the breakpoint and tries to handle it (static
void handle_breakpoint(Event *event)). To do that, ltrace checks if the
breakpoint has already been added to the dictionary. In this situation, an
entry corresponding to that address will be found and eventually used,
leading to an error condition described in the following.
...
if ((sbp = address2bpstruct(event->proc, event->e_un.brk_addr))) {
if (strcmp(sbp->libsym->name, "") == 0) {
debug(2, "Hit _dl_debug_state breakpoint!\n");
...
Nevertheless, this entry doesn't correspond to a breakpoint previously
added by ltrace for intercepting the end of a call instruction. Instead, it
is a breakpoint intentionally inserted by the program developer. ltrace
fails to properly check this situations and it dereferences a NULL pointer
(sbp->libsym->name) as the breakpoint doesn't
represent an address of an imported symbol.
--- ltrace-patch/handle_event.c 2011-11-22 08:40:40.000000000 +0100
+++ ltrace/handle_event.c 2011-11-22 08:48:53.000000000 +0100
@@ -617,6 +617,12 @@
}
if ((sbp = address2bpstruct(event->proc, event->e_un.brk_addr))) {
+ if (sbp->libsym == NULL) {
+ output_line(event->proc, "unexpected breakpoint at %p",
+ (void *)event->e_un.brk_addr);
+ continue_process(event->proc->pid);
+ return;
+ }
if (strcmp(sbp->libsym->name, "") == 0) {
debug(2, "Hit _dl_debug_state breakpoint!\n");
arch_check_dbg(event->proc);
--
Alessandro Reina <adrenaline.soars{at}security.dico.unimi.it>
Aristide Fattori <joystick{at}security.dico.unimi.it>
--
To UNSUBSCRIBE, email to [email protected]
with a subject of "unsubscribe". Trouble? Contact [email protected]