File: libpulp.7

package info (click to toggle)
libpulp 0.2.8-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,964 kB
  • sloc: ansic: 11,257; python: 1,110; sh: 881; makefile: 824; cpp: 582; asm: 124
file content (350 lines) | stat: -rw-r--r-- 11,524 bytes parent folder | download
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).