/work/toxcore/bin_unpack.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2022-2026 The TokTok team. |
3 | | */ |
4 | | |
5 | | #include "bin_unpack.h" |
6 | | |
7 | | #include <assert.h> |
8 | | #include <string.h> |
9 | | |
10 | | #include "../third_party/cmp/cmp.h" |
11 | | #include "attributes.h" |
12 | | #include "ccompat.h" |
13 | | #include "mem.h" |
14 | | |
15 | | struct Bin_Unpack { |
16 | | const Memory *_Nonnull mem; |
17 | | |
18 | | const uint8_t *_Nonnull bytes; |
19 | | uint32_t bytes_size; |
20 | | cmp_ctx_t ctx; |
21 | | }; |
22 | | |
23 | | static bool buf_reader(cmp_ctx_t *_Nonnull ctx, void *_Nullable data, size_t limit) |
24 | 262k | { |
25 | 262k | uint8_t *const bytes = (uint8_t *)data; |
26 | | |
27 | 262k | if (limit == 0) { |
28 | 357 | return true; |
29 | 357 | } |
30 | | |
31 | 262k | if (bytes == nullptr) { |
32 | 0 | return false; |
33 | 0 | } |
34 | 262k | Bin_Unpack *const reader = (Bin_Unpack *)ctx->buf; |
35 | 262k | assert(reader != nullptr && reader->bytes != nullptr); |
36 | 262k | if (limit > reader->bytes_size) { |
37 | 4.54k | return false; |
38 | 4.54k | } |
39 | 257k | memcpy(bytes, reader->bytes, limit); |
40 | 257k | reader->bytes += limit; |
41 | 257k | reader->bytes_size -= limit; |
42 | 257k | return true; |
43 | 262k | } |
44 | | |
45 | | static bool buf_skipper(cmp_ctx_t *_Nonnull ctx, size_t count) |
46 | 0 | { |
47 | 0 | if (count == 0) { |
48 | 0 | return true; |
49 | 0 | } |
50 | 0 | Bin_Unpack *const reader = (Bin_Unpack *)ctx->buf; |
51 | 0 | assert(reader != nullptr && reader->bytes != nullptr); |
52 | 0 | if (count > reader->bytes_size) { |
53 | 0 | return false; |
54 | 0 | } |
55 | 0 | reader->bytes += count; |
56 | 0 | reader->bytes_size -= count; |
57 | 0 | return true; |
58 | 0 | } |
59 | | |
60 | | static size_t null_writer(cmp_ctx_t *_Nonnull ctx, const void *_Nullable data, size_t count) |
61 | 0 | { |
62 | 0 | assert(count == 0); |
63 | 0 | return 0; |
64 | 0 | } |
65 | | |
66 | | static void bin_unpack_init(Bin_Unpack *_Nonnull bu, const Memory *_Nonnull mem, const uint8_t *_Nonnull buf, uint32_t buf_size) |
67 | 25.6k | { |
68 | 25.6k | bu->mem = mem; |
69 | 25.6k | bu->bytes = buf; |
70 | 25.6k | bu->bytes_size = buf_size; |
71 | 25.6k | cmp_init(&bu->ctx, bu, buf_reader, buf_skipper, null_writer); |
72 | 25.6k | } |
73 | | |
74 | | bool bin_unpack_obj(const Memory *mem, bin_unpack_cb *callback, void *obj, const uint8_t *buf, uint32_t buf_size) |
75 | 25.6k | { |
76 | 25.6k | Bin_Unpack bu; |
77 | 25.6k | bin_unpack_init(&bu, mem, buf, buf_size); |
78 | 25.6k | return callback(obj, &bu); |
79 | 25.6k | } |
80 | | |
81 | | bool bin_unpack_array(Bin_Unpack *bu, uint32_t *size) |
82 | 25.6k | { |
83 | 25.6k | return cmp_read_array(&bu->ctx, size) && *size <= bu->bytes_size; |
84 | 25.6k | } |
85 | | |
86 | | bool bin_unpack_array_fixed(Bin_Unpack *bu, uint32_t required_size, uint32_t *actual_size) |
87 | 58.3k | { |
88 | 58.3k | uint32_t size = 0; |
89 | 58.3k | const bool success = cmp_read_array(&bu->ctx, &size) && size == required_size; |
90 | 58.3k | if (actual_size != nullptr) { |
91 | 24.7k | *actual_size = size; |
92 | 24.7k | } |
93 | 58.3k | return success; |
94 | 58.3k | } |
95 | | |
96 | | bool bin_unpack_bool(Bin_Unpack *bu, bool *val) |
97 | 20.5k | { |
98 | 20.5k | return cmp_read_bool(&bu->ctx, val); |
99 | 20.5k | } |
100 | | |
101 | | bool bin_unpack_u08(Bin_Unpack *bu, uint8_t *val) |
102 | 26.0k | { |
103 | 26.0k | return cmp_read_uchar(&bu->ctx, val); |
104 | 26.0k | } |
105 | | |
106 | | bool bin_unpack_u16(Bin_Unpack *bu, uint16_t *val) |
107 | 49.8k | { |
108 | 49.8k | return cmp_read_ushort(&bu->ctx, val); |
109 | 49.8k | } |
110 | | |
111 | | bool bin_unpack_u32(Bin_Unpack *bu, uint32_t *val) |
112 | 31.3k | { |
113 | 31.3k | return cmp_read_uint(&bu->ctx, val); |
114 | 31.3k | } |
115 | | |
116 | | bool bin_unpack_u64(Bin_Unpack *bu, uint64_t *val) |
117 | 275 | { |
118 | 275 | return cmp_read_ulong(&bu->ctx, val); |
119 | 275 | } |
120 | | |
121 | | bool bin_unpack_nil(Bin_Unpack *bu) |
122 | 49 | { |
123 | 49 | return cmp_read_nil(&bu->ctx); |
124 | 49 | } |
125 | | |
126 | | bool bin_unpack_bin(Bin_Unpack *bu, uint8_t **data_ptr, uint32_t *data_length_ptr) |
127 | 529 | { |
128 | 529 | uint32_t bin_size; |
129 | 529 | if (!bin_unpack_bin_size(bu, &bin_size) || bin_size > bu->bytes_size) { |
130 | | // There aren't as many bytes as this bin claims to want to allocate. |
131 | 61 | return false; |
132 | 61 | } |
133 | | |
134 | 468 | if (bin_size == 0) { |
135 | 426 | *data_ptr = nullptr; |
136 | 426 | *data_length_ptr = 0; |
137 | 426 | return true; |
138 | 426 | } |
139 | | |
140 | 42 | uint8_t *const data = (uint8_t *)mem_balloc(bu->mem, bin_size); |
141 | | |
142 | 42 | if (data == nullptr) { |
143 | 1 | return false; |
144 | 1 | } |
145 | | |
146 | 41 | if (!bin_unpack_bin_b(bu, data, bin_size)) { |
147 | 0 | mem_delete(bu->mem, data); |
148 | 0 | return false; |
149 | 0 | } |
150 | | |
151 | 41 | *data_ptr = data; |
152 | 41 | *data_length_ptr = bin_size; |
153 | 41 | return true; |
154 | 41 | } |
155 | | |
156 | | bool bin_unpack_str(Bin_Unpack *bu, char **str_ptr, uint32_t *str_length_ptr) |
157 | 57 | { |
158 | 57 | uint32_t str_size; |
159 | 57 | if (!cmp_read_str_size(&bu->ctx, &str_size) || str_size > bu->bytes_size) { |
160 | 9 | return false; |
161 | 9 | } |
162 | | |
163 | 48 | if (str_size == UINT32_MAX) { |
164 | 0 | return false; |
165 | 0 | } |
166 | | |
167 | 48 | char *const str = (char *)mem_balloc(bu->mem, str_size + 1); |
168 | | |
169 | 48 | if (str == nullptr) { |
170 | 1 | return false; |
171 | 1 | } |
172 | | |
173 | 47 | if (str_size > 0) { |
174 | 38 | if (!bin_unpack_bin_b(bu, (uint8_t *)str, str_size)) { |
175 | 0 | mem_delete(bu->mem, str); |
176 | 0 | return false; |
177 | 0 | } |
178 | 38 | } |
179 | | |
180 | 47 | str[str_size] = 0; |
181 | 47 | *str_ptr = str; |
182 | 47 | *str_length_ptr = str_size; |
183 | 47 | return true; |
184 | 47 | } |
185 | | |
186 | | bool bin_unpack_bin_max(Bin_Unpack *bu, uint8_t *data, uint16_t *data_length_ptr, uint16_t max_data_length) |
187 | 1.08k | { |
188 | 1.08k | uint32_t bin_size; |
189 | 1.08k | if (!bin_unpack_bin_size(bu, &bin_size) || bin_size > max_data_length) { |
190 | 59 | return false; |
191 | 59 | } |
192 | | |
193 | 1.02k | *data_length_ptr = bin_size; |
194 | | |
195 | 1.02k | return bin_unpack_bin_b(bu, data, bin_size); |
196 | 1.08k | } |
197 | | |
198 | | bool bin_unpack_bin_fixed(Bin_Unpack *bu, uint8_t *data, uint32_t data_length) |
199 | 3.30k | { |
200 | 3.30k | uint32_t bin_size; |
201 | 3.30k | if (!bin_unpack_bin_size(bu, &bin_size) || bin_size != data_length) { |
202 | 238 | return false; |
203 | 238 | } |
204 | | |
205 | 3.06k | return bin_unpack_bin_b(bu, data, bin_size); |
206 | 3.30k | } |
207 | | |
208 | | bool bin_unpack_bin_size(Bin_Unpack *bu, uint32_t *size) |
209 | 4.91k | { |
210 | 4.91k | return cmp_read_bin_size(&bu->ctx, size); |
211 | 4.91k | } |
212 | | |
213 | | bool bin_unpack_u08_b(Bin_Unpack *bu, uint8_t *val) |
214 | 18 | { |
215 | 18 | return bin_unpack_bin_b(bu, val, 1); |
216 | 18 | } |
217 | | |
218 | | bool bin_unpack_u16_b(Bin_Unpack *bu, uint16_t *val) |
219 | 9 | { |
220 | 9 | uint8_t hi = 0; |
221 | 9 | uint8_t lo = 0; |
222 | 9 | if (!(bin_unpack_u08_b(bu, &hi) |
223 | 9 | && bin_unpack_u08_b(bu, &lo))) { |
224 | 0 | return false; |
225 | 0 | } |
226 | 9 | *val = ((uint16_t)hi << 8) | lo; |
227 | 9 | return true; |
228 | 9 | } |
229 | | |
230 | | bool bin_unpack_u32_b(Bin_Unpack *bu, uint32_t *val) |
231 | 4 | { |
232 | 4 | uint16_t hi = 0; |
233 | 4 | uint16_t lo = 0; |
234 | 4 | if (!(bin_unpack_u16_b(bu, &hi) |
235 | 4 | && bin_unpack_u16_b(bu, &lo))) { |
236 | 0 | return false; |
237 | 0 | } |
238 | 4 | *val = ((uint32_t)hi << 16) | lo; |
239 | 4 | return true; |
240 | 4 | } |
241 | | |
242 | | bool bin_unpack_u64_b(Bin_Unpack *bu, uint64_t *val) |
243 | 2 | { |
244 | 2 | uint32_t hi = 0; |
245 | 2 | uint32_t lo = 0; |
246 | 2 | if (!(bin_unpack_u32_b(bu, &hi) |
247 | 2 | && bin_unpack_u32_b(bu, &lo))) { |
248 | 0 | return false; |
249 | 0 | } |
250 | 2 | *val = ((uint64_t)hi << 32) | lo; |
251 | 2 | return true; |
252 | 2 | } |
253 | | |
254 | | bool bin_unpack_bin_b(Bin_Unpack *bu, uint8_t *data, uint32_t length) |
255 | 4.18k | { |
256 | 4.18k | return bu->ctx.read(&bu->ctx, data, length); |
257 | 4.18k | } |