1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
|
.\" libpulp - User-space Livepatching Library
.\"
.\" Copyright (C) 2021 SUSE Software Solutions GmbH
.\"
.\" This file is part of libpulp.
.\"
.\" libpulp is free software; you can redistribute it and/or
.\" modify it under the terms of the GNU Lesser General Public
.\" License as published by the Free Software Foundation; either
.\" version 2.1 of the License, or (at your option) any later version.
.\"
.\" libpulp is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
.\" Lesser General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with libpulp. If not, see <http://www.gnu.org/licenses/>.
.TH LIBPULP 7 "" "" "Libpulp Overview"
.SH NAME
Libpulp \- Userspace Live Patching
.SH INTRODUCTION
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.
.SH REQUIREMENTS AND RESTRICTIONS
.PP
The following requirements and restrictions must be met for live patching
operations to work:
.TP
.B Process Startup
Live patches cannot be applied to every process in the system. In order for a
process to be eligible for live patching, it must dynamically load libpulp.so,
the runtime support for live patching operations. Typically, dynamically linked
libraries needed by a program are automatically loaded into memory during
process initialization. However, since live patching operations are not
called by the program itself (but from an external tool), there is no real
dependency, and libpulp.so might be missing from DT_NEEDED entries (see
ld.so(8)). To bridge this gap, processes should be started with
LD_PRELOAD=libpulp.so.
.TP
.B Target Libraries Rebuilding
Not every library in the system is eligible for live patching, only those that
have been previously prepared to be so. These are referred to as
.IR "target libraries" "."
Making a target library does not require changes to its source code, however it
requires rebuilding the library with patchable function entries (see
.IR -fpatchable-function-entry
in
.IR gcc (1));
.\" XXX: Describe why 24,22 is the argument to -fpatchable-function-entry.
.TP
.B Patch Granularity
Live patches cannot be applied to arbitrarily tiny bits of a program. When a
live patch is applied, it replaces entire functions at a time.
.TP
.B Function visibility
Live patches cannot be applied to every function in a process. First, only
functions belonging to shared libraries can be live patched; functions in the
application itself, or functions linked from static libraries are not eligible.
Secondly, only externally visible (GLOBAL or WEAK) functions can be targets of
a live patch.
.TP
.B Process Permissions
Libpulp uses ptrace to attach to target processes, thus only the owner of a
process (or root) can apply a live patch to it. Moreover, on systems that have
Linux Security Modules (LSM) enabled, some extra convincing might be required
(see Ptrace access mode checking in
.IR ptrace (2)).
.SH TOOLS
Creating and applying live patches is achieved with the following tools:
.TP 16
.BR ulp_post (1)
Converts the sequences of one-byte nops at the entry of patchable functions
into multi-byte nops.
.TP 16
.BR ulp_packer (1)
Creates a live patch metadata file based on the live patch description file
that it takes as argument. The metadata file can later be used by
.IR ulp_trigger (1)
to actually apply the live patch to a running process. For more information
about the description file format, see the METADATA section.
.TP 16
.BR ulp_reverse (1)
Opens the metadata file passed as argument and creates a new metadata file that
can be used to reverse a previously applied live patch.
.TP 16
.BR ulp_dump (1)
Parses the metadata file passed as argument and prints its contents in
human-readable format.
.TP 16
.BR ulp_trigger (1)
Applies the live patch described by the metadata file received as argument to
the process with the specified
.IR pid .
.TP 16
.BR ulp_check (1)
Checks if the live patch referred to by the metadata file has already been
applied to the process with the specified
.IR pid .
.SH ANATOMY OF LIVE PATCHES
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.
.SH METADATA
A metadata file describes a live patch. It contains three pieces of
information:
.TP
.B Path to live patch DSO
Live patching operates on a function-by-function basis (see Patch Granularity).
These functions are provided in a dynamic shared object file. The absolute path
to this file is recorded in the metadata.
.TP
.B Path to the target library DSO
Each live patch contains replacement functions for a specific library. The
absolute path to the in-disk location of the library is recorded in the
metadata. Libpulp compares this path against the memory mappings of the target
process, as provided by
.IR procfs (5),
thus, even if the library is removed from disk between the starting of the
process and the application of the live patch, the comparison works.
.TP
.B List of replacement functions
Libpulp needs a correlation between original functions in the target library
and replacement functions provided by the live patch. This correlation is
recorded in the metadata file as a list of pairs of functions.
.TP
.B Description file format
The metadata file is created based on a description file. The description file
is rather simple. The first line contains the absolute path to the live patch
DSO. The second line starts with the
.I @
character, immediately followed by the absolute path to the target library DSO.
Subsequent lines, when not preceded by the
.I #
character, provide the list of replacement functions, where each line starts
with the name of the original function, followed by a colon, then by the
name of the replacement function. Finally, subsequent lines that do start with
.I #
specify the offsets that local (not-exported) variables in the target library
have from the beginning of the library load location, as well as the offsets of
references to those variables in the live patch object. Each line is composed
of four items separated by colons: the name of the variable in the target
library; the name of a reference to it in the live patch object; the offset of
the library variable within the library DSO; the offset of the reference
variable within the live patch DSO. These offsets are used by Libpulp to enable
access to local variables from the live patch.
.IP
For example, a live patch to the math library could have a description file
that looked like the following snippet (paths may differ across distributions):
.RS
.IP
.EX
\&
/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
.EE
.RE
.PP
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
.IR ulp_trigger (1).
.SH EXAMPLES
The programs and commands below demonstrate how to use Libpulp.
.PP
First, a live patchable library must be created and properly compiled:
.TP
.B Library source
.EX
\&
#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;
}
.EE
.PP
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
.IR ulp_post (1):
.IP
.EX
\&
$ gcc library.c -o library.so \\
-shared -fPIC \\
-fpatchable-function-entry=24,22
$ ulp_post library.so
.EE
.PP
Next, a program that uses the library:
.TP
.B Program source
.EX
\&
#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;
}
.EE
.PP
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:
.IP
.EX
\&
$ gcc program.c -L$PWD -lrary -Wl,-rpath=$PWD -o program
.EE
.PP
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.
.IP
.EX
\&
$ 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...)
.EE
.PP
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
.I proverbs
function, giving it a different name to avoid clashes:
.TP
.B Live patch source
.EX
\&
#include <string.h>
char *
proverb_v2(void)
{
return strdup("All good things must come to an end");
}
.EE
.PP
Live patches must be built like shared libraries (notice the use of the
.I -shared
option):
.IP
.EX
\&
$ gcc livepatch.c -shared -fPIC -o livepatch.so
.EE
.PP
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.
.TP
.B Description file
.EX
\&
/absolute/path/to/livepatch.so
@/absolute/path/to/library.so
proverb:proverb_v2
.EE
.PP
Converting from description to metadata is accomplished with
.IR ulp_packer (1):
.IP
.EX
\&
$ ulp_packer livepatch.dsc -o livepatch.ulp
.EE
.PP
Finally,
.IR ulp_trigger (1)
can be used to connect to the target process and apply the live patch (note the
PID specification, using the
.I -p
option):
.IP
.EX
\&
$ ulp_trigger -p 12345 livepatch.ulp
.EE
.PP
Wrapping up, the target process is now live patched and should behave
differently when ENTER is hit in its controlling terminal:
.IP
.EX
\&
(...)
<ENTER>
All good things must come to an end
<ENTER>
All good things must come to an end
.EE
.SH SEE ALSO
.BR ptrace (2),
.BR ulp_packer (1),
.BR ulp_reverse (1),
.BR ulp_dump (1),
.BR ulp_post (1),
.BR ulp_trigger (1),
.BR ulp_check (1).
|