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]

Reply via email to