Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plthook commits summary #5

Open
honggyukim opened this issue Mar 10, 2019 · 11 comments
Open

plthook commits summary #5

honggyukim opened this issue Mar 10, 2019 · 11 comments

Comments

@honggyukim
Copy link
Owner

3d4ea6b plthook: Trace dynamic (PLT) symbols too
04e7dd2 Implement plthook on ARM
fc664ce fixup for ARM plthook
2f2baf2 Access GLOBAL_OFFSET_TABLE through DT_PLTGOT info
cae6af7 Use mprotect() before updating GOT
d532a65 Find dynamic symbols to skip on __monstartup()
30cf417 Prevent recursion on plthook
16a31e2 Add support for library symbols
c3c8f72 Save resolved addresses of dynamic symbols
1437299 Speed up PLT hooking on x86_64
d8c6a4f Speed up PLT hooking on ARM
53b370f Handle fork() call in child

fee3062 mcount: Create libmcount directory
37d8201 mcount: Build dynsym idxlist for setjmp/longjmp
9635a1a mcount: Add basic support for setjmp/longjmp
b13df20 filter: Add '@plt' filter for PLT functions
f6fe591 mcount: Fix PLT record after filtering
124653d mcount: Protect resolved PLT addresses using mutex
244ce79 mcount: Fix PLT recursion with filter
* c5c2f79 mcount: Protect recursion from malloc()
80319b9 mcount: Ignore mcount() after it finished
9bf99b3 mcount: Flush rstack on special functions
8479be5 mcount: Pass mcount_regs to plthook_entry also

1a54771 mcount: Split PLT-hooking code into separate file
76899f8 mcount: Introduce struct mcount_thread_data
450becc mcount: Move plthook related data to mcount_thread_data
7e7ac2e mcount: Use map load address to adjust symbol offset
9b57d31 mcount: Pass offset to the PLT hook function
5b79331 mcount: Skip PLT hooking when no PLT reloc entries found
b7fdfbc mcount: Fix return value of PLT functions
3912dfa mcount: Cleanup the plthook_exit()
* 78db558 mcount: Prevent recursive call in mtd_dtor()
* 8eb2330 mcount: Fix recursion during fork()
87a0ee7 mcount: Hook dlopen() to trace functions in the dynamic library
* 6e3f577 mcount: Move raw access of mtd under unlikely
582ad22 mcount: Move recursion check out of mcount_should_stop()
* 3814d4f mcount: Check recursion in mcount_prepare()
eb47108 mcount: Use binary search for special PLT functions
** 9479ec2 mcount: Separate PLTGOT overwrite
* 3ec95cf mcount: Do not hook PLT of mcount()
* 66ac84a mcount: Fix a bug when longjmp() called twice
263ba58 mcount: Hook dlopen() early
* ad5869b mcount: Return saved address from plthook_entry()
** 3e0c35b mcount: Introduce setup_pltgot()
* 9392847 mcount: Basic support for bind-now binaries

mcount: Introduce mcount_arch_plthook_addr()
* 5f2e7d6 mcount: Introduce mcount_arch_plthook_addr()
* 6ef9641 mcount: Eliminate plthook_guard
ffbfd59 mcount: Don't restore parent ip in rstacks on longjmp()
* 2b2403e symbol: Fix SYMTAB_FL_ADJ_OFFSET usage in plthook
7f94a7e plthook: Skip binary has no PLTGOT address
** ef7137f plthook: Introduce struct plthook_data
** 14c9ac3 plthook: Setup module id for BIND-NOW binaries
50f43df plthook: Pass 'pd' to mcount_arch_plthook_addr()

** 3594de2 plthook: Support nested libcall tracing
f1f0b28 plthook: Handle special dynsym index per pd
* bd4a1a7 plthook: Restore already resolved PLT entries
* 4d39415 plthook: Save and use PLT0 address on ARM
1d10f0c plthook: Set resolver address only once

8d90229 symbol: Load and save all module symbols
fc41d32 plthook: Make x86's mcount_arch_plthook_addr() as default
** 8503de4 plthook: Resolve address of pthread_exit()
5c8c4ee plthook: Handle recursion in plthook_entry()
c6fde0e plthook: Handle corrupted rstack after vfork()
2cb0901 plthook: Check PT_GNU_RELRO
5604ae5 plthook: Pre-resolve address of special functions
3f20adf plthook: Fix infinite loop in execl() on ARM
b7230be plthook: Do not call dlsym() in the init routine
0c18764 plthook: Allow architecture to hook no-PLT libcall
5d81130 plthook: Remove mcount_arch_undo_bindnow()
* 961518c Revert "Handle mcount/plthook_return() return 0"
* 0ba73d0 mcount: Add ARCH_CAN_RESTORE_PLTHOOK logic

@honggyukim
Copy link
Owner Author

honggyukim commented Mar 11, 2019

typedef struct {
        Elf32_Sword d_tag;
        union {
                Elf32_Word      d_val;
                Elf32_Addr      d_ptr;
                Elf32_Off       d_off;
        } d_un;
} Elf32_Dyn;

DT_PLTGOT (d_tag)

  • The d_ptr field of this entry gives the address of the first byte in the Procedure Linkage Table (.PLT in the Section called Procedure Linkage Table).

DT_JMPREL (d_tag)

  • This entry is associated with a table of relocation entries for the PLT. For zSeries this entry is mandatory both for executable and shared object files. Moreover, the relocation table's entries must have a one-to-one correspondence with the PLT. The table of DT_JMPREL relocation entries is wholly contained within the DT_RELA referenced table. See the Section called Procedure Linkage Table for more information.

http://refspecs.linuxfoundation.org/ELF/zSeries/lzsabi0_zSeries/x2251.html

The symbol refers to the start of the .got section. Two words in the GOT are reserved:

The word at _GLOBAL_OFFSET_TABLE_[0] is set by the linkage editor to hold the address of the dynamic structure, referenced with the symbol _DYNAMIC. This allows a program, such as the dynamic linker, to find its own dynamic structure without having yet processed its relocation entries. This is especially important for the dynamic linker, because it must initialize itself without relying on other programs to relocate its memory image.

The word at _GLOBAL_OFFSET_TABLE_[1] is reserved for future use.

The Global Offset Table resides in the ELF .got section.

https://docs.oracle.com/cd/E19683-01/817-3677/chapter6-42444/index.html

Dynamic Section
If an object file participates in dynamic linking, its program header table will have an element of type PT_DYNAMIC. This segment contains the .dynamic section. A special symbol, _DYNAMIC, labels the section, which contains an array of the following structures, defined in sys/link.h:

typedef struct {
        Elf32_Sword d_tag;
        union {
                Elf32_Word      d_val;
                Elf32_Addr      d_ptr;
                Elf32_Off       d_off;
        } d_un;
} Elf32_Dyn;

typedef struct {
        Elf64_Xword d_tag;
        union {
                Elf64_Xword     d_val;
                Elf64_Addr      d_ptr;
        } d_un;
} Elf64_Dyn;

For each object with this type, d_tag controls the interpretation of d_un.

d_val

  • These objects represent integer values with various interpretations.

d_ptr

  • These objects represent program virtual addresses. A file's virtual addresses might not match the memory virtual addresses during execution. When interpreting addresses contained in the dynamic structure, the runtime linker computes actual addresses, based on the original file value and the memory "base address". For consistency, files do not contain relocation entries to correct addresses in the dynamic structure.

@honggyukim
Copy link
Owner Author

commit 3d4ea6b
Author: Namhyung Kim [email protected]
AuthorDate: Mon Jan 27 11:11:50 2014 +0900
Commit: Namhyung Kim [email protected]
CommitDate: Mon Jan 27 11:11:50 2014 +0900

plthook: Trace dynamic (PLT) symbols too

Second entry of the _GLOBAL_OFFSET_TABLE_ is an address of dynamic
symbol resolver in the dynamic linker.  Hijack the address to record
mcount information using dynamic symbol table.

To trace every invocation of such functions, it needs to set
LD_BIND_NOT environment variable.

commit 2f2baf2
Author: Namhyung Kim [email protected]
AuthorDate: Tue Feb 18 10:08:22 2014 +0900
Commit: Namhyung Kim [email protected]
CommitDate: Tue Feb 18 10:17:42 2014 +0900

Access _GLOBAL_OFFSET_TABLE_ through DT_PLTGOT info

Accessing through the .got or .got.plt section can be ambiguous.

commit cae6af7
Author: Namhyung Kim [email protected]
AuthorDate: Tue Feb 18 21:36:36 2014 +0900
Commit: Namhyung Kim [email protected]
CommitDate: Wed Feb 19 09:26:49 2014 +0900

Use mprotect() before updating GOT

On some systems (like Ubuntu 10.04) the GOT was protected from
overwriting so that it needs to be mprotected first.

Install SIGSEGV handler to catch such case and do mprotect() and then
restore original protection bits.  Note that other systems
(eg. Fedora) needs writable GOT for handle .fini section so we cannot
restore it to read-only blindly.

commit 30cf417
Author: Namhyung Kim [email protected]
AuthorDate: Tue Mar 11 13:31:42 2014 +0900
Commit: Namhyung Kim [email protected]
CommitDate: Tue Mar 11 15:22:14 2014 +0900

Prevent recursion on plthook

There was a recursion like below on some workload:

  plthook_entry
    -> mcount_entry
      -> mcount_prepare
        -> xmalloc
          -> plthook_entry

Add a per-thread recursion guard variable to protect it.

Note that the __libc_start_main() needs to be skipped since
it's a dynamic symbol that never returns.

@honggyukim
Copy link
Owner Author

honggyukim commented Mar 18, 2019

mcount_startup
  mcount_setup_plthook
    setup_{exe|mod}_plthook_data(struct dl_phdr_info *info, ...) -> offset = info->dlpi_addr;
      hook_pltgot
        find_got(..., offset)
          overwrite_pltgot(..., plt_hooker)

plt_hooker
  plthook_entry

dl_iterate_phdr

The info argument is a structure of the following type:

struct dl_phdr_info {
    ElfW(Addr)        dlpi_addr;  /* Base address of object */
    const char       *dlpi_name;  /* (Null-terminated) name of
                                     object */
    const ElfW(Phdr) *dlpi_phdr;  /* Pointer to array of
                                     ELF program headers
                                     for this object */
    ElfW(Half)        dlpi_phnum; /* # of items in dlpi_phdr */
};

(The ElfW() macro definition turns its argument into the name of an ELF data type suitable for the hardware architecture. For example, on a 32-bit platform, ElfW(Addr) yields the data type name Elf32_Addr. Further information on these types can be found in the <elf.h> and <link.h> header files.)

The dlpi_addr field indicates the base address of the shared object (i.e., the difference between the virtual memory address of the shared object and the offset of that object in the file from which it was loaded). The dlpi_name field is a null-terminated string giving the pathname from which the shared object was loaded.

@honggyukim
Copy link
Owner Author

honggyukim commented Mar 19, 2019

#define SKIP_SYM(func)  { #func, &uftrace_ ## func }

const struct plthook_skip_symbol plt_skip_syms[] = {
        SKIP_SYM(mcount),
        SKIP_SYM(_mcount),
        SKIP_SYM(__fentry__),
        SKIP_SYM(__gnu_mcount_nc),
        SKIP_SYM(__cyg_profile_func_enter),
        SKIP_SYM(__cyg_profile_func_exit),
};
size_t plt_skip_nr = ARRAY_SIZE(plt_skip_syms);

#undef SKIP_SYM
#undef ALIAS_DECL

/*
 * The `mcount` (and its friends) are part of uftrace itself,
 * so no need to use PLT hook for them.
 */
static void restore_plt_functions(struct plthook_data *pd)
{
        unsigned i, k;
        struct symtab *dsymtab = &pd->dsymtab;

        for (i = 0; i < dsymtab->nr_sym; i++) {
                bool skipped = false;
                unsigned long plthook_addr;
                unsigned long resolved_addr;
                struct sym *sym = dsymtab->sym_names[i];

                for (k = 0; k < plt_skip_nr; k++) {
                        const struct plthook_skip_symbol *skip_sym;

                        skip_sym = &plt_skip_syms[k];
                        if (strcmp(sym->name, skip_sym->name))
                                continue;

                        overwrite_pltgot(pd, 3 + i, skip_sym->addr);
                        pr_dbg2("overwrite [%u] %s: %p\n",
                                i, skip_sym->name, skip_sym->addr);

                        skipped = true;
                        break;
                }
                if (skipped)
                        continue;

                resolved_addr = pd->pltgot_ptr[3 + i];
                plthook_addr = mcount_arch_plthook_addr(pd, i);
                if (resolved_addr != plthook_addr) {
                        /* save already resolved address and hook it */
                        pd->resolved_addr[i] = resolved_addr;
                        overwrite_pltgot(pd, 3 + i, (void *)plthook_addr);
                        pr_dbg2("restore [%u] %s: %p (PLT: %#lx)\n",
                                i, sym->name, resolved_addr, plthook_addr);
                }
        }
}

@honggyukim
Copy link
Owner Author

$ nm libmcount/libmcount.so | grep mcount
0000000000025310 T mcount
...
                 w uftrace___gnu_mcount_nc
0000000000025310 t uftrace_mcount
                 w uftrace__mcount
$ nm libmcount/libmcount.so | grep cyg
000000000000a030 T __cyg_profile_func_enter
000000000000a230 T __cyg_profile_func_exit
000000000000a030 t uftrace___cyg_profile_func_enter
000000000000a230 t uftrace___cyg_profile_func_exit

The above weak references are used as follows:

/* use weak reference for non-defined (arch-dependent) symbols */                                                            
#define ALIAS_DECL(_sym)  extern __weak void (*uftrace_##_sym)(void);                                                        
                                                                                                                             
ALIAS_DECL(mcount);                                                                                                          
ALIAS_DECL(_mcount);                                                                                                         
ALIAS_DECL(__fentry__);                                                                                                      
ALIAS_DECL(__gnu_mcount_nc);                                                                                                 
ALIAS_DECL(__cyg_profile_func_enter);                                                                                        
ALIAS_DECL(__cyg_profile_func_exit);                                                                                         
                                                                                                                             
#define SKIP_SYM(func)  { #func, &uftrace_ ## func }                                                                         
                                                                                                                             
const struct plthook_skip_symbol plt_skip_syms[] = {                                                                         
        SKIP_SYM(mcount),                                                                                                    
        SKIP_SYM(_mcount),                                                                                                                                                                                                                                
        SKIP_SYM(__fentry__),                                                                                                
        SKIP_SYM(__gnu_mcount_nc),                                                                                           
        SKIP_SYM(__cyg_profile_func_enter),                                                                                  
        SKIP_SYM(__cyg_profile_func_exit),                                                                                   
};                                                                                                                           
size_t plt_skip_nr = ARRAY_SIZE(plt_skip_syms);                                                                              
                                                                                                                             
#undef SKIP_SYM                                                                                                              
#undef ALIAS_DECL                                    

@honggyukim
Copy link
Owner Author

10│ ENTRY(plt_hooker)
11│         @ we get called with
12│         @       stack[0] contains the return address from this call
13│         @       ip contains &GOT[n+3] (pointer to function)
14│         @       lr points to &GOT[2]
15
16├───────> push {r0-r3,ip,lr,pc}
17add r0, sp, #28
18sub r2, ip, lr
19sub r2, r2, #4
20│         lsr r1, r2, #2
21│         ldr r2, [lr, #-4]
22mov r3, sp
23bl plthook_entry
24
25cmp r0, $0
26│         beq 1f
27
28│         /*
29* if we skip the resolver, we also need to pop stack[0]
30* which saves the original 'lr'.
31*/
32str r0, [sp, #24]
33pop {r0-r3,ip,lr}
34add sp, sp, #8
35│         ldr lr, [sp, #-4]
36│         ldr pc, [sp, #-8] /* return */
37
381:
39│         ldr r2, .L2
40│ .LPIC0:
41add r2, pc, r2
42│         ldr r3, .L2+4
43│         ldr r1, [r2, r3]
44│         ldr r2, [r1]
45str r2, [sp, #24]
46pop {r0-r3,ip,lr,pc}
47
48│ .L3:
49│         .align 2
50│ .L2:
51│         .word _GLOBAL_OFFSET_TABLE_-(.LPIC0+8)
52│         .word plthook_resolver_addr(GOT)
53│ END(plt_hooker)
/usr/src/debug/uftrace/0.9.2-r0/git/arch/arm/plthook.S
(gdb) disassemble
Dump of assembler code for function plt_hooker:
=> 0x76fc1368 <+0>:     push    {r0, r1, r2, r3, r12, lr, pc}
   0x76fc136c <+4>:     add     r0, sp, #28
   0x76fc1370 <+8>:     sub     r2, r12, lr
   0x76fc1374 <+12>:    sub     r2, r2, #4
   0x76fc1378 <+16>:    lsr     r1, r2, #2
   0x76fc137c <+20>:    ldr     r2, [lr, #-4]
   0x76fc1380 <+24>:    mov     r3, sp
   0x76fc1384 <+28>:    blx     0x76fb3f04 <plthook_entry>
   0x76fc1388 <+32>:    cmp     r0, #0
   0x76fc138c <+36>:    beq     0x76fc13a4 <plt_hooker+60>
   0x76fc1390 <+40>:    str     r0, [sp, #24]
   0x76fc1394 <+44>:    pop     {r0, r1, r2, r3, r12, lr}
   0x76fc1398 <+48>:    add     sp, sp, #8
   0x76fc139c <+52>:    ldr     lr, [sp, #-4]
   0x76fc13a0 <+56>:    ldr     pc, [sp, #-8]
   0x76fc13a4 <+60>:    ldr     r2, [pc, #20]   ; 0x76fc13c0 <plt_hooker+88>
   0x76fc13a8 <+64>:    add     r2, pc, r2
   0x76fc13ac <+68>:    ldr     r3, [pc, #16]   ; 0x76fc13c4 <plt_hooker+92>
   0x76fc13b0 <+72>:    ldr     r1, [r2, r3]
   0x76fc13b4 <+76>:    ldr     r2, [r1]
   0x76fc13b8 <+80>:    str     r2, [sp, #24]
   0x76fc13bc <+84>:    pop     {r0, r1, r2, r3, r12, lr, pc}
   0x76fc13c0 <+88>:    andeq   r11, r0, r4, asr #31
   0x76fc13c4 <+92>:                    ; <UNDEFINED> instruction: 0xffffffe0
End of assembler dump.

(gdb) x/x 0x76fc13c0
0x76fc13c0 <plt_hooker+88>:     0x0000bfc4

(gdb) x/x 0x76fc13c4
0x76fc13c4 <plt_hooker+92>:     0xffffffe0

@honggyukim
Copy link
Owner Author

About RELRO

@honggyukim
Copy link
Owner Author

honggyukim commented Mar 19, 2019

plthook: opening executable image: /usr/lib/libxxx.so
plthook: "libxxx.so" is loaded at 0x729aa000 (id: 0, PLTGOT: 0x76d96000)
plthook: update module id to 0x5524d1c0 
plthook: found GOT at 0x76d96000 (file_addr: 0x43ec000, PLT resolver: 0)                                                                                                                                                                                  
plthook: restore GOT[3] to (nil) (__gmon_start__) (PLT: 0x72d26e7c)
plthook: restore GOT[4] to 0x72878c24 (__cxa_finalize) (PLT: 0x72d26e7c)
Disassembly of section .dynamic:
 
043bcd20 <.dynamic>:
 43bcd20:       00000003        andeq   r0, r0, r3
 43bcd24:       043ec000        ldrteq  ip, [lr], #-0   # <= same as PLTGOT and file_addr
 43bcd28:       00000002        andeq   r0, r0, r2
 43bcd2c:       00003fe8        andeq   r3, r0, r8, ror #31
 43bcd30:       00000017        andeq   r0, r0, r7, lsl r0
...
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x00000034 0x00000034 0x00160 0x00160 R   0x4
  LOAD           0x000000 0x00000000 0x00000000 0x41c9110 0x41c9110 R E 0x1000
  LOAD           0x41c9690 0x041ca690 0x041ca690 0x243198 0x410964 RW  0x1000
  DYNAMIC        0x43bbd20 0x043bcd20 0x043bcd20 0x001d8 0x001d8 RW  0x4
  NOTE           0x000194 0x00000194 0x00000194 0x00024 0x00024 R   0x4
  NOTE           0x440c808 0x0440d808 0x0440d808 0x00020 0x00020 RW  0x4
  GNU_EH_FRAME   0x41c90fc 0x041c90fc 0x041c90fc 0x00014 0x00014 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  EXIDX          0x387f670 0x0387f670 0x0387f670 0x19b830 0x19b830 R   0x4
  TLS            0x41c9690 0x041ca690 0x041ca690 0x00010 0x00018 R   0x8
  GNU_RELRO      0x41c9690 0x041ca690 0x041ca690 0x223970 0x223970 RW  0x8
...
Dynamic section at offset 0x43bbd20 contains 54 entries:
  Tag        Type                         Name/Value
 0x00000003 (PLTGOT)                     0x43ec000
 0x00000002 (PLTRELSZ)                   16360 (bytes)
 0x00000017 (JMPREL)                     0x378e88
 0x00000014 (PLTREL)                     REL
 0x00000011 (REL)                        0x49e50
 0x00000012 (RELSZ)                      3338296 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffa (RELCOUNT)                   409350
 0x00000006 (SYMTAB)                     0x1b8
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000005 (STRTAB)                     0xccd8
 0x0000000a (STRSZ)                      221394 (bytes)
 0x00000004 (HASH)                       0x42dac
Relocation section '.rel.dyn' at offset 0x49e50 ...
 Offset     Info    Type            Sym.Value  Sym. Name
...
043bd080  00007c15 R_ARM_GLOB_DAT    00000000   pthread_once@GLIBC_2.4
...
043ec170  00007c16 R_ARM_JUMP_SLOT   00000000   pthread_once@GLIBC_2.4
...
Symbol table '.dynsym' ...
...
124: 00000000     0 FUNC    GLOBAL DEFAULT  UND pthread_once@GLIBC_2.4 (28)

@honggyukim
Copy link
Owner Author

honggyukim commented Mar 26, 2019

commit ef7137f
Author: Namhyung Kim [email protected]
AuthorDate: Wed Aug 30 22:58:15 2017 +0900
Commit: Namhyung Kim [email protected]
CommitDate: Tue Sep 5 10:23:43 2017 +0900

    plthook: Introduce struct plthook_data
    
    This is a preparation to support nested PLT hooking which trace
    inter-library calls as well.  So each modules' PLT info should be
    managed separately using the plthook_data.
    
    Each data is identified by module_id and kept in plthook_modules list.

ommit bd4a1a7
Author: Namhyung Kim [email protected]
AuthorDate: Sun Sep 3 13:05:47 2017 +0900
Commit: Namhyung Kim [email protected]
CommitDate: Tue Sep 5 10:25:27 2017 +0900

    plthook: Restore already resolved PLT entries
    
    It might be possible for dynamic linker to resolve some symbols before
    uftrace hooks up the PLTGOT.  As we know the original PLT hook address,
    restore already resolved address so that we can see them in the output.

commit 4d39415
Author: Namhyung Kim [email protected]
AuthorDate: Thu Sep 7 19:05:25 2017 +0900
Commit: Namhyung Kim [email protected]
CommitDate: Thu Sep 7 21:24:52 2017 +0900

    plthook: Save and use PLT0 address on ARM
    
    In order to handle PLT hooking properly, it needs to know PLT0 address.
    But it's not deterministic so that we cannot calculate the address using
    the first symbol address.  So save it in the pd, and use it.
    
    Signed-off-by: Namhyung Kim <[email protected]>
diff --git a/arch/arm/mcount-support.c b/arch/arm/mcount-support.c
--- a/arch/arm/mcount-support.c
+++ b/arch/arm/mcount-support.c
@@ -604,7 +604,4 @@
 unsigned long mcount_arch_plthook_addr(struct plthook_data *pd, int idx)
 {
-       struct sym *sym;
-
-       sym = &pd->dsymtab.sym[0];
-       return sym->addr - ARCH_PLT0_SIZE;
+       return pd->plt_addr;
 }

commit bd4a1a7
Author: Namhyung Kim [email protected]
AuthorDate: Sun Sep 3 13:05:47 2017 +0900
Commit: Namhyung Kim [email protected]
CommitDate: Tue Sep 5 10:25:27 2017 +0900

    plthook: Restore already resolved PLT entries
    
    It might be possible for dynamic linker to resolve some symbols before
    uftrace hooks up the PLTGOT.  As we know the original PLT hook address,
    restore already resolved address so that we can see them in the output.
    
    Signed-off-by: Namhyung Kim <[email protected]>
diff --git a/libmcount/plthook.c b/libmcount/plthook.c
--- a/libmcount/plthook.c
+++ b/libmcount/plthook.c
@@ -121,1 +132,9 @@
+               resolved_addr = pd->pltgot_ptr[3 + i];
+               plthook_addr = mcount_arch_plthook_addr(pd, i);
+               if (resolved_addr != plthook_addr) {
+                       /* save already resolved address and hook it */
+                       pd->resolved_addr[i] = resolved_addr;
+                       overwrite_pltgot(pd, 3 + i, (void *)plthook_addr);
+                       pr_dbg2("restore [%u] %s: %p\n",
+                               i, dsymtab->sym[i].name, resolved_addr);
                }

commit 3f20adf
Author: Namhyung Kim [email protected]
AuthorDate: Mon Apr 30 14:51:01 2018 +0900
Commit: Namhyung Kim [email protected]
CommitDate: Mon Apr 30 15:27:17 2018 +0900

    plthook: Fix infinite loop in execl() on ARM
    
    In restore_plt_functions(), it tries to get address of some special
    functions (which cannot return normally for some reason).  It uses
    dlsym(RTLD_DEFAULT) but on ARM the function returns an address of PLT
    routine so the process will be stuck in an infinite loop.

@honggyukim
Copy link
Owner Author

Introduction to the ELF Format (Part VII): Dynamic Linking / Loading and the .dynamic section

@honggyukim
Copy link
Owner Author

commit b7230be
Author: Namhyung Kim [email protected]
AuthorDate: Mon Oct 15 11:32:46 2018 +0900
Commit: Namhyung Kim [email protected]
CommitDate: Thu Oct 18 14:16:16 2018 +0900

    plthook: Do not call dlsym() in the init routine
    
    It resulted in a crash on some systems.  It looks like a bug in glibc
    but we need to work around it somehow.
    
    Reported-by: Honggyu Kim <[email protected]>
    Signed-off-by: Namhyung Kim <[email protected]>
diff --git a/libmcount/plthook.c b/libmcount/plthook.c
--- a/libmcount/plthook.c
+++ b/libmcount/plthook.c
@@ -55,13 +55,22 @@
 static void resolve_pltgot(struct plthook_data *pd, int idx)
 {
        if (pd->resolved_addr[idx] == 0) {
-               char *name;
-               void *addr;
+               unsigned long addr;
+               struct sym *sym;
+
+               sym = &pd->dsymtab.sym[idx];
+               addr = (unsigned long) dlsym(RTLD_DEFAULT, sym->name);
+
+               /* On ARM dlsym(DEFAULT) returns the address of PLT */
+               if (unlikely(pd->base_addr <= addr &&
+                            addr < sym->addr + sym->size)) {
+                       void *real_addr = dlsym(RTLD_NEXT, sym->name);
 
-               name = pd->dsymtab.sym[idx].name;
-               addr = dlsym(RTLD_DEFAULT, name);
+                       if (real_addr)
+                               addr = (unsigned long)real_addr;
+               }
 
-               pr_dbg2("resolved addr of %s = %p\n", name, addr);
-               pd->resolved_addr[idx] = (unsigned long)addr;
+               pr_dbg2("resolved addr of %s = %p\n", sym->name, addr);
+               pd->resolved_addr[idx] = addr;
        }
 }

commit 8503de4
Author: Namhyung Kim [email protected]
AuthorDate: Wed Nov 15 13:41:10 2017 +0900
Commit: Namhyung Kim [email protected]
CommitDate: Mon Nov 27 09:45:01 2017 +0900

    plthook: Resolve address of pthread_exit()
    
    The PLT hooking relies on exit path to restore original PLT address so
    that it still can be called through PLT after dynamic resolver updates
    the address.  But as pthread_exit() never returns, there's no change to
    hook it up again.
    
    Add PLT_FL_RESOLVE to do it at entry time explicitly.
    
    Signed-off-by: Namhyung Kim <[email protected]>
diff --git a/libmcount/plthook.c b/libmcount/plthook.c
--- a/libmcount/plthook.c
+++ b/libmcount/plthook.c
@@ -76,0 +76,13 @@
+static void resolve_pltgot(struct plthook_data *pd, int idx)
+{
+       if (pd->resolved_addr[idx] == 0) {
+               char *name;
+               void *addr;
+
+               name = pd->dsymtab.sym[idx].name;
+               addr = dlsym(RTLD_DEFAULT, name);
+
+               pr_dbg2("resolved addr of %s = %p\n", name, addr);
+               pd->resolved_addr[idx] = (unsigned long)addr;
+       }
+}

honggyukim pushed a commit that referenced this issue Nov 10, 2019
This fixes a memory leak in live replay as follows:

  $ make ASAN=1 -j8
  $ ./uftrace -L. --no-pager t-abc
  # DURATION     TID     FUNCTION
     1.227 us [126865] | __monstartup();
     0.940 us [126865] | __cxa_atexit();
              [126865] | main() {
              [126865] |   a() {
              [126865] |     b() {
              [126865] |       c() {
     0.766 us [126865] |         getpid();
     1.791 us [126865] |       } /* c */
     2.281 us [126865] |     } /* b */
     2.714 us [126865] |   } /* a */
     3.200 us [126865] | } /* main */

  =================================================================
  ==126864==ERROR: LeakSanitizer: detected memory leaks

  Direct leak of 6 byte(s) in 1 object(s) allocated from:
      #0 0x7fdc57f5034f in strdup (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x6234f)
      #1 0x499e74 in __read_rstack /home/honggyu/work/uftrace/utils/fstack.c:2160
      #2 0x49a1b0 in peek_rstack /home/honggyu/work/uftrace/utils/fstack.c:2230
      #3 0x445b5d in command_replay /home/honggyu/work/uftrace/cmds/replay.c:1163
      #4 0x438558 in command_live /home/honggyu/work/uftrace/cmds/live.c:158
      #5 0x40b6c2 in main /home/honggyu/work/uftrace/uftrace.c:1166
      #6 0x7fdc568d682f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

Fixed: namhyung#940

Signed-off-by: Byeonggon Lee <[email protected]>
honggyukim added a commit that referenced this issue Nov 27, 2019
This patch is to fix the following memory leak.

  $ uftrace record t-abc
  $ uftrace report -t 3us --no-pager
    Total time   Self time       Calls  Function
    ==========  ==========  ==========  ====================
      4.163 us    1.000 us           1  main
      3.163 us    3.163 us           1  a

  =================================================================
  ==78057==ERROR: LeakSanitizer: detected memory leaks

  Direct leak of 6 byte(s) in 1 object(s) allocated from:
      #0 0x7f6c4cf5734f in strdup (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x6234f)
      #1 0x498e46 in process_perf_event /home/honggyu/work/uftrace/utils/perf.c:576
      #2 0x487755 in __read_rstack /home/honggyu/work/uftrace/utils/fstack.c:2060
      #3 0x488445 in read_rstack /home/honggyu/work/uftrace/utils/fstack.c:2210
      #4 0x440503 in build_function_tree /home/honggyu/work/uftrace/cmds/report.c:117
      #5 0x440ba3 in report_functions /home/honggyu/work/uftrace/cmds/report.c:227
      #6 0x4442ca in command_report /home/honggyu/work/uftrace/cmds/report.c:692
      #7 0x40b843 in main /home/honggyu/work/uftrace/uftrace.c:1190
      #8 0x7f6c4b8dd82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

  SUMMARY: AddressSanitizer: 6 byte(s) leaked in 1 allocation(s).

Fixed: namhyung#1023

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Nov 27, 2019
This patch is to fix the following heap buffer overflow problem.

  $ uftrace record --force gcc hello.c
  $ uftrace replay --no-pager
      ...
  =================================================================
  ==79233==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62e00003c3e8 at
                                    pc 0x00000047e08e bp 0x7ffec89b7cd0 sp 0x7ffec89b7cc0
  READ of size 8 at 0x62e00003c3e8 thread T0
      #0 0x47e08d in fstack_check_skip /home/honggyu/uftrace/utils/fstack.c:683
      #1 0x47e9af in fstack_skip /home/honggyu/uftrace/utils/fstack.c:780
      #2 0x43e24e in print_graph_rstack /home/honggyu/uftrace/cmds/replay.c:987
      #3 0x43f49b in command_replay /home/honggyu/uftrace/cmds/replay.c:1172
      #4 0x40b7f1 in main /home/honggyu/uftrace/uftrace.c:1184
      #5 0x7f8ecb67e82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
      #6 0x405bf8 in _start (/home/honggyu/usr/bin/uftrace+0x405bf8)

  0x62e00003c3e8 is located 24 bytes to the left of 40960-byte region [0x62e00003c400,0x62e000046400)
  allocated by thread T0 here:
      #0 0x7f8eccd2e7fa in __interceptor_calloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x987fa)
      #1 0x479eca in setup_task_handle /home/honggyu/uftrace/utils/fstack.c:61
      #2 0x47b0e2 in fstack_setup_task /home/honggyu/uftrace/utils/fstack.c:211
      #3 0x47c23b in fstack_setup_filters /home/honggyu/uftrace/utils/fstack.c:443
      #4 0x43f266 in command_replay /home/honggyu/uftrace/cmds/replay.c:1144
      #5 0x40b7f1 in main /home/honggyu/uftrace/uftrace.c:1184
      #6 0x7f8ecb67e82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

  SUMMARY: AddressSanitizer: heap-buffer-overflow /home/honggyu/uftrace/utils/fstack.c:683 fstack_check_skip

Fixed: namhyung#803

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Nov 28, 2019
This patch is to fix the following memory leak.

  $ uftrace record t-abc
  $ uftrace report -t 3us --no-pager
    Total time   Self time       Calls  Function
    ==========  ==========  ==========  ====================
      4.163 us    1.000 us           1  main
      3.163 us    3.163 us           1  a

  =================================================================
  ==78057==ERROR: LeakSanitizer: detected memory leaks

  Direct leak of 6 byte(s) in 1 object(s) allocated from:
      #0 0x7f6c4cf5734f in strdup (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x6234f)
      #1 0x498e46 in process_perf_event /home/honggyu/work/uftrace/utils/perf.c:576
      #2 0x487755 in __read_rstack /home/honggyu/work/uftrace/utils/fstack.c:2060
      #3 0x488445 in read_rstack /home/honggyu/work/uftrace/utils/fstack.c:2210
      #4 0x440503 in build_function_tree /home/honggyu/work/uftrace/cmds/report.c:117
      #5 0x440ba3 in report_functions /home/honggyu/work/uftrace/cmds/report.c:227
      #6 0x4442ca in command_report /home/honggyu/work/uftrace/cmds/report.c:692
      #7 0x40b843 in main /home/honggyu/work/uftrace/uftrace.c:1190
      #8 0x7f6c4b8dd82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

  SUMMARY: AddressSanitizer: 6 byte(s) leaked in 1 allocation(s).

Fixed: namhyung#1023

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Nov 28, 2019
This patch is to fix the following heap buffer overflow problem.

  $ uftrace record --force gcc hello.c
  $ uftrace replay --no-pager
      ...
  =================================================================
  ==79233==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62e00003c3e8 at
                                    pc 0x00000047e08e bp 0x7ffec89b7cd0 sp 0x7ffec89b7cc0
  READ of size 8 at 0x62e00003c3e8 thread T0
      #0 0x47e08d in fstack_check_skip /home/honggyu/uftrace/utils/fstack.c:683
      #1 0x47e9af in fstack_skip /home/honggyu/uftrace/utils/fstack.c:780
      #2 0x43e24e in print_graph_rstack /home/honggyu/uftrace/cmds/replay.c:987
      #3 0x43f49b in command_replay /home/honggyu/uftrace/cmds/replay.c:1172
      #4 0x40b7f1 in main /home/honggyu/uftrace/uftrace.c:1184
      #5 0x7f8ecb67e82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
      #6 0x405bf8 in _start (/home/honggyu/usr/bin/uftrace+0x405bf8)

  0x62e00003c3e8 is located 24 bytes to the left of 40960-byte region [0x62e00003c400,0x62e000046400)
  allocated by thread T0 here:
      #0 0x7f8eccd2e7fa in __interceptor_calloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x987fa)
      #1 0x479eca in setup_task_handle /home/honggyu/uftrace/utils/fstack.c:61
      #2 0x47b0e2 in fstack_setup_task /home/honggyu/uftrace/utils/fstack.c:211
      #3 0x47c23b in fstack_setup_filters /home/honggyu/uftrace/utils/fstack.c:443
      #4 0x43f266 in command_replay /home/honggyu/uftrace/cmds/replay.c:1144
      #5 0x40b7f1 in main /home/honggyu/uftrace/uftrace.c:1184
      #6 0x7f8ecb67e82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

  SUMMARY: AddressSanitizer: heap-buffer-overflow /home/honggyu/uftrace/utils/fstack.c:683 fstack_check_skip

Fixed: namhyung#803

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Nov 28, 2019
This patch is to fix the following memory leak.

  $ uftrace record t-abc
  $ uftrace report -t 3us --no-pager
    Total time   Self time       Calls  Function
    ==========  ==========  ==========  ====================
      4.163 us    1.000 us           1  main
      3.163 us    3.163 us           1  a

  =================================================================
  ==78057==ERROR: LeakSanitizer: detected memory leaks

  Direct leak of 6 byte(s) in 1 object(s) allocated from:
      #0 0x7f6c4cf5734f in strdup (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x6234f)
      #1 0x498e46 in process_perf_event /home/honggyu/work/uftrace/utils/perf.c:576
      #2 0x487755 in __read_rstack /home/honggyu/work/uftrace/utils/fstack.c:2060
      #3 0x488445 in read_rstack /home/honggyu/work/uftrace/utils/fstack.c:2210
      #4 0x440503 in build_function_tree /home/honggyu/work/uftrace/cmds/report.c:117
      #5 0x440ba3 in report_functions /home/honggyu/work/uftrace/cmds/report.c:227
      #6 0x4442ca in command_report /home/honggyu/work/uftrace/cmds/report.c:692
      #7 0x40b843 in main /home/honggyu/work/uftrace/uftrace.c:1190
      #8 0x7f6c4b8dd82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

  SUMMARY: AddressSanitizer: 6 byte(s) leaked in 1 allocation(s).

Fixed: namhyung#1023

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Nov 28, 2019
This patch is to fix the following heap buffer overflow problem.

  $ uftrace record --force gcc hello.c
  $ uftrace replay --no-pager
      ...
  =================================================================
  ==79233==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62e00003c3e8 at
                                    pc 0x00000047e08e bp 0x7ffec89b7cd0 sp 0x7ffec89b7cc0
  READ of size 8 at 0x62e00003c3e8 thread T0
      #0 0x47e08d in fstack_check_skip /home/honggyu/uftrace/utils/fstack.c:683
      #1 0x47e9af in fstack_skip /home/honggyu/uftrace/utils/fstack.c:780
      #2 0x43e24e in print_graph_rstack /home/honggyu/uftrace/cmds/replay.c:987
      #3 0x43f49b in command_replay /home/honggyu/uftrace/cmds/replay.c:1172
      #4 0x40b7f1 in main /home/honggyu/uftrace/uftrace.c:1184
      #5 0x7f8ecb67e82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
      #6 0x405bf8 in _start (/home/honggyu/usr/bin/uftrace+0x405bf8)

  0x62e00003c3e8 is located 24 bytes to the left of 40960-byte region [0x62e00003c400,0x62e000046400)
  allocated by thread T0 here:
      #0 0x7f8eccd2e7fa in __interceptor_calloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x987fa)
      #1 0x479eca in setup_task_handle /home/honggyu/uftrace/utils/fstack.c:61
      #2 0x47b0e2 in fstack_setup_task /home/honggyu/uftrace/utils/fstack.c:211
      #3 0x47c23b in fstack_setup_filters /home/honggyu/uftrace/utils/fstack.c:443
      #4 0x43f266 in command_replay /home/honggyu/uftrace/cmds/replay.c:1144
      #5 0x40b7f1 in main /home/honggyu/uftrace/uftrace.c:1184
      #6 0x7f8ecb67e82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

  SUMMARY: AddressSanitizer: heap-buffer-overflow /home/honggyu/uftrace/utils/fstack.c:683 fstack_check_skip

Fixed: namhyung#803

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Dec 1, 2019
This patch is to fix the following heap buffer overflow problem.

  $ uftrace record --force gcc hello.c
  $ uftrace replay --no-pager
      ...
  =================================================================
  ==79233==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62e00003c3e8 at
                                    pc 0x00000047e08e bp 0x7ffec89b7cd0 sp 0x7ffec89b7cc0
  READ of size 8 at 0x62e00003c3e8 thread T0
      #0 0x47e08d in fstack_check_skip /home/honggyu/uftrace/utils/fstack.c:683
      #1 0x47e9af in fstack_skip /home/honggyu/uftrace/utils/fstack.c:780
      #2 0x43e24e in print_graph_rstack /home/honggyu/uftrace/cmds/replay.c:987
      #3 0x43f49b in command_replay /home/honggyu/uftrace/cmds/replay.c:1172
      #4 0x40b7f1 in main /home/honggyu/uftrace/uftrace.c:1184
      #5 0x7f8ecb67e82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
      #6 0x405bf8 in _start (/home/honggyu/usr/bin/uftrace+0x405bf8)

  0x62e00003c3e8 is located 24 bytes to the left of 40960-byte region [0x62e00003c400,0x62e000046400)
  allocated by thread T0 here:
      #0 0x7f8eccd2e7fa in __interceptor_calloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x987fa)
      #1 0x479eca in setup_task_handle /home/honggyu/uftrace/utils/fstack.c:61
      #2 0x47b0e2 in fstack_setup_task /home/honggyu/uftrace/utils/fstack.c:211
      #3 0x47c23b in fstack_setup_filters /home/honggyu/uftrace/utils/fstack.c:443
      #4 0x43f266 in command_replay /home/honggyu/uftrace/cmds/replay.c:1144
      #5 0x40b7f1 in main /home/honggyu/uftrace/uftrace.c:1184
      #6 0x7f8ecb67e82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

  SUMMARY: AddressSanitizer: heap-buffer-overflow /home/honggyu/uftrace/utils/fstack.c:683 fstack_check_skip

Fixed: namhyung#803

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Jun 6, 2020
Sometimes it's useful to print backtrace or stacktrace by explicitly
write a request inside source code.

Example output:
  Stack trace:
    #0 uftrace() [0x410093]
    #1 uftrace() [0x410602]
    #2 uftrace() [0x4108e7]
    #3 uftrace(command_report+0x374) [0x411995]
    #4 uftrace(main+0x4f7) [0x40c7c5]
    #5 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f5e346d2830]
    #6 uftrace(_start+0x29) [0x40a0d9]

Some functions do not show their name, but it's because they are static functions.

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Jun 6, 2020
CHECK is same as assert, but dumps stacktrace before stopped.

However, DCHECK is only compiled when DEBUG=1 is on in debug mode build.
Otherwise, it's simply ignored and doesn't make any overhead.

It would be better if we add more assertion like statements with CHECK
and DCHECK.

Here is the example output.

  /home/honggyu/work/uftrace/cmds/report.c:98: add_remaining_fstack: CHECK `root' failed.
  Stack trace:
    #0 uftrace() [0x40ffed]
    #1 uftrace() [0x410542]
    #2 uftrace() [0x410827]
    #3 uftrace(command_report+0x374) [0x4118d5]
    #4 uftrace(main+0x4f7) [0x40c7c5]
    #5 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f1552007830]
    #6 uftrace(_start+0x29) [0x40a0d9]

  Please report this uftrace internal bug to https://github.com/namhyung/uftrace/issues
  Aborted (core dumped)

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Sep 30, 2020
The kernel tracing generates kernel-cpuXX.dat in 600 mode so it cannot
be read in other environments unlike other files that gives read
permission to other accounts.

  $ ls -l uftrace.data/
      ...
  -rw-r--r-- 1 root root   15910 Sep 29 09:15 info
  -rw-r--r-- 1 root root 4851625 Sep 29 09:15 kallsyms
  -rw------- 1 root root       0 Sep 29 09:15 kernel-cpu0.dat
  -rw------- 1 root root       0 Sep 29 09:15 kernel-cpu1.dat
      ...

It makes the recorded data not portable so the other user can see the
crash as follows.

  $ uftrace replay --no-pager
  Segmentation fault (core dumped)

The crash backtrace is as follows.

  $ uftrace replay --no-pager
  AddressSanitizer:DEADLYSIGNAL
  =================================================================
  ==37436==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008
                  (pc 0x0000004afdad bp 0x7ffd328c53d0 sp 0x7ffd328c53c0 T0)
  ==37436==The signal is caused by a READ memory access.
  ==37436==Hint: address points to the zero page.
      #0 0x4afdac in list_del /home/honggyu/work/uftrace/utils/list.h:111
      #1 0x4b7428 in reset_rstack_list /home/honggyu/work/uftrace/utils/fstack.c:1079
      #2 0x46d4a1 in finish_kernel_data /home/honggyu/work/uftrace/utils/kernel.c:1322
      #3 0x46d060 in setup_kernel_data /home/honggyu/work/uftrace/utils/kernel.c:1284
      #4 0x483c87 in open_data_file /home/honggyu/work/uftrace/utils/data-file.c:605
      #5 0x4308e3 in command_replay /home/honggyu/work/uftrace/cmds/replay.c:1175
      #6 0x40c707 in main /home/honggyu/work/uftrace/uftrace.c:1345
      #7 0x7fef4dfe282f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
      #8 0x405e08 in _start (/home/honggyu/usr/bin/uftrace+0x405e08)
  AddressSanitizer can not provide additional info.
  SUMMARY: AddressSanitizer: SEGV /home/honggyu/work/uftrace/utils/list.h:111 in list_del
  ==37436==ABORTING

This patch changes the permission of kernel-cpuXX.dat file from 600 to
644 to avoid this kind of segfault.

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Oct 2, 2020
The kernel tracing generates kernel-cpuXX.dat in 600 mode so it cannot
be read in other environments unlike other files that gives read
permission to other accounts.

  $ ls -l uftrace.data/
      ...
  -rw-r--r-- 1 root root   15910 Sep 29 09:15 info
  -rw-r--r-- 1 root root 4851625 Sep 29 09:15 kallsyms
  -rw------- 1 root root       0 Sep 29 09:15 kernel-cpu0.dat
  -rw------- 1 root root       0 Sep 29 09:15 kernel-cpu1.dat
      ...

It makes the recorded data not portable so the other user can see the
crash as follows.

  $ uftrace replay --no-pager
  Segmentation fault (core dumped)

The crash backtrace is as follows.

  $ uftrace replay --no-pager
  AddressSanitizer:DEADLYSIGNAL
  =================================================================
  ==37436==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008
                  (pc 0x0000004afdad bp 0x7ffd328c53d0 sp 0x7ffd328c53c0 T0)
  ==37436==The signal is caused by a READ memory access.
  ==37436==Hint: address points to the zero page.
      #0 0x4afdac in list_del /home/honggyu/work/uftrace/utils/list.h:111
      #1 0x4b7428 in reset_rstack_list /home/honggyu/work/uftrace/utils/fstack.c:1079
      #2 0x46d4a1 in finish_kernel_data /home/honggyu/work/uftrace/utils/kernel.c:1322
      #3 0x46d060 in setup_kernel_data /home/honggyu/work/uftrace/utils/kernel.c:1284
      #4 0x483c87 in open_data_file /home/honggyu/work/uftrace/utils/data-file.c:605
      #5 0x4308e3 in command_replay /home/honggyu/work/uftrace/cmds/replay.c:1175
      #6 0x40c707 in main /home/honggyu/work/uftrace/uftrace.c:1345
      #7 0x7fef4dfe282f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
      #8 0x405e08 in _start (/home/honggyu/usr/bin/uftrace+0x405e08)
  AddressSanitizer can not provide additional info.
  SUMMARY: AddressSanitizer: SEGV /home/honggyu/work/uftrace/utils/list.h:111 in list_del
  ==37436==ABORTING

This patch changes the permission of kernel-cpuXX.dat file from 600 to
644 to avoid this kind of segfault.

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Oct 4, 2020
This patch fixes the following heap buffer overflow.

  $ gcc -pg -g -o t-arg tests/s-arg.c

  $ uftrace record -a t-arg

  $ uftrace script -S scripts/dump.py --no-pager
  uftrace_begin(ctx)
    record  : False
    version : v0.9.4-129-g4be7 ( dwarf python luajit tui perf sched dynamic )
    cmds    :

  17910610.546322424  34457: [entry] __monstartup(4005b0) depth: 0
  17910610.546323121  34457: [exit ] __monstartup(4005b0) depth: 0
  17910610.546324914  34457: [entry] __cxa_atexit(4005d0) depth: 0
  17910610.546325298  34457: [exit ] __cxa_atexit(4005d0) depth: 0
  17910610.546325981  34457: [entry] main(400a6f) depth: 0
    args[0] <class 'int'>: 1
    args[1] <class 'int'>: 140727876517768
  17910610.546326972  34457: [entry] foo(40076c) depth: 1
    args[0] <class 'int'>: 3
  =================================================================
  ==34847==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000a8892 at pc 0x7ffb47655260 bp 0x7ffe54f558d0 sp 0x7ffe54f55078
  READ of size 4 at 0x6020000a8892 thread T0
      #0 0x7ffb4765525f  (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xd925f)
      #1 0x489f7e in setup_argument_context /home/honggyu/uftrace/utils/script-python.c:498
      #2 0x48a7a7 in python_uftrace_entry /home/honggyu/uftrace/utils/script-python.c:592
      #3 0x41395a in run_script_for_rstack /home/honggyu/uftrace/cmds/script.c:78
      #4 0x4146de in command_script /home/honggyu/uftrace/cmds/script.c:192
      #5 0x40c820 in main /home/honggyu/uftrace/uftrace.c:1366
      #6 0x7ffb45f0d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
      #7 0x405e08 in _start (/home/honkim01/usr/bin/uftrace+0x405e08)

  0x6020000a8892 is located 0 bytes to the right of 2-byte region [0x6020000a8890,0x6020000a8892)
  allocated by thread T0 here:
      #0 0x7ffb47688078 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c078)
      #1 0x489e85 in setup_argument_context /home/honggyu/uftrace/utils/script-python.c:491
      #2 0x48a7a7 in python_uftrace_entry /home/honggyu/uftrace/utils/script-python.c:592
      #3 0x41395a in run_script_for_rstack /home/honggyu/uftrace/cmds/script.c:78
      #4 0x4146de in command_script /home/honggyu/uftrace/cmds/script.c:192
      #5 0x40c820 in main /home/honggyu/uftrace/uftrace.c:1366
      #6 0x7ffb45f0d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Nov 7, 2020
This patch fixes the following heap buffer overflow.

  $ gcc -pg -g -o t-arg tests/s-arg.c

  $ uftrace record -a t-arg

  $ uftrace script -S scripts/dump.py --no-pager
  uftrace_begin(ctx)
    record  : False
    version : v0.9.4-129-g4be7 ( dwarf python luajit tui perf sched dynamic )
    cmds    :

  17910610.546322424  34457: [entry] __monstartup(4005b0) depth: 0
  17910610.546323121  34457: [exit ] __monstartup(4005b0) depth: 0
  17910610.546324914  34457: [entry] __cxa_atexit(4005d0) depth: 0
  17910610.546325298  34457: [exit ] __cxa_atexit(4005d0) depth: 0
  17910610.546325981  34457: [entry] main(400a6f) depth: 0
    args[0] <class 'int'>: 1
    args[1] <class 'int'>: 140727876517768
  17910610.546326972  34457: [entry] foo(40076c) depth: 1
    args[0] <class 'int'>: 3
  =================================================================
  ==34847==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000a8892 at pc 0x7ffb47655260 bp 0x7ffe54f558d0 sp 0x7ffe54f55078
  READ of size 4 at 0x6020000a8892 thread T0
      #0 0x7ffb4765525f  (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xd925f)
      #1 0x489f7e in setup_argument_context /home/honggyu/uftrace/utils/script-python.c:498
      #2 0x48a7a7 in python_uftrace_entry /home/honggyu/uftrace/utils/script-python.c:592
      #3 0x41395a in run_script_for_rstack /home/honggyu/uftrace/cmds/script.c:78
      #4 0x4146de in command_script /home/honggyu/uftrace/cmds/script.c:192
      #5 0x40c820 in main /home/honggyu/uftrace/uftrace.c:1366
      #6 0x7ffb45f0d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
      #7 0x405e08 in _start (/home/honkim01/usr/bin/uftrace+0x405e08)

  0x6020000a8892 is located 0 bytes to the right of 2-byte region [0x6020000a8890,0x6020000a8892)
  allocated by thread T0 here:
      #0 0x7ffb47688078 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10c078)
      #1 0x489e85 in setup_argument_context /home/honggyu/uftrace/utils/script-python.c:491
      #2 0x48a7a7 in python_uftrace_entry /home/honggyu/uftrace/utils/script-python.c:592
      #3 0x41395a in run_script_for_rstack /home/honggyu/uftrace/cmds/script.c:78
      #4 0x4146de in command_script /home/honggyu/uftrace/cmds/script.c:192
      #5 0x40c820 in main /home/honggyu/uftrace/uftrace.c:1366
      #6 0x7ffb45f0d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Jul 11, 2021
Sometimes it's useful to print backtrace or stacktrace by explicitly
write a request inside source code.

This patch implements stacktrace() function for this.  It's implemented
in two different versions.  In debug mode, it uses an external libunwind
library and it's output looks as follows.

  Stack trace:
    #1  0x00000045390a fstack_update_stack_count + 0x2c
    #2  0x000000454081 __fstack_consume + 0x48f
    #3  0x000000454b62 __read_rstack + 0x74e
    #4  0x000000454bbb read_rstack + 0x28
    #5  0x000000417386 command_replay + 0x28e
    #6  0x000000407bf9 main + 0x4c3
    #7  0x7f6d119f9840 __libc_start_main + 0xf0
    #8  0x0000004054e9 _start + 0x29
    #9  0x000000000000  + 0x29

In release mode, it's better not to rely on an external library, so it
just uses builtin backtrace() function, but it can't print static
functions.  The example output looks as follows.

  Stack trace:
    #1  uftrace() [0x442277]
    #2  uftrace() [0x443ced]
    #3  uftrace(command_replay+0xe8) [0x415f28]
    #4  uftrace(main+0x544) [0x40ae14]
    #5  /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f52982e0840]
    #6  uftrace(_start+0x29) [0x40af49]

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Jul 11, 2021
ASSERT is same as assert, but dumps stacktrace before stopped.

However, DASSERT is only compiled when DEBUG=1 is on in debug mode build.
Otherwise, it's simply ignored and doesn't make any overhead.

It would be better if we add more assertion like statements with ASSERT
and DASSERT.

When one of the assertion is failed, it prints the stack trace and
raises SIGTRAP signal so that it can be handled by a debugger, otherwise
it will just stop execution anyway.

Here is the example output.

  /data_sdb/honggyu/work/uftrace/git/uftrace/utils/fstack.c:2025: fstack_update_stack_count: ASSERT `!task' failed.
  Stack trace:
    #1  0x0000004559f2 fstack_update_stack_count + 0x66
    #2  0x000000456187 __fstack_consume + 0x48f
    #3  0x000000456c68 __read_rstack + 0x74e
    #4  0x000000456cc1 read_rstack + 0x28
    #5  0x000000417386 command_replay + 0x28e
    #6  0x000000407bf9 main + 0x4c3
    #7  0x7fd2a7bee840 __libc_start_main + 0xf0
    #8  0x0000004054e9 _start + 0x29
    #9  0x000000000000  + 0x29

  Please report this bug to https://github.com/namhyung/uftrace/issues.

  Trace/breakpoint trap

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Jul 11, 2021
Sometimes it's useful to print backtrace or stacktrace by explicitly
write a request inside source code.

This patch implements stacktrace() function for this.  It's implemented
in two different versions.  In debug mode, it uses an external libunwind
library and it's output looks as follows.

  Stack trace:
    #1  0x000000449ef9 parse_argspec + 0x5d6
    #2  0x00000044b76c parse_argument_spec + 0x70
    #3  0x00000044bf58 setup_trigger_action + 0x146
    #4  0x0000004329e5 find_dwarf_argspec + 0x153
    #5  0x000000432b18 find_auto_argspec + 0x4c
    #6  0x00000044b102 add_filter + 0x87
    #7  0x00000044c1bf add_trigger_entry + 0x15c
    #8  0x00000044c61f setup_trigger + 0x413
    #9  0x00000044c85b uftrace_setup_argument + 0x59
    #10 0x00000044e053 build_arg_spec + 0x4f
    #11 0x00000042a6fe walk_sessions + 0x4a
    #12 0x00000044e12d setup_fstack_args + 0x7d
    #13 0x00000043a353 open_data_file + 0x3ff
    #14 0x0000004171a5 command_replay + 0x7a
    #15 0x000000407ba9 main + 0x4c3
    #16 0x7f4e1df33840 __libc_start_main + 0xf0
    #17 0x000000405499 _start + 0x29

In release mode, it's better not to rely on an external library, so it
just uses builtin backtrace() function, but it can't print static
functions.  The example output looks as follows.

  Stack trace:
    #1  uftrace(parse_argspec+0x4d1) [0x43dd01]
    #2  uftrace() [0x43e44c]
    #3  uftrace(setup_trigger_action+0xb0) [0x43f4e0]
    #4  uftrace() [0x42a933]
    #5  uftrace(find_auto_argspec+0x29) [0x42aff9]
    #6  uftrace() [0x43f2f7]
    #7  uftrace() [0x43fa20]
    #8  uftrace() [0x440140]
    #9  uftrace(walk_sessions+0x39) [0x4248a9]
    #10 uftrace(setup_fstack_args+0xa6) [0x440bb6]
    #11 uftrace(open_data_file+0x16f) [0x43012f]
    #12 uftrace(command_replay+0x59) [0x415ee9]
    #13 uftrace(main+0x544) [0x40adc4]
    #14 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f8e3d944840]
    #15 uftrace(_start+0x29) [0x40aef9]

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Jul 11, 2021
ASSERT is same as assert, but dumps stacktrace before stopped.

However, DASSERT is only compiled when DEBUG=1 is on in debug mode build.
Otherwise, it's simply ignored and doesn't make any overhead.

It would be better if we add more assertion like statements with ASSERT
and DASSERT.

When one of the assertion is failed, it prints the stack trace and
raises SIGTRAP signal so that it can be handled by a debugger, otherwise
it will just stop execution anyway.

Here is the example output.

  /data_sdb/honggyu/work/uftrace/git/uftrace/utils/fstack.c:2025: fstack_update_stack_count: ASSERT `!task' failed.
  Stack trace:
    #1  0x0000004559f2 fstack_update_stack_count + 0x66
    #2  0x000000456187 __fstack_consume + 0x48f
    #3  0x000000456c68 __read_rstack + 0x74e
    #4  0x000000456cc1 read_rstack + 0x28
    #5  0x000000417386 command_replay + 0x28e
    #6  0x000000407bf9 main + 0x4c3
    #7  0x7fd2a7bee840 __libc_start_main + 0xf0
    #8  0x0000004054e9 _start + 0x29
    #9  0x000000000000  + 0x29

  Please report this bug to https://github.com/namhyung/uftrace/issues.

  Trace/breakpoint trap

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Jul 11, 2021
Sometimes it's useful to print backtrace or stacktrace by explicitly
write a request inside source code.

This patch implements stacktrace() function for this.  It's implemented
in two different versions.  In debug mode, it uses an external libunwind
library and it's output looks as follows.

  Stack trace:
    #1  0x000000449ef9 parse_argspec + 0x5d6
    #2  0x00000044b76c parse_argument_spec + 0x70
    #3  0x00000044bf58 setup_trigger_action + 0x146
    #4  0x0000004329e5 find_dwarf_argspec + 0x153
    #5  0x000000432b18 find_auto_argspec + 0x4c
    #6  0x00000044b102 add_filter + 0x87
    #7  0x00000044c1bf add_trigger_entry + 0x15c
    #8  0x00000044c61f setup_trigger + 0x413
    #9  0x00000044c85b uftrace_setup_argument + 0x59
    #10 0x00000044e053 build_arg_spec + 0x4f
    #11 0x00000042a6fe walk_sessions + 0x4a
    #12 0x00000044e12d setup_fstack_args + 0x7d
    #13 0x00000043a353 open_data_file + 0x3ff
    #14 0x0000004171a5 command_replay + 0x7a
    #15 0x000000407ba9 main + 0x4c3
    #16 0x7f4e1df33840 __libc_start_main + 0xf0
    #17 0x000000405499 _start + 0x29

In release mode, it's better not to rely on an external library, so it
just uses builtin backtrace() function, but it can't print static
functions.  The example output looks as follows.

  Stack trace:
    #1  uftrace(parse_argspec+0x4d1) [0x43dd01]
    #2  uftrace() [0x43e44c]
    #3  uftrace(setup_trigger_action+0xb0) [0x43f4e0]
    #4  uftrace() [0x42a933]
    #5  uftrace(find_auto_argspec+0x29) [0x42aff9]
    #6  uftrace() [0x43f2f7]
    #7  uftrace() [0x43fa20]
    #8  uftrace() [0x440140]
    #9  uftrace(walk_sessions+0x39) [0x4248a9]
    #10 uftrace(setup_fstack_args+0xa6) [0x440bb6]
    #11 uftrace(open_data_file+0x16f) [0x43012f]
    #12 uftrace(command_replay+0x59) [0x415ee9]
    #13 uftrace(main+0x544) [0x40adc4]
    #14 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f8e3d944840]
    #15 uftrace(_start+0x29) [0x40aef9]

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Jul 11, 2021
ASSERT is same as assert, but dumps stacktrace before stopped.

However, DASSERT is only compiled when DEBUG=1 is on in debug mode build.
Otherwise, it's simply ignored and doesn't make any overhead.

It would be better if we add more assertion like statements with ASSERT
and DASSERT.

When one of the assertion is failed, it prints the stack trace and
raises SIGTRAP signal so that it can be handled by a debugger, otherwise
it will just stop execution anyway.

Here is the example output.

  /data_sdb/honggyu/work/uftrace/git/uftrace/utils/fstack.c:2025: fstack_update_stack_count: ASSERT `!task' failed.
  Stack trace:
    #1  0x0000004559f2 fstack_update_stack_count + 0x66
    #2  0x000000456187 __fstack_consume + 0x48f
    #3  0x000000456c68 __read_rstack + 0x74e
    #4  0x000000456cc1 read_rstack + 0x28
    #5  0x000000417386 command_replay + 0x28e
    #6  0x000000407bf9 main + 0x4c3
    #7  0x7fd2a7bee840 __libc_start_main + 0xf0
    #8  0x0000004054e9 _start + 0x29
    #9  0x000000000000  + 0x29

  Please report this bug to https://github.com/namhyung/uftrace/issues.

  Trace/breakpoint trap

Signed-off-by: Honggyu Kim <[email protected]>
honggyukim added a commit that referenced this issue Jan 2, 2022
This patch enables clang build along with existing gcc build.

However, clang build with address sanitizer creates zero padding in
"uftrace.unit_test" section unlike gcc so we explicitly add no_sanitize
attribute to unittest for clang compiler.

Otherwise, clang with asan build hits the following error.

  $ make ASAN=1 unittest
      ...
  unittest.c:164:14: runtime error: null pointer passed as argument 1, which is declared to never be null
  /usr/include/string.h:141:33: note: nonnull attribute specified here
  SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior unittest.c:164:14 in
  AddressSanitizer:DEADLYSIGNAL
  =================================================================
  ==29255==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000000431b90 bp 0x7ffec6c3ed80 sp 0x7ffec6c3e520 T0)
  ==29255==The signal is caused by a READ memory access.
  ==29255==Hint: address points to the zero page.
      #0 0x431b90 in strcmp (/home/honggyu/uftrace/tests/unittest+0x431b90)
      #1 0x4cab6f in sort_tests /home/honggyu/uftrace/tests/unittest.c:164:7
      #2 0x47755b in qsort (/home/honggyu/uftrace/tests/unittest+0x47755b)
      #3 0x4c892e in setup_unit_test /home/honggyu/uftrace/tests/unittest.c:227:2
      #4 0x4c71db in main /home/honggyu/uftrace/tests/unittest.c:279:6
      #5 0x7f487ed1983f in __libc_start_main /build/glibc-S7Ft5T/glibc-2.23/csu/../csu/libc-start.c:291
      #6 0x41e568 in _start (/home/honggyu/uftrace/tests/unittest+0x41e568)

  AddressSanitizer can not provide additional info.
  SUMMARY: AddressSanitizer: SEGV (/home/honggyu/uftrace/tests/unittest+0x431b90) in strcmp
  ==29255==ABORTING

Signed-off-by: Honggyu Kim <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant