Commit | Line | Data |
---|---|---|
df08d338 JR |
1 | /* |
2 | * Copyright (C) 2019 Jonathan Rajotte <jonathan.rajotte-julien@efficios.com> | |
3 | * | |
4 | * SPDX-License-Identifier: LGPL-2.1-only | |
5 | * | |
6 | */ | |
7 | ||
8 | #include <assert.h> | |
58daac01 | 9 | #include <common/credentials.h> |
df08d338 | 10 | #include <common/error.h> |
0f7c2963 JR |
11 | #include <common/hashtable/hashtable.h> |
12 | #include <common/hashtable/utils.h> | |
df08d338 | 13 | #include <common/macros.h> |
0f7c2963 | 14 | #include <common/mi-lttng.h> |
df08d338 | 15 | #include <common/payload-view.h> |
0f7c2963 | 16 | #include <common/payload.h> |
df08d338 JR |
17 | #include <common/runas.h> |
18 | #include <lttng/event-rule/event-rule-internal.h> | |
e60e9a9a | 19 | #include <lttng/event-rule/kernel-uprobe-internal.h> |
df08d338 JR |
20 | #include <lttng/userspace-probe-internal.h> |
21 | ||
22 | #define IS_UPROBE_EVENT_RULE(rule) \ | |
e60e9a9a | 23 | (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE) |
df08d338 | 24 | |
e60e9a9a | 25 | static void lttng_event_rule_kernel_uprobe_destroy(struct lttng_event_rule *rule) |
df08d338 | 26 | { |
e60e9a9a | 27 | struct lttng_event_rule_kernel_uprobe *uprobe; |
df08d338 | 28 | |
e60e9a9a | 29 | uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent); |
df08d338 JR |
30 | |
31 | lttng_userspace_probe_location_destroy(uprobe->location); | |
32 | free(uprobe->name); | |
33 | free(uprobe); | |
34 | } | |
35 | ||
e60e9a9a | 36 | static bool lttng_event_rule_kernel_uprobe_validate( |
df08d338 JR |
37 | const struct lttng_event_rule *rule) |
38 | { | |
39 | bool valid = false; | |
e60e9a9a | 40 | struct lttng_event_rule_kernel_uprobe *uprobe; |
df08d338 JR |
41 | |
42 | if (!rule) { | |
43 | goto end; | |
44 | } | |
45 | ||
e60e9a9a | 46 | uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent); |
df08d338 JR |
47 | |
48 | /* Required field. */ | |
49 | if (!uprobe->name) { | |
50 | ERR("Invalid uprobe event rule: a pattern must be set."); | |
51 | goto end; | |
52 | } | |
53 | ||
54 | if (!uprobe->location) { | |
55 | ERR("Invalid uprobe event rule: a location must be set."); | |
56 | goto end; | |
57 | } | |
58 | ||
59 | valid = true; | |
60 | end: | |
61 | return valid; | |
62 | } | |
63 | ||
e60e9a9a | 64 | static int lttng_event_rule_kernel_uprobe_serialize( |
df08d338 JR |
65 | const struct lttng_event_rule *rule, |
66 | struct lttng_payload *payload) | |
67 | { | |
68 | int ret; | |
69 | size_t name_len, header_offset, size_before_probe; | |
e60e9a9a JR |
70 | struct lttng_event_rule_kernel_uprobe *uprobe; |
71 | struct lttng_event_rule_kernel_uprobe_comm uprobe_comm = {}; | |
72 | struct lttng_event_rule_kernel_uprobe_comm *header; | |
df08d338 JR |
73 | |
74 | if (!rule || !IS_UPROBE_EVENT_RULE(rule)) { | |
75 | ret = -1; | |
76 | goto end; | |
77 | } | |
78 | ||
79 | header_offset = payload->buffer.size; | |
80 | ||
81 | DBG("Serializing uprobe event rule."); | |
e60e9a9a | 82 | uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent); |
df08d338 JR |
83 | |
84 | name_len = strlen(uprobe->name) + 1; | |
85 | ||
86 | uprobe_comm.name_len = name_len; | |
87 | ||
88 | ret = lttng_dynamic_buffer_append( | |
89 | &payload->buffer, &uprobe_comm, sizeof(uprobe_comm)); | |
90 | if (ret) { | |
91 | goto end; | |
92 | } | |
93 | ret = lttng_dynamic_buffer_append( | |
94 | &payload->buffer, uprobe->name, name_len); | |
95 | if (ret) { | |
96 | goto end; | |
97 | } | |
98 | ||
99 | size_before_probe = payload->buffer.size; | |
100 | ||
101 | /* This serialize return the size taken in the buffer. */ | |
102 | ret = lttng_userspace_probe_location_serialize( | |
103 | uprobe->location, payload); | |
104 | if (ret < 0) { | |
105 | goto end; | |
106 | } | |
107 | ||
108 | /* Update the header regarding the probe size. */ | |
e60e9a9a | 109 | header = (struct lttng_event_rule_kernel_uprobe_comm |
df08d338 JR |
110 | *) ((char *) payload->buffer.data + |
111 | header_offset); | |
112 | header->location_len = payload->buffer.size - size_before_probe; | |
113 | ||
114 | ret = 0; | |
115 | ||
116 | end: | |
117 | return ret; | |
118 | } | |
119 | ||
e60e9a9a | 120 | static bool lttng_event_rule_kernel_uprobe_is_equal(const struct lttng_event_rule *_a, |
df08d338 JR |
121 | const struct lttng_event_rule *_b) |
122 | { | |
123 | bool is_equal = false; | |
e60e9a9a | 124 | struct lttng_event_rule_kernel_uprobe *a, *b; |
df08d338 | 125 | |
e60e9a9a JR |
126 | a = container_of(_a, struct lttng_event_rule_kernel_uprobe, parent); |
127 | b = container_of(_b, struct lttng_event_rule_kernel_uprobe, parent); | |
df08d338 JR |
128 | |
129 | /* uprobe is invalid if this is not true. */ | |
130 | assert(a->name); | |
131 | assert(b->name); | |
132 | if (strcmp(a->name, b->name)) { | |
133 | goto end; | |
134 | } | |
135 | ||
136 | assert(a->location); | |
137 | assert(b->location); | |
138 | is_equal = lttng_userspace_probe_location_is_equal( | |
139 | a->location, b->location); | |
140 | end: | |
141 | return is_equal; | |
142 | } | |
143 | ||
e60e9a9a | 144 | static enum lttng_error_code lttng_event_rule_kernel_uprobe_generate_filter_bytecode( |
58daac01 JR |
145 | struct lttng_event_rule *rule, |
146 | const struct lttng_credentials *creds) | |
df08d338 JR |
147 | { |
148 | /* Nothing to do. */ | |
149 | return LTTNG_OK; | |
150 | } | |
151 | ||
e60e9a9a | 152 | static const char *lttng_event_rule_kernel_uprobe_get_filter( |
df08d338 JR |
153 | const struct lttng_event_rule *rule) |
154 | { | |
155 | /* Unsupported. */ | |
156 | return NULL; | |
157 | } | |
158 | ||
2b00d462 | 159 | static const struct lttng_bytecode * |
e60e9a9a | 160 | lttng_event_rule_kernel_uprobe_get_filter_bytecode(const struct lttng_event_rule *rule) |
df08d338 JR |
161 | { |
162 | /* Unsupported. */ | |
163 | return NULL; | |
164 | } | |
165 | ||
993578ff | 166 | static enum lttng_event_rule_generate_exclusions_status |
e60e9a9a | 167 | lttng_event_rule_kernel_uprobe_generate_exclusions(const struct lttng_event_rule *rule, |
993578ff | 168 | struct lttng_event_exclusion **exclusions) |
df08d338 JR |
169 | { |
170 | /* Unsupported. */ | |
993578ff JR |
171 | *exclusions = NULL; |
172 | return LTTNG_EVENT_RULE_GENERATE_EXCLUSIONS_STATUS_NONE; | |
df08d338 JR |
173 | } |
174 | ||
959e3c66 | 175 | static unsigned long |
e60e9a9a | 176 | lttng_event_rule_kernel_uprobe_hash( |
959e3c66 JR |
177 | const struct lttng_event_rule *rule) |
178 | { | |
179 | unsigned long hash; | |
e60e9a9a | 180 | struct lttng_event_rule_kernel_uprobe *urule = |
959e3c66 JR |
181 | container_of(rule, typeof(*urule), parent); |
182 | ||
e60e9a9a | 183 | hash = hash_key_ulong((void *) LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE, |
959e3c66 JR |
184 | lttng_ht_seed); |
185 | hash ^= hash_key_str(urule->name, lttng_ht_seed); | |
186 | hash ^= lttng_userspace_probe_location_hash(urule->location); | |
187 | ||
188 | return hash; | |
189 | } | |
190 | ||
10685de6 JR |
191 | static |
192 | int userspace_probe_set_location( | |
e60e9a9a | 193 | struct lttng_event_rule_kernel_uprobe *uprobe, |
10685de6 JR |
194 | const struct lttng_userspace_probe_location *location) |
195 | { | |
196 | int ret; | |
197 | struct lttng_userspace_probe_location *location_copy = NULL; | |
198 | ||
199 | if (!uprobe || !location || uprobe->location) { | |
200 | ret = -1; | |
201 | goto end; | |
202 | } | |
203 | ||
204 | location_copy = lttng_userspace_probe_location_copy(location); | |
205 | if (!location_copy) { | |
206 | ret = -1; | |
207 | goto end; | |
208 | } | |
209 | ||
210 | uprobe->location = location_copy; | |
211 | location_copy = NULL; | |
212 | ret = 0; | |
213 | end: | |
214 | lttng_userspace_probe_location_destroy(location_copy); | |
215 | return ret; | |
216 | } | |
217 | ||
0f7c2963 JR |
218 | static enum lttng_error_code lttng_event_rule_kernel_uprobe_mi_serialize( |
219 | const struct lttng_event_rule *rule, struct mi_writer *writer) | |
220 | { | |
221 | int ret; | |
222 | enum lttng_error_code ret_code; | |
223 | enum lttng_event_rule_status status; | |
224 | const char *event_name = NULL; | |
225 | const struct lttng_userspace_probe_location *location = NULL; | |
226 | ||
227 | assert(rule); | |
228 | assert(writer); | |
229 | assert(IS_UPROBE_EVENT_RULE(rule)); | |
230 | ||
231 | status = lttng_event_rule_kernel_uprobe_get_event_name( | |
232 | rule, &event_name); | |
233 | assert(status == LTTNG_EVENT_RULE_STATUS_OK); | |
234 | assert(event_name); | |
235 | ||
236 | status = lttng_event_rule_kernel_uprobe_get_location(rule, &location); | |
237 | assert(status == LTTNG_EVENT_RULE_STATUS_OK); | |
238 | assert(location); | |
239 | ||
240 | /* Open event rule kernel uprobe element. */ | |
241 | ret = mi_lttng_writer_open_element( | |
242 | writer, mi_lttng_element_event_rule_kernel_uprobe); | |
243 | if (ret) { | |
244 | goto mi_error; | |
245 | } | |
246 | ||
247 | /* Event name. */ | |
248 | ret = mi_lttng_writer_write_element_string(writer, | |
249 | mi_lttng_element_event_rule_event_name, event_name); | |
250 | if (ret) { | |
251 | goto mi_error; | |
252 | } | |
253 | ||
254 | /* Probe location. */ | |
255 | ret_code = lttng_userspace_probe_location_mi_serialize(location, writer); | |
256 | if (ret_code != LTTNG_OK) { | |
257 | goto end; | |
258 | } | |
259 | ||
260 | /* Close event rule kernel uprobe element. */ | |
261 | ret = mi_lttng_writer_close_element(writer); | |
262 | if (ret) { | |
263 | goto mi_error; | |
264 | } | |
265 | ||
266 | ret_code = LTTNG_OK; | |
267 | goto end; | |
268 | ||
269 | mi_error: | |
270 | ret_code = LTTNG_ERR_MI_IO_FAIL; | |
271 | end: | |
272 | return ret_code; | |
273 | } | |
274 | ||
e60e9a9a | 275 | struct lttng_event_rule *lttng_event_rule_kernel_uprobe_create( |
10685de6 | 276 | const struct lttng_userspace_probe_location *location) |
df08d338 JR |
277 | { |
278 | struct lttng_event_rule *rule = NULL; | |
e60e9a9a | 279 | struct lttng_event_rule_kernel_uprobe *urule; |
df08d338 | 280 | |
e60e9a9a | 281 | urule = zmalloc(sizeof(struct lttng_event_rule_kernel_uprobe)); |
df08d338 JR |
282 | if (!urule) { |
283 | goto end; | |
284 | } | |
285 | ||
286 | rule = &urule->parent; | |
e60e9a9a JR |
287 | lttng_event_rule_init(&urule->parent, LTTNG_EVENT_RULE_TYPE_KERNEL_UPROBE); |
288 | urule->parent.validate = lttng_event_rule_kernel_uprobe_validate; | |
289 | urule->parent.serialize = lttng_event_rule_kernel_uprobe_serialize; | |
290 | urule->parent.equal = lttng_event_rule_kernel_uprobe_is_equal; | |
291 | urule->parent.destroy = lttng_event_rule_kernel_uprobe_destroy; | |
df08d338 | 292 | urule->parent.generate_filter_bytecode = |
e60e9a9a JR |
293 | lttng_event_rule_kernel_uprobe_generate_filter_bytecode; |
294 | urule->parent.get_filter = lttng_event_rule_kernel_uprobe_get_filter; | |
df08d338 | 295 | urule->parent.get_filter_bytecode = |
e60e9a9a | 296 | lttng_event_rule_kernel_uprobe_get_filter_bytecode; |
df08d338 | 297 | urule->parent.generate_exclusions = |
e60e9a9a JR |
298 | lttng_event_rule_kernel_uprobe_generate_exclusions; |
299 | urule->parent.hash = lttng_event_rule_kernel_uprobe_hash; | |
0f7c2963 | 300 | urule->parent.mi_serialize = lttng_event_rule_kernel_uprobe_mi_serialize; |
1f1567a5 | 301 | |
10685de6 JR |
302 | if (userspace_probe_set_location(urule, location)) { |
303 | lttng_event_rule_destroy(rule); | |
304 | rule = NULL; | |
305 | } | |
306 | ||
df08d338 JR |
307 | end: |
308 | return rule; | |
309 | } | |
310 | ||
311 | LTTNG_HIDDEN | |
e60e9a9a | 312 | ssize_t lttng_event_rule_kernel_uprobe_create_from_payload( |
df08d338 JR |
313 | struct lttng_payload_view *view, |
314 | struct lttng_event_rule **_event_rule) | |
315 | { | |
316 | ssize_t ret, offset = 0; | |
e60e9a9a | 317 | const struct lttng_event_rule_kernel_uprobe_comm *uprobe_comm; |
df08d338 JR |
318 | const char *name; |
319 | struct lttng_buffer_view current_buffer_view; | |
320 | struct lttng_event_rule *rule = NULL; | |
10685de6 | 321 | struct lttng_userspace_probe_location *location = NULL; |
df08d338 JR |
322 | enum lttng_event_rule_status status; |
323 | ||
324 | if (!_event_rule) { | |
325 | ret = -1; | |
326 | goto end; | |
327 | } | |
328 | ||
df08d338 JR |
329 | current_buffer_view = lttng_buffer_view_from_view( |
330 | &view->buffer, offset, sizeof(*uprobe_comm)); | |
3e6e0df2 JG |
331 | if (!lttng_buffer_view_is_valid(¤t_buffer_view)) { |
332 | ERR("Failed to initialize from malformed event rule uprobe: buffer too short to contain header"); | |
df08d338 JR |
333 | ret = -1; |
334 | goto end; | |
335 | } | |
336 | ||
3e6e0df2 | 337 | uprobe_comm = (typeof(uprobe_comm)) current_buffer_view.data; |
df08d338 JR |
338 | |
339 | /* Skip to payload. */ | |
340 | offset += current_buffer_view.size; | |
341 | ||
342 | /* Map the name. */ | |
343 | current_buffer_view = lttng_buffer_view_from_view( | |
344 | &view->buffer, offset, uprobe_comm->name_len); | |
3e6e0df2 | 345 | if (!lttng_buffer_view_is_valid(¤t_buffer_view)) { |
df08d338 JR |
346 | ret = -1; |
347 | goto end; | |
348 | } | |
349 | ||
3e6e0df2 | 350 | name = current_buffer_view.data; |
df08d338 JR |
351 | if (!lttng_buffer_view_contains_string(¤t_buffer_view, name, |
352 | uprobe_comm->name_len)) { | |
353 | ret = -1; | |
354 | goto end; | |
355 | } | |
356 | ||
357 | /* Skip after the name. */ | |
358 | offset += uprobe_comm->name_len; | |
359 | ||
360 | /* Map the location. */ | |
3e6e0df2 JG |
361 | { |
362 | struct lttng_payload_view current_payload_view = | |
363 | lttng_payload_view_from_view(view, offset, | |
364 | uprobe_comm->location_len); | |
365 | ||
366 | if (!lttng_payload_view_is_valid(¤t_payload_view)) { | |
367 | ERR("Failed to initialize from malformed event rule uprobe: buffer too short to contain location"); | |
368 | ret = -1; | |
369 | goto end; | |
370 | } | |
371 | ||
372 | ret = lttng_userspace_probe_location_create_from_payload( | |
373 | ¤t_payload_view, &location); | |
374 | if (ret < 0) { | |
375 | ret = -1; | |
376 | goto end; | |
377 | } | |
df08d338 JR |
378 | } |
379 | ||
380 | assert(ret == uprobe_comm->location_len); | |
381 | ||
382 | /* Skip after the location. */ | |
383 | offset += uprobe_comm->location_len; | |
384 | ||
e60e9a9a | 385 | rule = lttng_event_rule_kernel_uprobe_create(location); |
10685de6 JR |
386 | if (!rule) { |
387 | ERR("Failed to create event rule uprobe."); | |
388 | ret = -1; | |
389 | goto end; | |
390 | } | |
df08d338 | 391 | |
e60e9a9a | 392 | status = lttng_event_rule_kernel_uprobe_set_event_name(rule, name); |
df08d338 JR |
393 | if (status != LTTNG_EVENT_RULE_STATUS_OK) { |
394 | ret = -1; | |
395 | goto end; | |
396 | } | |
397 | ||
e60e9a9a | 398 | if (!lttng_event_rule_kernel_uprobe_validate(rule)) { |
df08d338 JR |
399 | ret = -1; |
400 | goto end; | |
401 | } | |
402 | ||
403 | *_event_rule = rule; | |
404 | rule = NULL; | |
405 | ret = offset; | |
406 | end: | |
10685de6 | 407 | lttng_userspace_probe_location_destroy(location); |
df08d338 JR |
408 | lttng_event_rule_destroy(rule); |
409 | return ret; | |
410 | } | |
411 | ||
df08d338 | 412 | |
e60e9a9a | 413 | enum lttng_event_rule_status lttng_event_rule_kernel_uprobe_get_location( |
df08d338 JR |
414 | const struct lttng_event_rule *rule, |
415 | const struct lttng_userspace_probe_location **location) | |
416 | { | |
417 | enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; | |
418 | ||
419 | if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !location) { | |
420 | status = LTTNG_EVENT_RULE_STATUS_INVALID; | |
421 | goto end; | |
422 | } | |
423 | ||
e60e9a9a | 424 | *location = lttng_event_rule_kernel_uprobe_get_location_mutable(rule); |
df08d338 JR |
425 | if (!*location) { |
426 | status = LTTNG_EVENT_RULE_STATUS_UNSET; | |
427 | goto end; | |
428 | } | |
429 | ||
430 | end: | |
431 | return status; | |
432 | } | |
433 | ||
434 | LTTNG_HIDDEN | |
435 | struct lttng_userspace_probe_location * | |
e60e9a9a | 436 | lttng_event_rule_kernel_uprobe_get_location_mutable( |
df08d338 JR |
437 | const struct lttng_event_rule *rule) |
438 | { | |
e60e9a9a | 439 | struct lttng_event_rule_kernel_uprobe *uprobe; |
df08d338 JR |
440 | |
441 | assert(rule); | |
e60e9a9a | 442 | uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent); |
df08d338 JR |
443 | |
444 | return uprobe->location; | |
445 | } | |
446 | ||
e60e9a9a | 447 | enum lttng_event_rule_status lttng_event_rule_kernel_uprobe_set_event_name( |
df08d338 JR |
448 | struct lttng_event_rule *rule, const char *name) |
449 | { | |
450 | char *name_copy = NULL; | |
e60e9a9a | 451 | struct lttng_event_rule_kernel_uprobe *uprobe; |
df08d338 JR |
452 | enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; |
453 | ||
454 | if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name || | |
455 | strlen(name) == 0) { | |
456 | status = LTTNG_EVENT_RULE_STATUS_INVALID; | |
457 | goto end; | |
458 | } | |
459 | ||
e60e9a9a | 460 | uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent); |
df08d338 JR |
461 | name_copy = strdup(name); |
462 | if (!name_copy) { | |
463 | status = LTTNG_EVENT_RULE_STATUS_ERROR; | |
464 | goto end; | |
465 | } | |
466 | ||
467 | if (uprobe->name) { | |
468 | free(uprobe->name); | |
469 | } | |
470 | ||
471 | uprobe->name = name_copy; | |
472 | name_copy = NULL; | |
473 | end: | |
474 | return status; | |
475 | } | |
476 | ||
e60e9a9a | 477 | enum lttng_event_rule_status lttng_event_rule_kernel_uprobe_get_event_name( |
df08d338 JR |
478 | const struct lttng_event_rule *rule, const char **name) |
479 | { | |
e60e9a9a | 480 | struct lttng_event_rule_kernel_uprobe *uprobe; |
df08d338 JR |
481 | enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; |
482 | ||
483 | if (!rule || !IS_UPROBE_EVENT_RULE(rule) || !name) { | |
484 | status = LTTNG_EVENT_RULE_STATUS_INVALID; | |
485 | goto end; | |
486 | } | |
487 | ||
e60e9a9a | 488 | uprobe = container_of(rule, struct lttng_event_rule_kernel_uprobe, parent); |
df08d338 JR |
489 | if (!uprobe->name) { |
490 | status = LTTNG_EVENT_RULE_STATUS_UNSET; | |
491 | goto end; | |
492 | } | |
493 | ||
494 | *name = uprobe->name; | |
495 | end: | |
496 | return status; | |
497 | } |