| LIBPULP(7) | Libpulp Overview | LIBPULP(7) |
Libpulp - Userspace Live Patching
Libpulp is a framework that enables live patching of userspace processes. In other words, it allows that a running process be modified without restarting the whole application. Libpulp is composed of a library, which provides live patching capabilities to running processes, as well as of a collection of tools to perform live patching operations.
The following requirements and restrictions must be met for live patching operations to work:
Creating and applying live patches is achieved with the following tools:
A live patch is very simple. It is composed of a dynamic shared object (DSO) containing replacement functions, and of a metadata file describing which target library and functions they are meant for. The DSO is no different than a regular shared library, in the sense that it is built from regular source code into a shared object. The metadata is described below.
A metadata file describes a live patch. It contains three pieces of information:
/usr/lib64/livepatches/libm_livepatch_20210514.so
@/lib64/libm.so.6
hypot:hypot_v2
gamma:new_gamma
atan:atan_new
#narenas:ulpr_narenas:00000000001c2720:0000000000004020
#main_arena:ulpr_main_arena:00000000001c3a00:0000000000004060
Notice, however, that even though the paths mentioned above refer to files in storage, the patches are not applied to the files themselves. Rather, they are applied to running processes that have loaded these files. See ulp_trigger(1).
The programs and commands below demonstrate how to use Libpulp.
First, a live patchable library must be created and properly compiled:
#include <stdlib.h>
#include <string.h>
char *
proverb(void)
{
int selection;
char *result;
char *proverbs[] = {
"A picture is worth a thousand words",
"Actions speak louder than words",
"An apple a day keeps the doctor away",
"Birds of a feather flock together",
"Do not judge a book by its cover",
"Never look a gift horse in the mouth",
"Practice makes perfect",
"Slow and steady wins the race",
"There is no place like home",
"Too many cooks spoil the broth"
};
selection = rand() % (sizeof(proverbs) / sizeof(char *));
result = strdup(proverbs[selection]);
return result;
}
As explained in the Target Libraries Rebuilding section above, in order to be live patchable, a target library must be built with patchable function entries. Apart from that, it may be optionally post-processed with ulp_post(1):
$ gcc library.c -o library.so \
-shared -fPIC \
-fpatchable-function-entry=24,22 $ ulp_post library.so
Next, a program that uses the library:
#include <stdio.h>
#include <unistd.h>
char *proverb(void);
int
main(void)
{
char buffer[128];
printf("%d\n", getpid());
while (fgets(buffer, sizeof(buffer), stdin))
printf("%s\n", proverb());
return 0;
}
Applications themselves do not require rebuilds, but for the sake of completeness, commands to build an application and link it to a library in a non-default location are shown below:
$ gcc program.c -L$PWD -lrary -Wl,-rpath=$PWD -o program
After startup, the program prints its own PID, which will be used further down in this example. Also, hitting ENTER causes the program to call into the library, which replies with a message.
$ LD_PRELOAD=libpulp.so ./program
libpulp loaded...
12345
<ENTER>
Birds of a feather flock together
<ENTER>
An apple a day keeps the doctor away
(and so on...)
Next, recall that a live patch can only replace entire functions (see Patch Granularity), thus the following live patch source provides a reimplementation of the proverbs function, giving it a different name to avoid clashes:
#include <string.h>
char *
proverb_v2(void)
{
return strdup("All good things must come to an end");
}
Live patches must be built like shared libraries (notice the use of the -shared option):
$ gcc livepatch.c -shared -fPIC -o livepatch.so
Next, recall that a live patch is not only composed of the object created above; it also requires a metadata file, which lets Libpulp know which library the live patch refers to, as well as it provides the correlation between original and replaced functions. A metadata file is built out of a description file.
/absolute/path/to/livepatch.so
@/absolute/path/to/library.so
proverb:proverb_v2
Converting from description to metadata is accomplished with ulp_packer(1):
$ ulp_packer livepatch.dsc -o livepatch.ulp
Finally, ulp_trigger(1) can be used to connect to the target process and apply the live patch (note the PID specification, using the -p option):
$ ulp_trigger -p 12345 livepatch.ulp
Wrapping up, the target process is now live patched and should behave differently when ENTER is hit in its controlling terminal:
(...)
<ENTER>
All good things must come to an end
<ENTER>
All good things must come to an end
ptrace(2), ulp_packer(1), ulp_reverse(1), ulp_dump(1), ulp_post(1), ulp_trigger(1), ulp_check(1).