Jump to contentJump to page navigation: previous page [access key p]/next page [access key n]
documentation.suse.com / Creating a User Space Live Patch

Creating a User Space Live Patch

Publication Date: 27 Sep 2024
WHAT?

A guide on how to create a live patch on a system library.

WHY?

You want to understand the entire process of patching a library using user space live patching.

EFFORT

Approx. 5 minutes reading time.

GOAL

You will be able to create your own live patch.

1 Creating user space live patches

The following sections describe how to create live patches for libraries using the libpulp. The task comprises the following actions:

  • creating the patch

  • testing the patch

  • packing the patch into an RPM

  • deploying the patch

For more information regarding libpulp, refer to the ULP documentation.

2 Creating a live patch for a system library

In the procedure below, you will create a live patch for the malloc function. The patched function will initialize the memory allocated by the function with the string glibc-livepatches. To create the live patch, proceed as follows:

  1. Create a test program that checks if a live patch has been applied. The program should just allocate a region in memory and check if the string is there. An example follows:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdbool.h>
    #include <string.h>
    
    #define NUM_ATTEMPTS 30
    #define LEN 32
    
    static const char *const lp_string = "glibc-livepatch";
    
    int main(int argc, char *argv[])
    {
      for (int i = 0; i < NUM_ATTEMPTS; i++) {
        bool flag = false;
        char *m = malloc(LEN);
        m[LEN-1] = '\0';
    
        fprintf(stderr, "%s\n", m);
    
        if (m) {
          if (!strcmp(m, lp_string)) {
            flag = true;
          }
          free(m);
        }
    
        m = calloc(1, 32);
        if (m) {
          free(m);
        } else if (flag) {
          return 0;
        }
        sleep(1);
      }
    
      return 1;
    }
  2. Compile the program:

    > gcc -o test test.c
  3. Create a file containing the new malloc function, for example, libc_livepatch1.c.

    The code snippet shown below implements the new malloc function by using the calloc function to allocate memory and then overwrite its content with the glibc-livepatch string. The file is called libc_livepatch1.c.

    #include <stdlib.h>
    #include <string.h>
    
    #define MIN(x, y) ((x) < (y) ? (x) : (y))
    
    static const char *const lp_string = "glibc-livepatch";
    
    void *malloc_lp(size_t s)
    {
      char *block = calloc(1, s);
      if (block && s > 0) {
        int lp_string_len = strlen(lp_string);
        int copy_len = MIN(lp_string_len + 1, s);
    
        memcpy(block, lp_string, copy_len);
        block[s-1] = '\0';
      }
      return block;
    }

    Compile the file for x86 as follows:

    > gcc -fPIC -fpatchable-function-entry=16,14 -shared -o  libc_livepatch1.so libc_livepatch1.c
  4. Create a live patch description file. The file must contain the following information:

    • A path to the live patch container, in this case the path to the libc_livepatch1.so.6 file.

    • A path to the target library— /usr/lib64/libc.so in this case.

    • The function name that will be patched and its replacement. You can also describe multiple replacements.

    • The description file for the example live patch looks as follows:

      libc_livepatch1.so
      @/usr/lib64/libc.so.6
      __libc_malloc:malloc_lp

      The patch is for the __libc_malloc function, not for malloc, because the malloc name is an indirect function, not a regular function, and in the end it calls the __libc_malloc function. For details about ifunc, refer to the indirect function description.

  5. Build the live patch container:

    > gcc -fPIC -fpatchable-function-entry=16,14 -shared -o libc_livepatch1.so libc_livepatch1.c
  6. Embed the description file into the live patch container:

    > ulp packer libc_livepatch1.dsc

3 Testing the live patch

After you prepare the live patch and its test program as described in Section 2, “Creating a live patch for a system library”, you can test if the live patch works:

  1. Launch the test program:

    > LD_PRELOAD=/usr/lib64/libpulp.so.0 ./test
  2. Install the live patch:

    > ulp trigger libc_livepatch.so

If the installation was successful, the ULP tool displays the message SUCCESS. You should also see the glibc-livepatch message from the test program.

4 Files required in a live patch package

To prepare a live patch package, use the glibc-livepatches as a reference source. The package contains the following files:

4.1 The glibc-livepatches.spec file

The glibc-livepatches.spec file contains instructions on how to prepare, build, install and apply the live patch.

The file also calls the ulp_post_hook script. The script calls the ULP trigger with extracted boilerplate code to avoid adding such a code to every package. To check the code, view the file /usr/lib/userspace-livepatch/rpm-helper. The call to the ULP trigger is performed with the –revert-all command that reverts all already installed patches. Make sure that your patch is cumulative and contains fixes for the previous versions of the patch.

4.2 The glibc-livepatches.tar.xz file

The glibc-livepatches.tar.xz file contains the actual source code of the live patch. The archive must also contain information about all glibc versions issued for the particular SLES version.

5 Making an RPM from the live patch

To prepare a live patch package, follow the steps below:

  1. Extract the glibc-livepatches package.

  2. Add the live patch source files.

  3. Run the following command to update the package so that it includes all glibc versions released for the particular SLES version.

        > make download
  4. Run the following command to check if the live patch is buildable.

    > make

    If the process is successfully completed, one .so file is created for each glibc version. For example, libc_2.31-150300.26.5_livepatch1.so.

  5. To ensure that your package does not contain any of your temporary build files, run the command:

    > make clean
  6. To create a new .tar.xz, run the command:

    > make dist
  7. Build the package with OSC.

  8. If the build is successful, you can upload the package to a repository.

6 Deploying the live patch package

If you have the RPM package ready and tested, you can submit a merge request to the glibc-livepatches SLES16.0 update repository.

> osc -A https://api.suse.de mr YOUR_HOME glibc-livepatches SUSE:SLE-15-SP5:GA