Skip to content

uaddr(), usym(), ustack to support PIE ASLR #75

@brendangregg

Description

@brendangregg

Ubuntu 18.04 Bionic (and other OSes) have switched to randomizing the address space layout, which breaks simple approaches for symbol resolution. From https://wiki.ubuntu.com/BionicBeaver/ReleaseNotes#Security_Improvements:

In Ubuntu 18.04 LTS, gcc is now set to default to compile applications as position independent executables (PIE) as well as with immediate binding, to make more effective use of Address Space Layout Randomization (ASLR).

The bpftrace uaddr() call needs to work on both normal executables, as well as PIE executables (gcc -pie -fpie). Here's how to tell the difference:

# file uaddr-old uaddr-pie
uaddr-old: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=1babcdea1d0220ae6982428da4e7e4c665c587d7, not stripped
uaddr-pie: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=01419c4c8cddc834734c552097af169b83b7d77e, not stripped

From the above output, uaddr-old is an "executable", whereas uaddr-pie is a "shared object".

You can also see this in the address space of a running process:

# pmap -x `pgrep -n uaddr-old` | head
30157:   ./uaddr-old
Address           Kbytes     RSS   Dirty Mode  Mapping
0000000000400000       4       4       0 r-x-- uaddr-old
0000000000400000       0       0       0 r-x-- uaddr-old
0000000000600000       4       4       4 r---- uaddr-old
0000000000600000       0       0       0 r---- uaddr-old
0000000000601000       4       4       4 rw--- uaddr-old
0000000000601000       0       0       0 rw--- uaddr-old
00007ff992637000    1792     804       0 r-x-- libc-2.23.so
00007ff992637000       0       0       0 r-x-- libc-2.23.so

# pmap -x `pgrep -n uaddr-pie` | head
30158:   ./uaddr-pie
Address           Kbytes     RSS   Dirty Mode  Mapping
0000561202e7b000       4       4       0 r-x-- uaddr-pie
0000561202e7b000       0       0       0 r-x-- uaddr-pie
000056120307b000       4       4       4 r---- uaddr-pie
000056120307b000       0       0       0 r---- uaddr-pie
000056120307c000       4       4       4 rw--- uaddr-pie
000056120307c000       0       0       0 rw--- uaddr-pie
00007feec176e000    1792     776       0 r-x-- libc-2.23.so
00007feec176e000       0       0       0 r-x-- libc-2.23.so

Which means techniques like objdump no longer work:

# objdump -tT uaddr-old | grep my
0000000000400500 l     F .text	0000000000000000              frame_dummy
0000000000600e10 l     O .init_array	0000000000000000              __frame_dummy_init_array_entry
0000000000400526 g     F .text	0000000000000011              mysleep
0000000000601038 g     O .data	0000000000000008              mystring

# objdump -tT uaddr-pie | grep my
0000000000000740 l     F .text	0000000000000000              frame_dummy
0000000000200de0 l     O .init_array	0000000000000000              __frame_dummy_init_array_entry
0000000000000770 g     F .text	0000000000000011              mysleep
0000000000201038 g     O .data	0000000000000008              mystring

However:

# bpftrace -e 'uprobe:/root/uaddr-old:mysleep { printf("hit at %llx\n", reg("ip")); }'
Attaching 1 probe...
hit at 400526
hit at 400526
^C

# bpftrace -e 'uprobe:/root/uaddr-pie:mysleep { printf("hit at %llx\n", reg("ip")); }'
Attaching 1 probe...
hit at 561202e7b770
hit at 561202e7b770
^C

uprobe already works for both!

uprobe uses bcc_resolve_symname() to get the offset. Maybe we can do the same here, since it seems to already deal with PIE.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions