Fix: unchecked buffer size for communication header
[lttng-tools.git] / src / common / trigger.c
1 /*
2 * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 *
6 */
7
8 #include <lttng/trigger/trigger-internal.h>
9 #include <lttng/condition/condition-internal.h>
10 #include <lttng/action/action-internal.h>
11 #include <common/credentials.h>
12 #include <common/payload.h>
13 #include <common/payload-view.h>
14 #include <common/error.h>
15 #include <common/dynamic-array.h>
16 #include <common/optional.h>
17 #include <assert.h>
18 #include <inttypes.h>
19
20 LTTNG_HIDDEN
21 bool lttng_trigger_validate(struct lttng_trigger *trigger)
22 {
23 bool valid;
24
25 if (!trigger) {
26 valid = false;
27 goto end;
28 }
29
30 if (!trigger->creds.uid.is_set) {
31 valid = false;
32 goto end;
33 }
34
35 valid = lttng_condition_validate(trigger->condition) &&
36 lttng_action_validate(trigger->action);
37 end:
38 return valid;
39 }
40
41 struct lttng_trigger *lttng_trigger_create(
42 struct lttng_condition *condition,
43 struct lttng_action *action)
44 {
45 struct lttng_trigger *trigger = NULL;
46
47 if (!condition || !action) {
48 goto end;
49 }
50
51 trigger = zmalloc(sizeof(struct lttng_trigger));
52 if (!trigger) {
53 goto end;
54 }
55
56 urcu_ref_init(&trigger->ref);
57
58 lttng_condition_get(condition);
59 trigger->condition = condition;
60
61 lttng_action_get(action);
62 trigger->action = action;
63
64 end:
65 return trigger;
66 }
67
68 /*
69 * Note: the lack of reference counting 'get' on the condition object is normal.
70 * This API was exposed as such in 2.11. The client is not expected to call
71 * lttng_condition_destroy on the returned object.
72 */
73 struct lttng_condition *lttng_trigger_get_condition(
74 struct lttng_trigger *trigger)
75 {
76 return trigger ? trigger->condition : NULL;
77 }
78
79 LTTNG_HIDDEN
80 const struct lttng_condition *lttng_trigger_get_const_condition(
81 const struct lttng_trigger *trigger)
82 {
83 return trigger->condition;
84 }
85
86
87 /*
88 * Note: the lack of reference counting 'get' on the action object is normal.
89 * This API was exposed as such in 2.11. The client is not expected to call
90 * lttng_action_destroy on the returned object.
91 */
92 struct lttng_action *lttng_trigger_get_action(
93 struct lttng_trigger *trigger)
94 {
95 return trigger ? trigger->action : NULL;
96 }
97
98 LTTNG_HIDDEN
99 const struct lttng_action *lttng_trigger_get_const_action(
100 const struct lttng_trigger *trigger)
101 {
102 return trigger->action;
103 }
104
105 static void trigger_destroy_ref(struct urcu_ref *ref)
106 {
107 struct lttng_trigger *trigger =
108 container_of(ref, struct lttng_trigger, ref);
109 struct lttng_action *action = lttng_trigger_get_action(trigger);
110 struct lttng_condition *condition =
111 lttng_trigger_get_condition(trigger);
112
113 assert(action);
114 assert(condition);
115
116 /* Release ownership. */
117 lttng_action_put(action);
118 lttng_condition_put(condition);
119
120 free(trigger->name);
121 free(trigger);
122 }
123
124 void lttng_trigger_destroy(struct lttng_trigger *trigger)
125 {
126 lttng_trigger_put(trigger);
127 }
128
129 LTTNG_HIDDEN
130 ssize_t lttng_trigger_create_from_payload(
131 struct lttng_payload_view *src_view,
132 struct lttng_trigger **trigger)
133 {
134 ssize_t ret, offset = 0, condition_size, action_size, name_size = 0;
135 struct lttng_condition *condition = NULL;
136 struct lttng_action *action = NULL;
137 const struct lttng_trigger_comm *trigger_comm;
138 const char *name = NULL;
139 struct lttng_credentials creds = {
140 .uid = LTTNG_OPTIONAL_INIT_UNSET,
141 .gid = LTTNG_OPTIONAL_INIT_UNSET,
142 };
143 const struct lttng_payload_view trigger_comm_view =
144 lttng_payload_view_from_view(
145 src_view, 0, sizeof(*trigger_comm));
146
147 if (!src_view || !trigger) {
148 ret = -1;
149 goto end;
150 }
151
152 if (!lttng_payload_view_is_valid(&trigger_comm_view)) {
153 /* Payload not large enough to contain the header. */
154 ret = -1;
155 goto end;
156 }
157
158 /* lttng_trigger_comm header */
159 trigger_comm = (typeof(trigger_comm)) trigger_comm_view.buffer.data;
160
161 /* Set the trigger's creds. */
162 if (trigger_comm->uid > (uint64_t) ((uid_t) -1)) {
163 /* UID out of range for this platform. */
164 ret = -1;
165 goto end;
166 }
167
168 LTTNG_OPTIONAL_SET(&creds.uid, trigger_comm->uid);
169
170 offset += sizeof(*trigger_comm);
171
172 if (trigger_comm->name_length != 0) {
173 /* Name. */
174 const struct lttng_payload_view name_view =
175 lttng_payload_view_from_view(
176 src_view, offset,
177 trigger_comm->name_length);
178
179 if (!lttng_payload_view_is_valid(&name_view)) {
180 ret = -1;
181 goto end;
182 }
183
184 name = name_view.buffer.data;
185 if (!lttng_buffer_view_contains_string(&name_view.buffer, name,
186 trigger_comm->name_length)) {
187 ret = -1;
188 goto end;
189 }
190
191 offset += trigger_comm->name_length;
192 name_size = trigger_comm->name_length;
193 }
194
195 {
196 /* struct lttng_condition */
197 struct lttng_payload_view condition_view =
198 lttng_payload_view_from_view(
199 src_view, offset, -1);
200
201 condition_size = lttng_condition_create_from_payload(&condition_view,
202 &condition);
203 }
204
205 if (condition_size < 0) {
206 ret = condition_size;
207 goto end;
208 }
209
210 offset += condition_size;
211 {
212 /* struct lttng_action */
213 struct lttng_payload_view action_view =
214 lttng_payload_view_from_view(
215 src_view, offset, -1);
216
217 action_size = lttng_action_create_from_payload(&action_view, &action);
218 }
219
220 if (action_size < 0) {
221 ret = action_size;
222 goto end;
223 }
224 offset += action_size;
225
226 /* Unexpected size of inner-elements; the buffer is corrupted. */
227 if ((ssize_t) trigger_comm->length != condition_size + action_size + name_size) {
228 ret = -1;
229 goto error;
230 }
231
232 *trigger = lttng_trigger_create(condition, action);
233 if (!*trigger) {
234 ret = -1;
235 goto error;
236 }
237
238 lttng_trigger_set_credentials(*trigger, &creds);
239
240 /*
241 * The trigger object owns references to the action and condition
242 * objects.
243 */
244 lttng_condition_put(condition);
245 condition = NULL;
246
247 lttng_action_put(action);
248 action = NULL;
249
250 if (name) {
251 const enum lttng_trigger_status status =
252 lttng_trigger_set_name(*trigger, name);
253
254 if (status != LTTNG_TRIGGER_STATUS_OK) {
255 ret = -1;
256 goto end;
257 }
258 }
259
260 ret = offset;
261
262 error:
263 lttng_condition_destroy(condition);
264 lttng_action_destroy(action);
265 end:
266 return ret;
267 }
268
269 /*
270 * Both elements are stored contiguously, see their "*_comm" structure
271 * for the detailed format.
272 */
273 LTTNG_HIDDEN
274 int lttng_trigger_serialize(const struct lttng_trigger *trigger,
275 struct lttng_payload *payload)
276 {
277 int ret;
278 size_t header_offset, size_before_payload, size_name;
279 struct lttng_trigger_comm trigger_comm = {};
280 struct lttng_trigger_comm *header;
281 const struct lttng_credentials *creds = NULL;
282
283 creds = lttng_trigger_get_credentials(trigger);
284 assert(creds);
285
286 trigger_comm.uid = LTTNG_OPTIONAL_GET(creds->uid);
287
288 if (trigger->name != NULL) {
289 size_name = strlen(trigger->name) + 1;
290 } else {
291 size_name = 0;
292 }
293
294 trigger_comm.name_length = size_name;
295
296 header_offset = payload->buffer.size;
297 ret = lttng_dynamic_buffer_append(&payload->buffer, &trigger_comm,
298 sizeof(trigger_comm));
299 if (ret) {
300 goto end;
301 }
302
303 size_before_payload = payload->buffer.size;
304
305 /* Trigger name. */
306 ret = lttng_dynamic_buffer_append(
307 &payload->buffer, trigger->name, size_name);
308 if (ret) {
309 goto end;
310 }
311
312 ret = lttng_condition_serialize(trigger->condition, payload);
313 if (ret) {
314 goto end;
315 }
316
317 ret = lttng_action_serialize(trigger->action, payload);
318 if (ret) {
319 goto end;
320 }
321
322 /* Update payload size. */
323 header = (typeof(header)) (payload->buffer.data + header_offset);
324 header->length = payload->buffer.size - size_before_payload;
325 end:
326 return ret;
327 }
328
329 LTTNG_HIDDEN
330 bool lttng_trigger_is_equal(
331 const struct lttng_trigger *a, const struct lttng_trigger *b)
332 {
333 /*
334 * Name is not taken into account since it is cosmetic only.
335 */
336 if (!lttng_condition_is_equal(a->condition, b->condition)) {
337 return false;
338 }
339
340 if (!lttng_action_is_equal(a->action, b->action)) {
341 return false;
342 }
343
344 if (!lttng_credentials_is_equal(lttng_trigger_get_credentials(a),
345 lttng_trigger_get_credentials(b))) {
346 return false;
347 }
348
349 return true;
350 }
351
352 enum lttng_trigger_status lttng_trigger_set_name(struct lttng_trigger *trigger,
353 const char* name)
354 {
355 char *name_copy = NULL;
356 enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
357
358 if (!trigger || !name ||
359 strlen(name) == 0) {
360 status = LTTNG_TRIGGER_STATUS_INVALID;
361 goto end;
362 }
363
364 name_copy = strdup(name);
365 if (!name_copy) {
366 status = LTTNG_TRIGGER_STATUS_ERROR;
367 goto end;
368 }
369
370 free(trigger->name);
371
372 trigger->name = name_copy;
373 name_copy = NULL;
374 end:
375 return status;
376 }
377
378 enum lttng_trigger_status lttng_trigger_get_name(
379 const struct lttng_trigger *trigger, const char **name)
380 {
381 enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
382
383 if (!trigger || !name) {
384 status = LTTNG_TRIGGER_STATUS_INVALID;
385 goto end;
386 }
387
388 if (!trigger->name) {
389 status = LTTNG_TRIGGER_STATUS_UNSET;
390 }
391
392 *name = trigger->name;
393 end:
394 return status;
395 }
396
397 LTTNG_HIDDEN
398 int lttng_trigger_assign_name(struct lttng_trigger *dst,
399 const struct lttng_trigger *src)
400 {
401 int ret = 0;
402 enum lttng_trigger_status status;
403
404 status = lttng_trigger_set_name(dst, src->name);
405 if (status != LTTNG_TRIGGER_STATUS_OK) {
406 ret = -1;
407 ERR("Failed to set name for trigger");
408 goto end;
409 }
410 end:
411 return ret;
412 }
413
414 LTTNG_HIDDEN
415 void lttng_trigger_set_tracer_token(struct lttng_trigger *trigger,
416 uint64_t token)
417 {
418 assert(trigger);
419 LTTNG_OPTIONAL_SET(&trigger->tracer_token, token);
420 }
421
422 LTTNG_HIDDEN
423 uint64_t lttng_trigger_get_tracer_token(const struct lttng_trigger *trigger)
424 {
425 assert(trigger);
426
427 return LTTNG_OPTIONAL_GET(trigger->tracer_token);
428 }
429
430 LTTNG_HIDDEN
431 int lttng_trigger_generate_name(struct lttng_trigger *trigger,
432 uint64_t unique_id)
433 {
434 int ret = 0;
435 char *generated_name = NULL;
436
437 ret = asprintf(&generated_name, "T%" PRIu64 "", unique_id);
438 if (ret < 0) {
439 ERR("Failed to generate trigger name");
440 ret = -1;
441 goto end;
442 }
443
444 ret = 0;
445 free(trigger->name);
446 trigger->name = generated_name;
447 end:
448 return ret;
449 }
450
451 LTTNG_HIDDEN
452 void lttng_trigger_get(struct lttng_trigger *trigger)
453 {
454 urcu_ref_get(&trigger->ref);
455 }
456
457 LTTNG_HIDDEN
458 void lttng_trigger_put(struct lttng_trigger *trigger)
459 {
460 if (!trigger) {
461 return;
462 }
463
464 urcu_ref_put(&trigger->ref , trigger_destroy_ref);
465 }
466
467 static void delete_trigger_array_element(void *ptr)
468 {
469 struct lttng_trigger *trigger = ptr;
470
471 lttng_trigger_put(trigger);
472 }
473
474 LTTNG_HIDDEN
475 struct lttng_triggers *lttng_triggers_create(void)
476 {
477 struct lttng_triggers *triggers = NULL;
478
479 triggers = zmalloc(sizeof(*triggers));
480 if (!triggers) {
481 goto end;
482 }
483
484 lttng_dynamic_pointer_array_init(&triggers->array, delete_trigger_array_element);
485
486 end:
487 return triggers;
488 }
489
490 LTTNG_HIDDEN
491 struct lttng_trigger *lttng_triggers_borrow_mutable_at_index(
492 const struct lttng_triggers *triggers, unsigned int index)
493 {
494 struct lttng_trigger *trigger = NULL;
495
496 assert(triggers);
497 if (index >= lttng_dynamic_pointer_array_get_count(&triggers->array)) {
498 goto end;
499 }
500
501 trigger = (struct lttng_trigger *)
502 lttng_dynamic_pointer_array_get_pointer(
503 &triggers->array, index);
504 end:
505 return trigger;
506 }
507
508 LTTNG_HIDDEN
509 int lttng_triggers_add(
510 struct lttng_triggers *triggers, struct lttng_trigger *trigger)
511 {
512 int ret;
513
514 assert(triggers);
515 assert(trigger);
516
517 lttng_trigger_get(trigger);
518
519 ret = lttng_dynamic_pointer_array_add_pointer(&triggers->array, trigger);
520 if (ret) {
521 lttng_trigger_put(trigger);
522 }
523
524 return ret;
525 }
526
527 const struct lttng_trigger *lttng_triggers_get_at_index(
528 const struct lttng_triggers *triggers, unsigned int index)
529 {
530 return lttng_triggers_borrow_mutable_at_index(triggers, index);
531 }
532
533 enum lttng_trigger_status lttng_triggers_get_count(const struct lttng_triggers *triggers, unsigned int *count)
534 {
535 enum lttng_trigger_status status = LTTNG_TRIGGER_STATUS_OK;
536
537 if (!triggers || !count) {
538 status = LTTNG_TRIGGER_STATUS_INVALID;
539 goto end;
540 }
541
542 *count = lttng_dynamic_pointer_array_get_count(&triggers->array);
543 end:
544 return status;
545 }
546
547 void lttng_triggers_destroy(struct lttng_triggers *triggers)
548 {
549 if (!triggers) {
550 return;
551 }
552
553 lttng_dynamic_pointer_array_reset(&triggers->array);
554 free(triggers);
555 }
556
557 int lttng_triggers_serialize(const struct lttng_triggers *triggers,
558 struct lttng_payload *payload)
559 {
560 int ret;
561 unsigned int i, count;
562 size_t size_before_payload;
563 struct lttng_triggers_comm triggers_comm = {};
564 struct lttng_triggers_comm *header;
565 enum lttng_trigger_status status;
566 const size_t header_offset = payload->buffer.size;
567
568 status = lttng_triggers_get_count(triggers, &count);
569 if (status != LTTNG_TRIGGER_STATUS_OK) {
570 ret = LTTNG_ERR_INVALID;
571 goto end;
572 }
573
574 triggers_comm.count = count;
575
576 /* Placeholder header; updated at the end. */
577 ret = lttng_dynamic_buffer_append(&payload->buffer, &triggers_comm,
578 sizeof(triggers_comm));
579 if (ret) {
580 goto end;
581 }
582
583 size_before_payload = payload->buffer.size;
584
585 for (i = 0; i < count; i++) {
586 const struct lttng_trigger *trigger =
587 lttng_triggers_get_at_index(triggers, i);
588
589 assert(trigger);
590
591 ret = lttng_trigger_serialize(trigger, payload);
592 if (ret) {
593 goto end;
594 }
595 }
596
597 /* Update payload size. */
598 header = (struct lttng_triggers_comm *) ((char *) payload->buffer.data + header_offset);
599 header->length = payload->buffer.size - size_before_payload;
600 end:
601 return ret;
602 }
603
604 LTTNG_HIDDEN
605 ssize_t lttng_triggers_create_from_payload(
606 struct lttng_payload_view *src_view,
607 struct lttng_triggers **triggers)
608 {
609 ssize_t ret, offset = 0, triggers_size = 0;
610 unsigned int i;
611 const struct lttng_triggers_comm *triggers_comm;
612 struct lttng_triggers *local_triggers = NULL;
613
614 if (!src_view || !triggers) {
615 ret = -1;
616 goto error;
617 }
618
619 /* lttng_trigger_comms header */
620 triggers_comm = (const struct lttng_triggers_comm *) src_view->buffer.data;
621 offset += sizeof(*triggers_comm);
622
623 local_triggers = lttng_triggers_create();
624 if (!local_triggers) {
625 ret = -1;
626 goto error;
627 }
628
629 for (i = 0; i < triggers_comm->count; i++) {
630 struct lttng_trigger *trigger = NULL;
631 struct lttng_payload_view trigger_view =
632 lttng_payload_view_from_view(src_view, offset, -1);
633 ssize_t trigger_size;
634
635 trigger_size = lttng_trigger_create_from_payload(
636 &trigger_view, &trigger);
637 if (trigger_size < 0) {
638 ret = trigger_size;
639 goto error;
640 }
641
642 /* Transfer ownership of the trigger to the collection. */
643 ret = lttng_triggers_add(local_triggers, trigger);
644 lttng_trigger_put(trigger);
645 if (ret < 0) {
646 ret = -1;
647 goto error;
648 }
649
650 offset += trigger_size;
651 triggers_size += trigger_size;
652 }
653
654 /* Unexpected size of inner-elements; the buffer is corrupted. */
655 if ((ssize_t) triggers_comm->length != triggers_size) {
656 ret = -1;
657 goto error;
658 }
659
660 /* Pass ownership to caller. */
661 *triggers = local_triggers;
662 local_triggers = NULL;
663
664 ret = offset;
665 error:
666
667 lttng_triggers_destroy(local_triggers);
668 return ret;
669 }
670
671 LTTNG_HIDDEN
672 const struct lttng_credentials *lttng_trigger_get_credentials(
673 const struct lttng_trigger *trigger)
674 {
675 return &trigger->creds;
676 }
677
678 LTTNG_HIDDEN
679 void lttng_trigger_set_credentials(struct lttng_trigger *trigger,
680 const struct lttng_credentials *creds)
681 {
682 assert(creds);
683 trigger->creds = *creds;
684 }
685
686 enum lttng_trigger_status lttng_trigger_set_owner_uid(
687 struct lttng_trigger *trigger, uid_t uid)
688 {
689 enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK;
690 const struct lttng_credentials creds = {
691 .uid = LTTNG_OPTIONAL_INIT_VALUE(uid),
692 .gid = LTTNG_OPTIONAL_INIT_UNSET,
693 };
694
695 if (!trigger) {
696 ret = LTTNG_TRIGGER_STATUS_INVALID;
697 goto end;
698 }
699
700 /* Client-side validation only to report a clearer error. */
701 if (geteuid() != 0) {
702 ret = LTTNG_TRIGGER_STATUS_PERMISSION_DENIED;
703 goto end;
704 }
705
706 lttng_trigger_set_credentials(trigger, &creds);
707
708 end:
709 return ret;
710 }
711
712 enum lttng_trigger_status lttng_trigger_get_owner_uid(
713 const struct lttng_trigger *trigger, uid_t *uid)
714 {
715 enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK;
716 const struct lttng_credentials *creds = NULL;
717
718 if (!trigger || !uid ) {
719 ret = LTTNG_TRIGGER_STATUS_INVALID;
720 goto end;
721 }
722
723 if (!trigger->creds.uid.is_set ) {
724 ret = LTTNG_TRIGGER_STATUS_UNSET;
725 goto end;
726 }
727
728 creds = lttng_trigger_get_credentials(trigger);
729 *uid = lttng_credentials_get_uid(creds);
730
731 end:
732 return ret;
733 }
This page took 0.043339 seconds and 4 git commands to generate.