SUSE / libpulp

libpulp enables live patching in user space applications.
GNU Lesser General Public License v2.1
55 stars 11 forks source link

[Regression] GLIBC livepatches broken since commit 0b8eb6104aa #79

Closed giulianobelinassi closed 2 years ago

giulianobelinassi commented 3 years ago

For some reason parsing the ELF PHDRs in libc.so.6 seems to not be returning the correct address. This is breaking glibc livepatches, althrough this commit is required in order to get openssl livepatches to work.

Probably the solution would be finding the target library path using the dl_iterate, but still dlsym that file for the correct symbol.

giulianobelinassi commented 3 years ago

So after further investigation, the reason why the livepatch failed on the current commit is:

  1. Assume we want to patch strcpy. This function has the following prototype: char * strcpy(char *, const char *)
  2. The following function is an intuitive replacement for it:

    char *mystrcpy(char *dst, const char *src)
    {
    char *rdst = dst;
    while (*src != '\0')
      *dst++ = *src++;
    *dst = '\0';
    
    return rdst;
    }
  3. Setting this function to patch glibc 2.34 libc.so.6's strcpy will result in a crash. The reason of this crash is that strcpy is actually an alias to strcpy_ifunc (notice that the offsets are the same, thus they point to the same function):
    objdump -t libc.so.6 | grep strcpy
    **00000000000b4d9e** l     F .text  0000000000000083              **strcpy_ifunc**
    00000000001adf50 l     F .text  0000000000000433              __strcpy_evex
    00000000000b4d9e l   i   .text  0000000000000083              __GI_strcpy
    0000000000185470 l     F .text  00000000000017a2              __strcpy_ssse3
    000000000019a050 l     F .text  0000000000000388              __strcpy_avx2
    00000000000c9f10 l     F .text  00000000000000ec              __strcpy_sse2
    00000000000ca0f0 l     F .text  0000000000000623              __strcpy_sse2_unaligned
    00000000001a69a0 l     F .text  00000000000003a9              __strcpy_avx2_rtm
    00000000000bbf2e l     F .text  00000000000000ba              __old_strcpy_small
    00000000000bbf2e g     F .text  00000000000000ba              __strcpy_small@GLIBC_2.2.5
    **00000000000b4d9e** g   i   .text  0000000000000083              **strcpy**
    0000000000135a0e g     F .text  0000000000000038              __strcpy_chk
  4. strcpy_ifunc actually is just a selector for an optimized strcpy function. On my machine, it returns a pointer to __strcpy_evex, and this address is put into strcpy's entry on the .plt table of the process.
  5. Therefore, if we actually write the following patch container:
    
    void *mystrcpy_selector()
    {
    return (void *) mystrcpy;
    }

char mystrcpy(char dst, const char src) { char rdst = dst; while (src != '\0') dst++ = src++; dst = '\0';

return rdst; }


and instruct to patch `strcpy` to `mystrcpy_selector`, the livepatch succeeds.
6. If the selector has already run, then `__strcpy_evex` will be at `strcpy` entry at plt. This is not a problem if that function is written in C, because `-fpatchable-function-entry` would generate patchable assembly for it. However,  `__strcpy_evex` is written in assembly and thus cannot be livepatched.
giulianobelinassi commented 2 years ago

Since this is not a regression, I am closing this.