Commit | Line | Data |
---|---|---|
e6a39346 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> |
e6a39346 JR |
10 | #include <common/error.h> |
11 | #include <common/macros.h> | |
12 | #include <common/payload.h> | |
13 | #include <common/payload-view.h> | |
14 | #include <common/runas.h> | |
15 | #include <lttng/event-rule/event-rule-internal.h> | |
16 | #include <lttng/event-rule/syscall-internal.h> | |
17 | ||
18 | #define IS_SYSCALL_EVENT_RULE(rule) \ | |
19 | (lttng_event_rule_get_type(rule) == LTTNG_EVENT_RULE_TYPE_SYSCALL) | |
20 | ||
21 | static void lttng_event_rule_syscall_destroy(struct lttng_event_rule *rule) | |
22 | { | |
23 | struct lttng_event_rule_syscall *syscall; | |
24 | ||
25 | if (rule == NULL) { | |
26 | return; | |
27 | } | |
28 | ||
29 | syscall = container_of(rule, struct lttng_event_rule_syscall, parent); | |
30 | ||
31 | free(syscall->pattern); | |
32 | free(syscall->filter_expression); | |
33 | free(syscall->internal_filter.filter); | |
34 | free(syscall->internal_filter.bytecode); | |
35 | free(syscall); | |
36 | } | |
37 | ||
38 | static bool lttng_event_rule_syscall_validate( | |
39 | const struct lttng_event_rule *rule) | |
40 | { | |
41 | bool valid = false; | |
42 | struct lttng_event_rule_syscall *syscall; | |
43 | ||
44 | if (!rule) { | |
45 | goto end; | |
46 | } | |
47 | ||
48 | syscall = container_of(rule, struct lttng_event_rule_syscall, parent); | |
49 | ||
50 | /* Required field. */ | |
51 | if (!syscall->pattern) { | |
52 | ERR("Invalid syscall event rule: a pattern must be set."); | |
53 | goto end; | |
54 | } | |
55 | ||
56 | valid = true; | |
57 | end: | |
58 | return valid; | |
59 | } | |
60 | ||
61 | static int lttng_event_rule_syscall_serialize( | |
62 | const struct lttng_event_rule *rule, | |
63 | struct lttng_payload *payload) | |
64 | { | |
65 | int ret; | |
66 | size_t pattern_len, filter_expression_len; | |
67 | struct lttng_event_rule_syscall *syscall; | |
68 | struct lttng_event_rule_syscall_comm syscall_comm; | |
69 | ||
70 | if (!rule || !IS_SYSCALL_EVENT_RULE(rule)) { | |
71 | ret = -1; | |
72 | goto end; | |
73 | } | |
74 | ||
75 | DBG("Serializing syscall event rule"); | |
76 | syscall = container_of(rule, struct lttng_event_rule_syscall, parent); | |
77 | ||
78 | pattern_len = strlen(syscall->pattern) + 1; | |
79 | ||
80 | if (syscall->filter_expression != NULL) { | |
81 | filter_expression_len = strlen(syscall->filter_expression) + 1; | |
82 | } else { | |
83 | filter_expression_len = 0; | |
84 | } | |
85 | ||
86 | syscall_comm.pattern_len = pattern_len; | |
87 | syscall_comm.filter_expression_len = filter_expression_len; | |
88 | ||
89 | ret = lttng_dynamic_buffer_append( | |
90 | &payload->buffer, &syscall_comm, sizeof(syscall_comm)); | |
91 | if (ret) { | |
92 | goto end; | |
93 | } | |
94 | ||
95 | ret = lttng_dynamic_buffer_append( | |
96 | &payload->buffer, syscall->pattern, pattern_len); | |
97 | if (ret) { | |
98 | goto end; | |
99 | } | |
100 | ||
101 | ret = lttng_dynamic_buffer_append(&payload->buffer, | |
102 | syscall->filter_expression, filter_expression_len); | |
103 | end: | |
104 | return ret; | |
105 | } | |
106 | ||
107 | static bool lttng_event_rule_syscall_is_equal(const struct lttng_event_rule *_a, | |
108 | const struct lttng_event_rule *_b) | |
109 | { | |
110 | bool is_equal = false; | |
111 | struct lttng_event_rule_syscall *a, *b; | |
112 | ||
113 | a = container_of(_a, struct lttng_event_rule_syscall, parent); | |
114 | b = container_of(_b, struct lttng_event_rule_syscall, parent); | |
115 | ||
116 | if (!!a->filter_expression != !!b->filter_expression) { | |
117 | goto end; | |
118 | } | |
119 | ||
120 | assert(a->pattern); | |
121 | assert(b->pattern); | |
122 | if (strcmp(a->pattern, b->pattern)) { | |
123 | goto end; | |
124 | } | |
125 | ||
126 | if (a->filter_expression && b->filter_expression) { | |
127 | if (strcmp(a->filter_expression, b->filter_expression)) { | |
128 | goto end; | |
129 | } | |
130 | } else if (!!a->filter_expression != !!b->filter_expression) { | |
131 | /* One is set and not the other. */ | |
132 | goto end; | |
133 | } | |
134 | ||
135 | is_equal = true; | |
136 | end: | |
137 | return is_equal; | |
138 | } | |
139 | ||
140 | static enum lttng_error_code lttng_event_rule_syscall_generate_filter_bytecode( | |
58daac01 JR |
141 | struct lttng_event_rule *rule, |
142 | const struct lttng_credentials *creds) | |
e6a39346 JR |
143 | { |
144 | int ret; | |
145 | enum lttng_error_code ret_code = LTTNG_OK; | |
146 | struct lttng_event_rule_syscall *syscall; | |
147 | enum lttng_event_rule_status status; | |
148 | const char *filter; | |
149 | struct lttng_filter_bytecode *bytecode = NULL; | |
150 | ||
151 | assert(rule); | |
152 | ||
153 | syscall = container_of(rule, struct lttng_event_rule_syscall, parent); | |
154 | ||
155 | /* Generate the filter bytecode. */ | |
156 | status = lttng_event_rule_syscall_get_filter(rule, &filter); | |
157 | if (status == LTTNG_EVENT_RULE_STATUS_UNSET) { | |
158 | filter = NULL; | |
159 | } else if (status != LTTNG_EVENT_RULE_STATUS_OK) { | |
160 | ret_code = LTTNG_ERR_FILTER_INVAL; | |
161 | goto end; | |
162 | } | |
163 | ||
164 | if (filter && filter[0] == '\0') { | |
165 | ret_code = LTTNG_ERR_FILTER_INVAL; | |
166 | goto end; | |
167 | } | |
168 | ||
169 | if (filter == NULL) { | |
170 | /* Nothing to do. */ | |
171 | ret = LTTNG_OK; | |
172 | goto end; | |
173 | } | |
174 | ||
175 | syscall->internal_filter.filter = strdup(filter); | |
176 | if (syscall->internal_filter.filter == NULL) { | |
177 | ret_code = LTTNG_ERR_NOMEM; | |
178 | goto end; | |
179 | } | |
180 | ||
181 | ret = run_as_generate_filter_bytecode( | |
58daac01 | 182 | syscall->internal_filter.filter, creds, &bytecode); |
e6a39346 JR |
183 | if (ret) { |
184 | ret_code = LTTNG_ERR_FILTER_INVAL; | |
185 | } | |
186 | ||
187 | syscall->internal_filter.bytecode = bytecode; | |
188 | bytecode = NULL; | |
189 | ||
190 | end: | |
191 | free(bytecode); | |
192 | return ret_code; | |
193 | } | |
194 | ||
195 | static const char *lttng_event_rule_syscall_get_internal_filter( | |
196 | const struct lttng_event_rule *rule) | |
197 | { | |
198 | struct lttng_event_rule_syscall *syscall; | |
199 | ||
200 | assert(rule); | |
201 | syscall = container_of(rule, struct lttng_event_rule_syscall, parent); | |
202 | ||
203 | return syscall->internal_filter.filter; | |
204 | } | |
205 | ||
206 | static const struct lttng_filter_bytecode * | |
207 | lttng_event_rule_syscall_get_internal_filter_bytecode( | |
208 | const struct lttng_event_rule *rule) | |
209 | { | |
210 | struct lttng_event_rule_syscall *syscall; | |
211 | ||
212 | assert(rule); | |
213 | syscall = container_of(rule, struct lttng_event_rule_syscall, parent); | |
214 | ||
215 | return syscall->internal_filter.bytecode; | |
216 | } | |
217 | ||
218 | static struct lttng_event_exclusion * | |
219 | lttng_event_rule_syscall_generate_exclusions( | |
220 | const struct lttng_event_rule *rule) | |
221 | { | |
222 | /* Not supported. */ | |
223 | return NULL; | |
224 | } | |
225 | ||
226 | struct lttng_event_rule *lttng_event_rule_syscall_create() | |
227 | { | |
228 | struct lttng_event_rule *rule = NULL; | |
229 | struct lttng_event_rule_syscall *syscall_rule; | |
230 | ||
231 | syscall_rule = zmalloc(sizeof(struct lttng_event_rule_syscall)); | |
232 | if (!syscall_rule) { | |
233 | goto end; | |
234 | } | |
235 | ||
236 | rule = &syscall_rule->parent; | |
237 | lttng_event_rule_init( | |
238 | &syscall_rule->parent, LTTNG_EVENT_RULE_TYPE_SYSCALL); | |
239 | syscall_rule->parent.validate = lttng_event_rule_syscall_validate; | |
240 | syscall_rule->parent.serialize = lttng_event_rule_syscall_serialize; | |
241 | syscall_rule->parent.equal = lttng_event_rule_syscall_is_equal; | |
242 | syscall_rule->parent.destroy = lttng_event_rule_syscall_destroy; | |
243 | syscall_rule->parent.generate_filter_bytecode = | |
244 | lttng_event_rule_syscall_generate_filter_bytecode; | |
245 | syscall_rule->parent.get_filter = | |
246 | lttng_event_rule_syscall_get_internal_filter; | |
247 | syscall_rule->parent.get_filter_bytecode = | |
248 | lttng_event_rule_syscall_get_internal_filter_bytecode; | |
249 | syscall_rule->parent.generate_exclusions = | |
250 | lttng_event_rule_syscall_generate_exclusions; | |
251 | end: | |
252 | return rule; | |
253 | } | |
254 | ||
255 | LTTNG_HIDDEN | |
256 | ssize_t lttng_event_rule_syscall_create_from_payload( | |
257 | struct lttng_payload_view *view, | |
258 | struct lttng_event_rule **_event_rule) | |
259 | { | |
260 | ssize_t ret, offset = 0; | |
261 | enum lttng_event_rule_status status; | |
262 | const struct lttng_event_rule_syscall_comm *syscall_comm; | |
263 | const char *pattern; | |
264 | const char *filter_expression = NULL; | |
265 | struct lttng_buffer_view current_buffer_view; | |
266 | struct lttng_event_rule *rule = NULL; | |
267 | ||
268 | if (!_event_rule) { | |
269 | ret = -1; | |
270 | goto end; | |
271 | } | |
272 | ||
273 | if (view->buffer.size < sizeof(*syscall_comm)) { | |
274 | ERR("Failed to initialize from malformed event rule syscall: buffer too short to contain header"); | |
275 | ret = -1; | |
276 | goto end; | |
277 | } | |
278 | ||
279 | current_buffer_view = lttng_buffer_view_from_view( | |
280 | &view->buffer, offset, sizeof(*syscall_comm)); | |
3e6e0df2 | 281 | if (!lttng_buffer_view_is_valid(¤t_buffer_view)) { |
e6a39346 JR |
282 | ret = -1; |
283 | goto end; | |
284 | } | |
285 | ||
3e6e0df2 | 286 | syscall_comm = (typeof(syscall_comm)) current_buffer_view.data; |
e6a39346 JR |
287 | rule = lttng_event_rule_syscall_create(); |
288 | if (!rule) { | |
289 | ERR("Failed to create event rule syscall"); | |
290 | ret = -1; | |
291 | goto end; | |
292 | } | |
293 | ||
294 | /* Skip to payload. */ | |
295 | offset += current_buffer_view.size; | |
296 | ||
297 | /* Map the pattern. */ | |
298 | current_buffer_view = lttng_buffer_view_from_view( | |
299 | &view->buffer, offset, syscall_comm->pattern_len); | |
3e6e0df2 | 300 | if (!lttng_buffer_view_is_valid(¤t_buffer_view)) { |
e6a39346 JR |
301 | ret = -1; |
302 | goto end; | |
303 | } | |
304 | ||
3e6e0df2 | 305 | pattern = current_buffer_view.data; |
e6a39346 JR |
306 | if (!lttng_buffer_view_contains_string(¤t_buffer_view, pattern, |
307 | syscall_comm->pattern_len)) { | |
308 | ret = -1; | |
309 | goto end; | |
310 | } | |
311 | ||
312 | /* Skip after the pattern. */ | |
313 | offset += syscall_comm->pattern_len; | |
314 | ||
315 | if (!syscall_comm->filter_expression_len) { | |
316 | goto skip_filter_expression; | |
317 | } | |
318 | ||
319 | /* Map the filter_expression. */ | |
320 | current_buffer_view = lttng_buffer_view_from_view(&view->buffer, offset, | |
321 | syscall_comm->filter_expression_len); | |
3e6e0df2 | 322 | if (!lttng_buffer_view_is_valid(¤t_buffer_view)) { |
e6a39346 JR |
323 | ret = -1; |
324 | goto end; | |
325 | } | |
326 | ||
3e6e0df2 | 327 | filter_expression = current_buffer_view.data; |
e6a39346 JR |
328 | if (!lttng_buffer_view_contains_string(¤t_buffer_view, |
329 | filter_expression, | |
330 | syscall_comm->filter_expression_len)) { | |
331 | ret = -1; | |
332 | goto end; | |
333 | } | |
334 | ||
335 | /* Skip after the pattern. */ | |
336 | offset += syscall_comm->filter_expression_len; | |
337 | ||
338 | skip_filter_expression: | |
339 | ||
340 | status = lttng_event_rule_syscall_set_pattern(rule, pattern); | |
341 | if (status != LTTNG_EVENT_RULE_STATUS_OK) { | |
342 | ERR("Failed to set event rule syscall pattern"); | |
343 | ret = -1; | |
344 | goto end; | |
345 | } | |
346 | ||
347 | if (filter_expression) { | |
348 | status = lttng_event_rule_syscall_set_filter( | |
349 | rule, filter_expression); | |
350 | if (status != LTTNG_EVENT_RULE_STATUS_OK) { | |
351 | ERR("Failed to set event rule syscall pattern"); | |
352 | ret = -1; | |
353 | goto end; | |
354 | } | |
355 | } | |
356 | ||
357 | *_event_rule = rule; | |
358 | rule = NULL; | |
359 | ret = offset; | |
360 | end: | |
361 | lttng_event_rule_destroy(rule); | |
362 | return ret; | |
363 | } | |
364 | ||
365 | enum lttng_event_rule_status lttng_event_rule_syscall_set_pattern( | |
366 | struct lttng_event_rule *rule, const char *pattern) | |
367 | { | |
368 | char *pattern_copy = NULL; | |
369 | struct lttng_event_rule_syscall *syscall; | |
370 | enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; | |
371 | ||
372 | if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern || | |
373 | strlen(pattern) == 0) { | |
374 | status = LTTNG_EVENT_RULE_STATUS_INVALID; | |
375 | goto end; | |
376 | } | |
377 | ||
378 | syscall = container_of(rule, struct lttng_event_rule_syscall, parent); | |
379 | pattern_copy = strdup(pattern); | |
380 | if (!pattern_copy) { | |
381 | status = LTTNG_EVENT_RULE_STATUS_ERROR; | |
382 | goto end; | |
383 | } | |
384 | ||
385 | if (syscall->pattern) { | |
386 | free(syscall->pattern); | |
387 | } | |
388 | ||
389 | syscall->pattern = pattern_copy; | |
390 | pattern_copy = NULL; | |
391 | end: | |
392 | return status; | |
393 | } | |
394 | ||
395 | enum lttng_event_rule_status lttng_event_rule_syscall_get_pattern( | |
396 | const struct lttng_event_rule *rule, const char **pattern) | |
397 | { | |
398 | struct lttng_event_rule_syscall *syscall; | |
399 | enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; | |
400 | ||
401 | if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !pattern) { | |
402 | status = LTTNG_EVENT_RULE_STATUS_INVALID; | |
403 | goto end; | |
404 | } | |
405 | ||
406 | syscall = container_of(rule, struct lttng_event_rule_syscall, parent); | |
407 | if (!syscall->pattern) { | |
408 | status = LTTNG_EVENT_RULE_STATUS_UNSET; | |
409 | goto end; | |
410 | } | |
411 | ||
412 | *pattern = syscall->pattern; | |
413 | end: | |
414 | return status; | |
415 | } | |
416 | ||
417 | enum lttng_event_rule_status lttng_event_rule_syscall_set_filter( | |
418 | struct lttng_event_rule *rule, const char *expression) | |
419 | { | |
420 | char *expression_copy = NULL; | |
421 | struct lttng_event_rule_syscall *syscall; | |
422 | enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; | |
423 | ||
424 | /* TODO: validate that the passed expression is valid. */ | |
425 | ||
426 | if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression || | |
427 | strlen(expression) == 0) { | |
428 | status = LTTNG_EVENT_RULE_STATUS_INVALID; | |
429 | goto end; | |
430 | } | |
431 | ||
432 | syscall = container_of(rule, struct lttng_event_rule_syscall, parent); | |
433 | expression_copy = strdup(expression); | |
434 | if (!expression_copy) { | |
435 | status = LTTNG_EVENT_RULE_STATUS_ERROR; | |
436 | goto end; | |
437 | } | |
438 | ||
439 | if (syscall->filter_expression) { | |
440 | free(syscall->filter_expression); | |
441 | } | |
442 | ||
443 | syscall->filter_expression = expression_copy; | |
444 | expression_copy = NULL; | |
445 | end: | |
446 | return status; | |
447 | } | |
448 | ||
449 | enum lttng_event_rule_status lttng_event_rule_syscall_get_filter( | |
450 | const struct lttng_event_rule *rule, const char **expression) | |
451 | { | |
452 | struct lttng_event_rule_syscall *syscall; | |
453 | enum lttng_event_rule_status status = LTTNG_EVENT_RULE_STATUS_OK; | |
454 | ||
455 | if (!rule || !IS_SYSCALL_EVENT_RULE(rule) || !expression) { | |
456 | status = LTTNG_EVENT_RULE_STATUS_INVALID; | |
457 | goto end; | |
458 | } | |
459 | ||
460 | syscall = container_of(rule, struct lttng_event_rule_syscall, parent); | |
461 | if (!syscall->filter_expression) { | |
462 | status = LTTNG_EVENT_RULE_STATUS_UNSET; | |
463 | goto end; | |
464 | } | |
465 | ||
466 | *expression = syscall->filter_expression; | |
467 | end: | |
468 | return status; | |
469 | } |