Fix: sessiond: ODR violation results in memory corruption
[lttng-tools.git] / src / common / actions / list.cpp
1 /*
2 * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 *
6 */
7
8 #include <common/dynamic-array.hpp>
9 #include <common/error.hpp>
10 #include <common/macros.hpp>
11 #include <common/mi-lttng.hpp>
12 #include <common/payload-view.hpp>
13 #include <common/payload.hpp>
14 #include <lttng/action/action-internal.hpp>
15 #include <lttng/action/list-internal.hpp>
16 #include <lttng/action/list.h>
17
18 #define IS_LIST_ACTION(action) \
19 (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_LIST)
20
21 namespace {
22 struct lttng_action_list {
23 struct lttng_action parent;
24
25 /* The array owns the action elements. */
26 struct lttng_dynamic_pointer_array actions;
27 };
28
29 struct lttng_action_list_comm {
30 uint32_t action_count;
31
32 /*
33 * Variable data: each element serialized sequentially.
34 */
35 char data[];
36 } LTTNG_PACKED;
37 } /* namespace */
38
39 static void destroy_lttng_action_list_element(void *ptr)
40 {
41 struct lttng_action *element = (struct lttng_action *) ptr;
42
43 lttng_action_destroy(element);
44 }
45
46 static struct lttng_action_list *action_list_from_action(
47 const struct lttng_action *action)
48 {
49 LTTNG_ASSERT(action);
50
51 return container_of(action, struct lttng_action_list, parent);
52 }
53
54 static const struct lttng_action_list *action_list_from_action_const(
55 const struct lttng_action *action)
56 {
57 LTTNG_ASSERT(action);
58
59 return container_of(action, struct lttng_action_list, parent);
60 }
61
62 static bool lttng_action_list_validate(struct lttng_action *action)
63 {
64 unsigned int i, count;
65 struct lttng_action_list *action_list;
66 bool valid;
67
68 LTTNG_ASSERT(IS_LIST_ACTION(action));
69
70 action_list = action_list_from_action(action);
71
72 count = lttng_dynamic_pointer_array_get_count(&action_list->actions);
73
74 for (i = 0; i < count; i++) {
75 struct lttng_action *child =
76 (lttng_action *) lttng_dynamic_pointer_array_get_pointer(
77 &action_list->actions, i);
78
79 LTTNG_ASSERT(child);
80
81 if (!lttng_action_validate(child)) {
82 valid = false;
83 goto end;
84 }
85 }
86
87 valid = true;
88
89 end:
90 return valid;
91 }
92
93 static bool lttng_action_list_is_equal(
94 const struct lttng_action *_a, const struct lttng_action *_b)
95 {
96 bool is_equal = false;
97 unsigned int i;
98 unsigned int a_count, b_count;
99
100 if (lttng_action_list_get_count(_a, &a_count) !=
101 LTTNG_ACTION_STATUS_OK) {
102 goto end;
103 }
104
105 if (lttng_action_list_get_count(_b, &b_count) !=
106 LTTNG_ACTION_STATUS_OK) {
107 goto end;
108 }
109
110 if (a_count != b_count) {
111 goto end;
112 }
113
114 for (i = 0; i < a_count; i++) {
115 const struct lttng_action *child_a =
116 lttng_action_list_get_at_index(_a, i);
117 const struct lttng_action *child_b =
118 lttng_action_list_get_at_index(_b, i);
119
120 LTTNG_ASSERT(child_a);
121 LTTNG_ASSERT(child_b);
122
123 if (!lttng_action_is_equal(child_a, child_b)) {
124 goto end;
125 }
126 }
127
128 is_equal = true;
129 end:
130 return is_equal;
131 }
132
133 static int lttng_action_list_serialize(
134 struct lttng_action *action, struct lttng_payload *payload)
135 {
136 struct lttng_action_list *action_list;
137 struct lttng_action_list_comm comm;
138 int ret;
139 unsigned int i, count;
140
141 LTTNG_ASSERT(action);
142 LTTNG_ASSERT(payload);
143 LTTNG_ASSERT(IS_LIST_ACTION(action));
144
145 action_list = action_list_from_action(action);
146
147 DBG("Serializing action list");
148
149 count = lttng_dynamic_pointer_array_get_count(&action_list->actions);
150
151 comm.action_count = count;
152
153 ret = lttng_dynamic_buffer_append(
154 &payload->buffer, &comm, sizeof(comm));
155 if (ret) {
156 ret = -1;
157 goto end;
158 }
159
160 for (i = 0; i < count; i++) {
161 struct lttng_action *child =
162 (lttng_action *) lttng_dynamic_pointer_array_get_pointer(
163 &action_list->actions, i);
164
165 LTTNG_ASSERT(child);
166
167 ret = lttng_action_serialize(child, payload);
168 if (ret) {
169 goto end;
170 }
171 }
172
173 ret = 0;
174
175 end:
176 return ret;
177 }
178
179 static void lttng_action_list_destroy(struct lttng_action *action)
180 {
181 struct lttng_action_list *action_list;
182
183 if (!action) {
184 goto end;
185 }
186
187 action_list = action_list_from_action(action);
188 lttng_dynamic_pointer_array_reset(&action_list->actions);
189 free(action_list);
190
191 end:
192 return;
193 }
194
195 ssize_t lttng_action_list_create_from_payload(
196 struct lttng_payload_view *view,
197 struct lttng_action **p_action)
198 {
199 ssize_t consumed_len;
200 const struct lttng_action_list_comm *comm;
201 struct lttng_action *list;
202 struct lttng_action *child_action = NULL;
203 enum lttng_action_status status;
204 size_t i;
205
206 list = lttng_action_list_create();
207 if (!list) {
208 consumed_len = -1;
209 goto end;
210 }
211
212 comm = (typeof(comm)) view->buffer.data;
213
214 consumed_len = sizeof(struct lttng_action_list_comm);
215
216 for (i = 0; i < comm->action_count; i++) {
217 ssize_t consumed_len_child;
218 struct lttng_payload_view child_view =
219 lttng_payload_view_from_view(view, consumed_len,
220 view->buffer.size - consumed_len);
221
222 if (!lttng_payload_view_is_valid(&child_view)) {
223 consumed_len = -1;
224 goto end;
225 }
226
227 consumed_len_child = lttng_action_create_from_payload(
228 &child_view, &child_action);
229 if (consumed_len_child < 0) {
230 consumed_len = -1;
231 goto end;
232 }
233
234 status = lttng_action_list_add_action(list, child_action);
235 if (status != LTTNG_ACTION_STATUS_OK) {
236 consumed_len = -1;
237 goto end;
238 }
239
240 /* Transfer ownership to the action list. */
241 lttng_action_put(child_action);
242 child_action = NULL;
243
244 consumed_len += consumed_len_child;
245 }
246
247 *p_action = list;
248 list = NULL;
249
250 end:
251 lttng_action_list_destroy(list);
252 return consumed_len;
253 }
254
255 static enum lttng_action_status lttng_action_list_add_error_query_results(
256 const struct lttng_action *action,
257 struct lttng_error_query_results *results)
258 {
259 unsigned int i, count;
260 enum lttng_action_status action_status;
261 const struct lttng_action_list *list =
262 container_of(action, typeof(*list), parent);
263
264 action_status = lttng_action_list_get_count(action, &count);
265 if (action_status != LTTNG_ACTION_STATUS_OK) {
266 goto end;
267 }
268
269 for (i = 0; i < count; i++) {
270 struct lttng_action *inner_action =
271 lttng_action_list_borrow_mutable_at_index(action, i);
272
273 action_status = lttng_action_add_error_query_results(
274 inner_action, results);
275 if (action_status != LTTNG_ACTION_STATUS_OK) {
276 goto end;
277 }
278 }
279 end:
280 return action_status;
281 }
282
283 enum lttng_error_code lttng_action_list_mi_serialize(
284 const struct lttng_trigger *trigger,
285 const struct lttng_action *action,
286 struct mi_writer *writer,
287 const struct mi_lttng_error_query_callbacks
288 *error_query_callbacks,
289 struct lttng_dynamic_array *action_path_indexes)
290 {
291 int ret;
292 struct lttng_action_list *action_list;
293 unsigned int i, count;
294 enum lttng_error_code ret_code;
295
296 LTTNG_ASSERT(action);
297 LTTNG_ASSERT(IS_LIST_ACTION(action));
298 LTTNG_ASSERT(writer);
299
300 /* Open action list. */
301 ret = mi_lttng_writer_open_element(
302 writer, mi_lttng_element_action_list);
303 if (ret) {
304 goto mi_error;
305 }
306
307 /* Serialize every action of the list. */
308 action_list = action_list_from_action(action);
309 count = lttng_dynamic_pointer_array_get_count(&action_list->actions);
310 for (i = 0; i < count; i++) {
311 const struct lttng_action *child =
312 lttng_action_list_get_at_index(action, i);
313 const uint64_t index = (uint64_t) i;
314
315 LTTNG_ASSERT(child);
316
317 /*
318 * Add the index to the action path.
319 *
320 * This index is replaced on every iteration to walk the action
321 * tree in-order and to re-use the dynamic array instead of
322 * copying it at every level.
323 */
324 ret = lttng_dynamic_array_add_element(
325 action_path_indexes, &index);
326 if (ret) {
327 ret_code = LTTNG_ERR_NOMEM;
328 goto end;
329 }
330
331 ret_code = lttng_action_mi_serialize(trigger, child, writer,
332 error_query_callbacks, action_path_indexes);
333 if (ret_code != LTTNG_OK) {
334 goto end;
335 }
336
337 ret = lttng_dynamic_array_remove_element(action_path_indexes,
338 lttng_dynamic_array_get_count(
339 action_path_indexes) -
340 1);
341 if (ret) {
342 ret_code = LTTNG_ERR_UNK;
343 goto end;
344 }
345 }
346
347 /* Close action_list element. */
348 ret = mi_lttng_writer_close_element(writer);
349 if (ret) {
350 goto mi_error;
351 }
352
353 ret_code = LTTNG_OK;
354 goto end;
355
356 mi_error:
357 ret_code = LTTNG_ERR_MI_IO_FAIL;
358 end:
359 return ret_code;
360 }
361
362 struct lttng_action *lttng_action_list_create(void)
363 {
364 struct lttng_action_list *action_list;
365 struct lttng_action *action;
366
367 action_list = zmalloc<lttng_action_list>();
368 if (!action_list) {
369 action = NULL;
370 goto end;
371 }
372
373 action = &action_list->parent;
374
375 /*
376 * The mi for the list is handled at the lttng_action_mi level to ease
377 * action path management for error query.
378 */
379 lttng_action_init(action, LTTNG_ACTION_TYPE_LIST,
380 lttng_action_list_validate, lttng_action_list_serialize,
381 lttng_action_list_is_equal, lttng_action_list_destroy,
382 NULL, lttng_action_list_add_error_query_results, NULL);
383
384 lttng_dynamic_pointer_array_init(&action_list->actions,
385 destroy_lttng_action_list_element);
386
387 end:
388 return action;
389 }
390
391 enum lttng_action_status lttng_action_list_add_action(
392 struct lttng_action *list, struct lttng_action *action)
393 {
394 struct lttng_action_list *action_list;
395 enum lttng_action_status status;
396 int ret;
397
398 if (!list || !IS_LIST_ACTION(list) || !action) {
399 status = LTTNG_ACTION_STATUS_INVALID;
400 goto end;
401 }
402
403 /*
404 * Don't allow adding lists in lists for now, since we're afraid of
405 * cycles.
406 */
407 if (IS_LIST_ACTION(action)) {
408 status = LTTNG_ACTION_STATUS_INVALID;
409 goto end;
410 }
411
412 action_list = action_list_from_action(list);
413
414 ret = lttng_dynamic_pointer_array_add_pointer(&action_list->actions,
415 action);
416 if (ret < 0) {
417 status = LTTNG_ACTION_STATUS_ERROR;
418 goto end;
419 }
420
421 /* Take ownership of the object. */
422 lttng_action_get(action);
423 status = LTTNG_ACTION_STATUS_OK;
424 end:
425 return status;
426 }
427
428 enum lttng_action_status lttng_action_list_get_count(
429 const struct lttng_action *list, unsigned int *count)
430 {
431 const struct lttng_action_list *action_list;
432 enum lttng_action_status status = LTTNG_ACTION_STATUS_OK;
433
434 if (!list || !IS_LIST_ACTION(list)) {
435 status = LTTNG_ACTION_STATUS_INVALID;
436 *count = 0;
437 goto end;
438 }
439
440 action_list = action_list_from_action_const(list);
441 *count = lttng_dynamic_pointer_array_get_count(&action_list->actions);
442 end:
443 return status;
444 }
445
446 const struct lttng_action *lttng_action_list_get_at_index(
447 const struct lttng_action *list, unsigned int index)
448 {
449 return lttng_action_list_borrow_mutable_at_index(list, index);
450 }
451
452 struct lttng_action *lttng_action_list_borrow_mutable_at_index(
453 const struct lttng_action *list, unsigned int index)
454 {
455 unsigned int count;
456 const struct lttng_action_list *action_list;
457 struct lttng_action *action = NULL;
458
459 if (lttng_action_list_get_count(list, &count) !=
460 LTTNG_ACTION_STATUS_OK) {
461 goto end;
462 }
463
464 if (index >= count) {
465 goto end;
466 }
467
468 action_list = action_list_from_action_const(list);
469 action = (lttng_action *) lttng_dynamic_pointer_array_get_pointer(&action_list->actions,
470 index);
471 end:
472 return action;
473 }
This page took 0.052476 seconds and 4 git commands to generate.