Coverage Report

Created: 2023-03-26 07:33

/src/gnutls/lib/mbuffers.c
Line
Count
Source (jump to first uncovered line)
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
0
{
55
0
  buf->head = NULL;
56
0
  buf->tail = NULL;
57
58
0
  buf->length = 0;
59
0
  buf->byte_length = 0;
60
0
}
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
0
{
69
0
  mbuffer_st *bufel, *next;
70
71
0
  for (bufel = buf->head; bufel != NULL; bufel = next) {
72
0
    next = bufel->next;
73
0
    gnutls_free(bufel);
74
0
  }
75
76
0
  _mbuffer_head_init(buf);
77
0
}
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
0
{
85
0
  bufel->next = NULL;
86
0
  bufel->prev = buf->tail;
87
88
0
  buf->length++;
89
0
  buf->byte_length += bufel->msg.size - bufel->mark;
90
91
0
  if (buf->tail != NULL)
92
0
    buf->tail->next = bufel;
93
0
  else
94
0
    buf->head = bufel;
95
0
  buf->tail = bufel;
96
0
}
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
0
{
106
0
  mbuffer_st *ret = bufel->next;
107
108
0
  if (buf->tail == bufel) /* if last */
109
0
    buf->tail = bufel->prev;
110
111
0
  if (buf->head == bufel) /* if first */
112
0
    buf->head = bufel->next;
113
114
0
  if (bufel->prev)
115
0
    bufel->prev->next = bufel->next;
116
117
0
  if (bufel->next)
118
0
    bufel->next->prev = NULL;
119
120
0
  buf->length--;
121
0
  buf->byte_length -= bufel->msg.size - bufel->mark;
122
123
0
  bufel->next = bufel->prev = NULL;
124
125
0
  return ret;
126
0
}
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
0
{
134
0
  bufel->prev = NULL;
135
0
  bufel->next = buf->head;
136
137
0
  buf->length++;
138
0
  buf->byte_length += bufel->msg.size - bufel->mark;
139
140
0
  if (buf->head != NULL)
141
0
    buf->head->prev = bufel;
142
0
  else
143
0
    buf->tail = bufel;
144
0
  buf->head = bufel;
145
0
}
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
0
{
156
0
  mbuffer_st *bufel = buf->head;
157
158
0
  if (buf->head == NULL)
159
0
    return NULL;
160
161
0
  _mbuffer_dequeue(buf, bufel);
162
163
0
  return bufel;
164
0
}
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
0
{
174
0
  mbuffer_st *bufel = buf->head;
175
176
0
  if (msg) {
177
0
    if (bufel) {
178
0
      msg->data = bufel->msg.data + bufel->mark;
179
0
      msg->size = bufel->msg.size - bufel->mark;
180
0
    } else {
181
0
      msg->data = NULL;
182
0
      msg->size = 0;
183
0
    }
184
0
  }
185
0
  return bufel;
186
0
}
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
0
{
196
0
  mbuffer_st *bufel = cur->next;
197
198
0
  if (msg) {
199
0
    if (bufel) {
200
0
      msg->data = bufel->msg.data + bufel->mark;
201
0
      msg->size = bufel->msg.size - bufel->mark;
202
0
    } else {
203
0
      msg->data = NULL;
204
0
      msg->size = 0;
205
0
    }
206
0
  }
207
0
  return bufel;
208
0
}
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
0
{
219
0
  mbuffer_st *bufel = buf->head;
220
221
0
  if (!bufel)
222
0
    return;
223
224
0
  _mbuffer_dequeue(buf, bufel);
225
0
  gnutls_free(bufel);
226
0
}
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
0
{
240
0
  size_t left = bytes;
241
0
  mbuffer_st *bufel, *next;
242
0
  int ret = 0;
243
244
0
  if (bytes > buf->byte_length) {
245
0
    gnutls_assert();
246
0
    return GNUTLS_E_INVALID_REQUEST;
247
0
  }
248
249
0
  for (bufel = buf->head; bufel != NULL && left > 0; bufel = next) {
250
0
    next = bufel->next;
251
252
0
    if (left >= (bufel->msg.size - bufel->mark)) {
253
0
      left -= (bufel->msg.size - bufel->mark);
254
0
      remove_front(buf);
255
0
      ret = 1;
256
0
    } else {
257
0
      bufel->mark += left;
258
0
      buf->byte_length -= left;
259
0
      left = 0;
260
0
    }
261
0
  }
262
0
  return ret;
263
0
}
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
0
{
276
0
  mbuffer_st *st;
277
278
0
  st = gnutls_malloc(maximum_size + sizeof(mbuffer_st));
279
0
  if (st == NULL) {
280
0
    gnutls_assert();
281
0
    return NULL;
282
0
  }
283
284
  /* set the structure to zero */
285
0
  memset(st, 0, sizeof(*st));
286
287
  /* payload points after the mbuffer_st structure */
288
0
  st->msg.data = (uint8_t *) st + sizeof(mbuffer_st);
289
0
  st->msg.size = 0;
290
0
  st->maximum_size = maximum_size;
291
292
0
  return st;
293
0
}
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
0
{
307
0
  if (bufel->msg.size + newdata_size <= bufel->maximum_size) {
308
0
    memcpy(&bufel->msg.data[bufel->msg.size], newdata,
309
0
           newdata_size);
310
0
    bufel->msg.size += newdata_size;
311
0
  } else {
312
0
    gnutls_assert();
313
0
    return GNUTLS_E_INVALID_REQUEST;
314
0
  }
315
316
0
  return 0;
317
0
}
318
319
#ifdef ENABLE_ALIGN16
320
0
# 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
0
{
337
0
  mbuffer_st *st;
338
0
  size_t cur_alignment;
339
340
0
  st = gnutls_malloc(maximum_size + sizeof(mbuffer_st) + ALIGN_SIZE);
341
0
  if (st == NULL) {
342
0
    gnutls_assert();
343
0
    return NULL;
344
0
  }
345
346
  /* set the structure to zero */
347
0
  memset(st, 0, sizeof(*st));
348
349
  /* payload points after the mbuffer_st structure */
350
0
  st->msg.data = (uint8_t *) st + sizeof(mbuffer_st);
351
352
0
  cur_alignment = ((size_t)(st->msg.data + align_pos)) % ALIGN_SIZE;
353
0
  if (cur_alignment > 0)
354
0
    st->msg.data += ALIGN_SIZE - cur_alignment;
355
356
0
  st->msg.size = 0;
357
0
  st->maximum_size = maximum_size;
358
359
0
  return st;
360
0
}
361
362
static unsigned is_aligned16(mbuffer_st * bufel, unsigned align_pos)
363
0
{
364
0
  uint8_t *ptr = _mbuffer_get_udata_ptr(bufel);
365
366
0
  if (((size_t)(ptr + align_pos)) % ALIGN_SIZE == 0)
367
0
    return 1;
368
0
  else
369
0
    return 0;
370
0
}
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
0
{
382
0
  mbuffer_st *bufel, *cur;
383
0
  gnutls_datum_t msg;
384
0
  size_t pos = 0;
385
386
0
  if (buf->length == 0) {
387
    /* Nothing to do */
388
0
    return 0;
389
0
  }
390
391
0
  bufel = _mbuffer_head_get_first(buf, NULL);
392
0
  if (buf->length == 1 && is_aligned16(bufel, align_pos))
393
0
    return 0;
394
395
0
  bufel = _mbuffer_alloc_align16(buf->byte_length, align_pos);
396
0
  if (!bufel) {
397
0
    gnutls_assert();
398
0
    return GNUTLS_E_MEMORY_ERROR;
399
0
  }
400
401
0
  bufel->type = _mbuffer_head_get_first(buf, NULL)->type;
402
403
0
  for (cur = _mbuffer_head_get_first(buf, &msg);
404
0
       msg.data != NULL; cur = _mbuffer_head_get_next(cur, &msg)) {
405
0
    memcpy(&bufel->msg.data[pos], msg.data, msg.size);
406
0
    bufel->msg.size += msg.size;
407
0
    pos += msg.size;
408
0
  }
409
410
0
  _mbuffer_head_clear(buf);
411
0
  _mbuffer_enqueue(buf, bufel);
412
413
0
  return 0;
414
0
}
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);
436
       msg.data != NULL; 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