Coverage Report

Created: 2026-10-24 08:16

/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
}