Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0-only */
2 : /*
3 : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #pragma once
7 :
8 : #include <stdbool.h>
9 : #include <stddef.h>
10 :
11 : #include "core/helper.h"
12 :
13 : /**
14 : * Marshalled data.
15 : *
16 : * @var bf_marsh::data_len
17 : * Length of marshalled data. It doesn't include the length of the header.
18 : * @var bf_marsh::data
19 : * Marshalled data.
20 : */
21 : struct bf_marsh
22 : {
23 : size_t data_len;
24 : char data[];
25 : } bf_packed;
26 :
27 : #define _cleanup_bf_marsh_ __attribute__((__cleanup__(bf_marsh_free)))
28 :
29 : /**
30 : * Returns true if a marsh object is empty (only contains a header).
31 : *
32 : * @param marsh Marsh object to check for empty. Can't be NULL.
33 : * @return True if the @c marsh object is empty, false otherwise.
34 : */
35 1 : static inline bool bf_marsh_is_empty(const struct bf_marsh *marsh)
36 : {
37 1 : bf_assert(marsh);
38 :
39 1 : return marsh->data_len == 0;
40 : }
41 :
42 : /**
43 : * Get the total size of marshalled data.
44 : *
45 : * @param marsh Marshalled data.
46 : * @return Total size of marshalled data, including the header.
47 : */
48 235 : static inline size_t bf_marsh_size(const struct bf_marsh *marsh)
49 : {
50 235 : bf_assert(marsh);
51 :
52 235 : return sizeof(struct bf_marsh) + marsh->data_len;
53 : }
54 :
55 : /**
56 : * Get pointer to the end of a @ref bf_marsh structure.
57 : *
58 : * "End" here, means the first byte after the content of the marshalled data.
59 : *
60 : * @param marsh Marshalled data.
61 : * @return Pointer to the end of the marshalled data.
62 : */
63 501 : static inline void *bf_marsh_end(const struct bf_marsh *marsh)
64 : {
65 501 : bf_assert(marsh);
66 :
67 501 : return (void *)(marsh->data + marsh->data_len);
68 : }
69 :
70 : /**
71 : * Check if `child` is a valid child for `marsh`.
72 : *
73 : * A valid marsh is defined by the following criteria:
74 : * - It starts within its parent's data.
75 : * - Its full length (including the header) is within its parent's data.
76 : * A marsh can only be validated relative to its parent. By recursively
77 : * validating all the children of a marsh, we can validate the whole marsh.
78 : *
79 : * @warning This function doesn't check if the marshalled data is valid. It
80 : * only checks if the marshalled data is within the parent's data and can be
81 : * accessed safely.
82 : *
83 : * @param marsh Parent marsh, must be valid.
84 : * @param child Child marsh to validate. Can be NULL.
85 : * @return true if `child` is a valid child of `marsh`, false otherwise.
86 : */
87 135 : static inline bool bf_marsh_child_is_valid(const struct bf_marsh *marsh,
88 : const struct bf_marsh *child)
89 : {
90 135 : bf_assert(marsh);
91 :
92 134 : if (!child)
93 : return false;
94 :
95 : // Child must start within the parent marsh.
96 265 : if ((void *)child < (void *)marsh->data ||
97 132 : (void *)child > bf_marsh_end(marsh))
98 2 : return false;
99 :
100 : /* Child's data_len field must be within the parent bf_marsh. This check
101 : * is required to safely access child->data_len. */
102 131 : if ((void *)child + sizeof(struct bf_marsh) > bf_marsh_end(marsh))
103 : return false;
104 :
105 : // Child's data must be within the parent bf_marsh.
106 117 : if (bf_marsh_end(child) > bf_marsh_end(marsh))
107 : return false;
108 :
109 : return true;
110 : }
111 :
112 : /**
113 : * Get `marsh`'s child located after `child`.
114 : *
115 : * @param marsh Parent marsh, must be valid.
116 : * @param child Child of `marsh`, must be a valid child of `marsh` or NULL. If
117 : * `child` is NULL, the first child of `marsh` is returned.
118 : * @return Next child of `marsh` after `child`, or NULL if `child` is the
119 : * last valid child of `marsh`.
120 : */
121 130 : static inline struct bf_marsh *bf_marsh_next_child(const struct bf_marsh *marsh,
122 : const struct bf_marsh *child)
123 : {
124 130 : bf_assert(marsh);
125 :
126 : struct bf_marsh *next_child =
127 129 : child ? (struct bf_marsh *)(child->data + child->data_len) :
128 : (struct bf_marsh *)marsh->data;
129 :
130 129 : return bf_marsh_child_is_valid(marsh, next_child) ? next_child : NULL;
131 : }
132 :
133 : /**
134 : * Allocate and initialise a @ref bf_marsh structure.
135 : *
136 : * @param marsh Marsh to be allocated. On success, contains a pointer to the
137 : * marsh structure, and is owned by the caller. If the function fails, it's
138 : * left unchanged.
139 : * @param data Data to be marshalled.
140 : * @param data_len Length of @p data.
141 : * @return 0 on success, negative errno value on error.
142 : */
143 : int bf_marsh_new(struct bf_marsh **marsh, const void *data, size_t data_len);
144 :
145 : /**
146 : * Free a marsh, including its data.
147 : *
148 : * If @p marsh points to NULL, then nothing is done.
149 : *
150 : * @param marsh Marsh to free. Must not be NULL.
151 : */
152 : void bf_marsh_free(struct bf_marsh **marsh);
153 :
154 : /**
155 : * Add a child to a marsh, from another marsh.
156 : *
157 : * @p obj will be added to the data in @p marsh.
158 : *
159 : * @param marsh Parent marsh. Must be non NULL.
160 : * @param obj Marsh to be added as a child. Must be non NULL.
161 : * @return 0 on success, negative errno value on error.
162 : */
163 : int bf_marsh_add_child_obj(struct bf_marsh **marsh, const struct bf_marsh *obj);
164 :
165 : /**
166 : * Add a child to a marsh, from raw data.
167 : *
168 : * If @p data is NULL, nothing is done and @p marsh remain unchanged. In this
169 : * case, @p data_len must be 0.
170 : *
171 : * @param marsh Parent marsh. Must be non NULL.
172 : * @param data Data to add to the marsh.
173 : * @param data_len Length of the data to add to @p marsh.
174 : * @return 0 on success, negative errno value on error.
175 : */
176 : int bf_marsh_add_child_raw(struct bf_marsh **marsh, const void *data,
177 : size_t data_len);
|