Coverage Report

Created: 2026-02-09 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnutls/lib/mbuffers.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2009-2012 Free Software Foundation, Inc.
3
 *
4
 * Author: Jonathan Bastien-Filiatrault
5
 *
6
 * This file is part of GNUTLS.
7
 *
8
 * The GNUTLS library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public License
10
 * as published by the Free Software Foundation; either version 2.1 of
11
 * the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful, but
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
20
 *
21
 */
22
23
#include "mbuffers.h"
24
#include "errors.h"
25
26
/* Here be mbuffers */
27
28
/* A note on terminology:
29
 *
30
 * Variables named bufel designate a single buffer segment (mbuffer_st
31
 * type). This type is textually referred to as a "segment" or a
32
 * "buffer element".
33
 *
34
 * Variables named buf desigate a chain of buffer segments
35
 * (mbuffer_head_st type).  This type is textually referred to as a
36
 * "buffer head" or simply as "buffer".
37
 *
38
 * Design objectives:
39
 *
40
 * - Make existing code easier to understand.
41
 * - Make common operations more efficient by avoiding unnecessary
42
 *    copying.
43
 * - Provide a common datatype with a well-known interface to move
44
 *    data around and through the multiple protocol layers.
45
 * - Enable a future implementation of DTLS, which needs the concept
46
 *    of record boundaries.
47
 */
48
49
/* Initialize a buffer head.
50
 *
51
 * Cost: O(1)
52
 */
53
void _mbuffer_head_init(mbuffer_head_st *buf)
54
1.45M
{
55
1.45M
  buf->head = NULL;
56
1.45M
  buf->tail = NULL;
57
58
1.45M
  buf->length = 0;
59
1.45M
  buf->byte_length = 0;
60
1.45M
}
61
62
/* Deallocate all buffer segments and reset the buffer head.
63
 *
64
 * Cost: O(n)
65
 * n: Number of segments currently in the buffer.
66
 */
67
void _mbuffer_head_clear(mbuffer_head_st *buf)
68
1.21M
{
69
1.21M
  mbuffer_st *bufel, *next;
70
71
3.19M
  for (bufel = buf->head; bufel != NULL; bufel = next) {
72
1.97M
    next = bufel->next;
73
1.97M
    gnutls_free(bufel);
74
1.97M
  }
75
76
1.21M
  _mbuffer_head_init(buf);
77
1.21M
}
78
79
/* Append a segment to the end of this buffer.
80
 *
81
 * Cost: O(1)
82
 */
83
void _mbuffer_enqueue(mbuffer_head_st *buf, mbuffer_st *bufel)
84
3.98M
{
85
3.98M
  bufel->next = NULL;
86
3.98M
  bufel->prev = buf->tail;
87
88
3.98M
  buf->length++;
89
3.98M
  buf->byte_length += bufel->msg.size - bufel->mark;
90
91
3.98M
  if (buf->tail != NULL)
92
1.03M
    buf->tail->next = bufel;
93
2.94M
  else
94
2.94M
    buf->head = bufel;
95
3.98M
  buf->tail = bufel;
96
3.98M
}
97
98
/* Remove a segment from the buffer.
99
 *
100
 * Cost: O(1)
101
 *
102
 * Returns the buffer following it.
103
 */
104
mbuffer_st *_mbuffer_dequeue(mbuffer_head_st *buf, mbuffer_st *bufel)
105
2.00M
{
106
2.00M
  mbuffer_st *ret = bufel->next;
107
108
2.00M
  if (buf->tail == bufel) /* if last */
109
1.95M
    buf->tail = bufel->prev;
110
111
2.00M
  if (buf->head == bufel) /* if first */
112
2.00M
    buf->head = bufel->next;
113
114
2.00M
  if (bufel->prev)
115
0
    bufel->prev->next = bufel->next;
116
117
2.00M
  if (bufel->next)
118
55.8k
    bufel->next->prev = NULL;
119
120
2.00M
  buf->length--;
121
2.00M
  buf->byte_length -= bufel->msg.size - bufel->mark;
122
123
2.00M
  bufel->next = bufel->prev = NULL;
124
125
2.00M
  return ret;
126
2.00M
}
127
128
/* Append a segment to the beginning of this buffer.
129
 *
130
 * Cost: O(1)
131
 */
132
void _mbuffer_head_push_first(mbuffer_head_st *buf, mbuffer_st *bufel)
133
1.59k
{
134
1.59k
  bufel->prev = NULL;
135
1.59k
  bufel->next = buf->head;
136
137
1.59k
  buf->length++;
138
1.59k
  buf->byte_length += bufel->msg.size - bufel->mark;
139
140
1.59k
  if (buf->head != NULL)
141
0
    buf->head->prev = bufel;
142
1.59k
  else
143
1.59k
    buf->tail = bufel;
144
1.59k
  buf->head = bufel;
145
1.59k
}
146
147
/* Get a reference to the first segment of the buffer and
148
 * remove it from the list.
149
 *
150
 * Used to start iteration.
151
 *
152
 * Cost: O(1)
153
 */
154
mbuffer_st *_mbuffer_head_pop_first(mbuffer_head_st *buf)
155
10.4k
{
156
10.4k
  mbuffer_st *bufel = buf->head;
157
158
10.4k
  if (buf->head == NULL)
159
0
    return NULL;
160
161
10.4k
  _mbuffer_dequeue(buf, bufel);
162
163
10.4k
  return bufel;
164
10.4k
}
165
166
/* Get a reference to the first segment of the buffer and its data.
167
 *
168
 * Used to start iteration or to peek at the data.
169
 *
170
 * Cost: O(1)
171
 */
172
mbuffer_st *_mbuffer_head_get_first(mbuffer_head_st *buf, gnutls_datum_t *msg)
173
9.35M
{
174
9.35M
  mbuffer_st *bufel = buf->head;
175
176
9.35M
  if (msg) {
177
5.23M
    if (bufel) {
178
3.16M
      msg->data = bufel->msg.data + bufel->mark;
179
3.16M
      msg->size = bufel->msg.size - bufel->mark;
180
3.16M
    } else {
181
2.06M
      msg->data = NULL;
182
2.06M
      msg->size = 0;
183
2.06M
    }
184
5.23M
  }
185
9.35M
  return bufel;
186
9.35M
}
187
188
/* Get a reference to the next segment of the buffer and its data.
189
 *
190
 * Used to iterate over the buffer segments.
191
 *
192
 * Cost: O(1)
193
 */
194
mbuffer_st *_mbuffer_head_get_next(mbuffer_st *cur, gnutls_datum_t *msg)
195
2.03M
{
196
2.03M
  mbuffer_st *bufel = cur->next;
197
198
2.03M
  if (msg) {
199
2.03M
    if (bufel) {
200
1.01M
      msg->data = bufel->msg.data + bufel->mark;
201
1.01M
      msg->size = bufel->msg.size - bufel->mark;
202
1.02M
    } else {
203
1.02M
      msg->data = NULL;
204
1.02M
      msg->size = 0;
205
1.02M
    }
206
2.03M
  }
207
2.03M
  return bufel;
208
2.03M
}
209
210
/* Remove the first segment from the buffer.
211
 *
212
 * Used to dequeue data from the buffer. Not yet exposed in the
213
 * internal interface since it is not yet needed outside of this unit.
214
 *
215
 * Cost: O(1)
216
 */
217
static inline void remove_front(mbuffer_head_st *buf)
218
1.99M
{
219
1.99M
  mbuffer_st *bufel = buf->head;
220
221
1.99M
  if (!bufel)
222
0
    return;
223
224
1.99M
  _mbuffer_dequeue(buf, bufel);
225
1.99M
  gnutls_free(bufel);
226
1.99M
}
227
228
/* Remove a specified number of bytes from the start of the buffer.
229
 *
230
 * Useful for uses that treat the buffer as a simple array of bytes.
231
 *
232
 * If more than one mbuffer_st have been removed it
233
 * returns 1, 0 otherwise and an error code on error.
234
 *
235
 * Cost: O(n)
236
 * n: Number of segments needed to remove the specified amount of data.
237
 */
238
int _mbuffer_head_remove_bytes(mbuffer_head_st *buf, size_t bytes)
239
2.17M
{
240
2.17M
  size_t left = bytes;
241
2.17M
  mbuffer_st *bufel, *next;
242
2.17M
  int ret = 0;
243
244
2.17M
  if (bytes > buf->byte_length) {
245
0
    gnutls_assert();
246
0
    return GNUTLS_E_INVALID_REQUEST;
247
0
  }
248
249
4.37M
  for (bufel = buf->head; bufel != NULL && left > 0; bufel = next) {
250
2.20M
    next = bufel->next;
251
252
2.20M
    if (left >= (bufel->msg.size - bufel->mark)) {
253
1.99M
      left -= (bufel->msg.size - bufel->mark);
254
1.99M
      remove_front(buf);
255
1.99M
      ret = 1;
256
1.99M
    } else {
257
201k
      bufel->mark += left;
258
201k
      buf->byte_length -= left;
259
201k
      left = 0;
260
201k
    }
261
2.20M
  }
262
2.17M
  return ret;
263
2.17M
}
264
265
/* Allocate a buffer segment. The segment is not initially "owned" by
266
 * any buffer.
267
 *
268
 * maximum_size: Amount of data that this segment can contain.
269
 *
270
 * Returns the segment or NULL on error.
271
 *
272
 * Cost: O(1)
273
 */
274
mbuffer_st *_mbuffer_alloc(size_t maximum_size)
275
23.8k
{
276
23.8k
  mbuffer_st *st;
277
278
23.8k
  st = gnutls_malloc(maximum_size + sizeof(mbuffer_st));
279
23.8k
  if (st == NULL) {
280
0
    gnutls_assert();
281
0
    return NULL;
282
0
  }
283
284
  /* set the structure to zero */
285
23.8k
  memset(st, 0, sizeof(*st));
286
287
  /* payload points after the mbuffer_st structure */
288
23.8k
  st->msg.data = (uint8_t *)st + sizeof(mbuffer_st);
289
23.8k
  st->msg.size = 0;
290
23.8k
  st->maximum_size = maximum_size;
291
292
23.8k
  return st;
293
23.8k
}
294
295
/* Copy data into a segment. The segment must not be part of a buffer
296
 * head when using this function.
297
 *
298
 * Bounds checking is performed by this function.
299
 *
300
 * Returns 0 on success or an error code otherwise.
301
 *
302
 * Cost: O(n)
303
 * n: number of bytes to copy
304
 */
305
int _mbuffer_append_data(mbuffer_st *bufel, void *newdata, size_t newdata_size)
306
1.41k
{
307
1.41k
  if (bufel->msg.size + newdata_size <= bufel->maximum_size) {
308
1.41k
    memcpy(&bufel->msg.data[bufel->msg.size], newdata,
309
1.41k
           newdata_size);
310
1.41k
    bufel->msg.size += newdata_size;
311
1.41k
  } else {
312
0
    gnutls_assert();
313
0
    return GNUTLS_E_INVALID_REQUEST;
314
0
  }
315
316
1.41k
  return 0;
317
1.41k
}
318
319
#ifdef ENABLE_ALIGN16
320
12.0M
#define ALIGN_SIZE 16
321
322
/* Allocate a 16-byte aligned buffer segment. The segment is not initially "owned" by
323
 * any buffer.
324
 *
325
 * maximum_size: Amount of data that this segment can contain.
326
 * align_pos: identifies the position of the buffer that will be aligned at 16-bytes
327
 *
328
 * This function should be used to ensure that encrypted data or data to
329
 * be encrypted are properly aligned.
330
 *
331
 * Returns the segment or NULL on error.
332
 *
333
 * Cost: O(1)
334
 */
335
mbuffer_st *_mbuffer_alloc_align16(size_t maximum_size, unsigned align_pos)
336
3.98M
{
337
3.98M
  mbuffer_st *st;
338
3.98M
  size_t cur_alignment;
339
340
3.98M
  st = gnutls_malloc(maximum_size + sizeof(mbuffer_st) + ALIGN_SIZE);
341
3.98M
  if (st == NULL) {
342
0
    gnutls_assert();
343
0
    return NULL;
344
0
  }
345
346
  /* set the structure to zero */
347
3.98M
  memset(st, 0, sizeof(*st));
348
349
  /* payload points after the mbuffer_st structure */
350
3.98M
  st->msg.data = (uint8_t *)st + sizeof(mbuffer_st);
351
352
3.98M
  cur_alignment = ((size_t)(st->msg.data + align_pos)) % ALIGN_SIZE;
353
3.98M
  if (cur_alignment > 0)
354
2.92M
    st->msg.data += ALIGN_SIZE - cur_alignment;
355
356
3.98M
  st->msg.size = 0;
357
3.98M
  st->maximum_size = maximum_size;
358
359
3.98M
  return st;
360
3.98M
}
361
362
static unsigned is_aligned16(mbuffer_st *bufel, unsigned align_pos)
363
1.11M
{
364
1.11M
  uint8_t *ptr = _mbuffer_get_udata_ptr(bufel);
365
366
1.11M
  if (((size_t)(ptr + align_pos)) % ALIGN_SIZE == 0)
367
1.11M
    return 1;
368
900
  else
369
900
    return 0;
370
1.11M
}
371
372
/* Takes a buffer in multiple chunks and puts all the data in a single
373
 * contiguous segment, ensuring that the @align_pos is 16-byte aligned.
374
 *
375
 * Returns 0 on success or an error code otherwise.
376
 *
377
 * Cost: O(n)
378
 * n: number of segments initially in the buffer
379
 */
380
int _mbuffer_linearize_align16(mbuffer_head_st *buf, unsigned align_pos)
381
2.09M
{
382
2.09M
  mbuffer_st *bufel, *cur;
383
2.09M
  gnutls_datum_t msg;
384
2.09M
  size_t pos = 0;
385
386
2.09M
  if (buf->length == 0) {
387
    /* Nothing to do */
388
0
    return 0;
389
0
  }
390
391
2.09M
  bufel = _mbuffer_head_get_first(buf, NULL);
392
2.09M
  if (buf->length == 1 && is_aligned16(bufel, align_pos))
393
1.11M
    return 0;
394
395
982k
  bufel = _mbuffer_alloc_align16(buf->byte_length, align_pos);
396
982k
  if (!bufel) {
397
0
    gnutls_assert();
398
0
    return GNUTLS_E_MEMORY_ERROR;
399
0
  }
400
401
982k
  bufel->type = _mbuffer_head_get_first(buf, NULL)->type;
402
403
2.94M
  for (cur = _mbuffer_head_get_first(buf, &msg); msg.data != NULL;
404
1.96M
       cur = _mbuffer_head_get_next(cur, &msg)) {
405
1.96M
    memcpy(&bufel->msg.data[pos], msg.data, msg.size);
406
1.96M
    bufel->msg.size += msg.size;
407
1.96M
    pos += msg.size;
408
1.96M
  }
409
410
982k
  _mbuffer_head_clear(buf);
411
982k
  _mbuffer_enqueue(buf, bufel);
412
413
982k
  return 0;
414
982k
}
415
#else
416
int _mbuffer_linearize(mbuffer_head_st *buf)
417
{
418
  mbuffer_st *bufel, *cur;
419
  gnutls_datum_t msg;
420
  size_t pos = 0;
421
422
  if (buf->length <= 1) {
423
    /* Nothing to do */
424
    return 0;
425
  }
426
427
  bufel = _mbuffer_alloc(buf->byte_length);
428
  if (!bufel) {
429
    gnutls_assert();
430
    return GNUTLS_E_MEMORY_ERROR;
431
  }
432
433
  bufel->type = _mbuffer_head_get_first(buf, NULL)->type;
434
435
  for (cur = _mbuffer_head_get_first(buf, &msg); msg.data != NULL;
436
       cur = _mbuffer_head_get_next(cur, &msg)) {
437
    memcpy(&bufel->msg.data[pos], msg.data, msg.size);
438
    bufel->msg.size += msg.size;
439
    pos += msg.size;
440
  }
441
442
  _mbuffer_head_clear(buf);
443
  _mbuffer_enqueue(buf, bufel);
444
445
  return 0;
446
}
447
#endif