Coverage Report

Created: 2026-07-10 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/yara/libyara/arena.c
Line
Count
Source
1
/*
2
Copyright (c) 2020. The YARA Authors. All Rights Reserved.
3
4
Redistribution and use in source and binary forms, with or without modification,
5
are permitted provided that the following conditions are met:
6
7
1. Redistributions of source code must retain the above copyright notice, this
8
list of conditions and the following disclaimer.
9
10
2. Redistributions in binary form must reproduce the above copyright notice,
11
this list of conditions and the following disclaimer in the documentation and/or
12
other materials provided with the distribution.
13
14
3. Neither the name of the copyright holder nor the names of its contributors
15
may be used to endorse or promote products derived from this software without
16
specific prior written permission.
17
18
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
*/
29
30
#include <assert.h>
31
#include <stdarg.h>
32
#include <stddef.h>
33
#include <string.h>
34
#include <yara/arena.h>
35
#include <yara/error.h>
36
#include <yara/mem.h>
37
38
typedef struct YR_ARENA_FILE_HEADER YR_ARENA_FILE_HEADER;
39
typedef struct YR_ARENA_FILE_BUFFER YR_ARENA_FILE_BUFFER;
40
41
#pragma pack(push)
42
#pragma pack(1)
43
44
struct YR_ARENA_FILE_HEADER
45
{
46
  uint8_t magic[4];
47
  uint8_t version;
48
  uint8_t num_buffers;
49
};
50
51
struct YR_ARENA_FILE_BUFFER
52
{
53
  uint64_t offset;
54
  uint32_t size;
55
};
56
57
#pragma pack(pop)
58
59
////////////////////////////////////////////////////////////////////////////////
60
// Tells the arena that certain offsets within a buffer contain relocatable
61
// pointers. The offsets are passed as a vararg list where the end of the
62
// list is indicated by the special value EOL (-1), offsets in the list are
63
// relative to base_offset, which in turns is relative to the beginning of the
64
// buffer.
65
//
66
// Args:
67
//   arena: Pointer the arena.
68
//   buffer_id: Buffer number.
69
//   base_offset: Base offset.
70
//   offsets: List of offsets relative to base offset.
71
//
72
// Returns:
73
//   ERROR_SUCCESS
74
//   ERROR_INSUFFICIENT_MEMORY
75
//
76
static int _yr_arena_make_ptr_relocatable(
77
    YR_ARENA* arena,
78
    uint32_t buffer_id,
79
    yr_arena_off_t base_offset,
80
    va_list offsets)
81
107
{
82
107
  size_t offset;
83
84
107
  int result = ERROR_SUCCESS;
85
86
  // The argument to va_arg is size_t because the offsets passed to this
87
  // function are obtained with offsetof().
88
107
  offset = va_arg(offsets, size_t);
89
90
229
  while (offset != EOL)
91
122
  {
92
122
    YR_RELOC* reloc = (YR_RELOC*) yr_malloc(sizeof(YR_RELOC));
93
94
122
    if (reloc == NULL)
95
0
      return ERROR_INSUFFICIENT_MEMORY;
96
97
122
    reloc->buffer_id = buffer_id;
98
122
    reloc->offset = base_offset + (yr_arena_off_t) offset;
99
122
    reloc->next = NULL;
100
101
122
    if (arena->reloc_list_head == NULL)
102
9
      arena->reloc_list_head = reloc;
103
104
122
    if (arena->reloc_list_tail != NULL)
105
113
      arena->reloc_list_tail->next = reloc;
106
107
122
    arena->reloc_list_tail = reloc;
108
122
    offset = va_arg(offsets, size_t);
109
122
  }
110
111
107
  return result;
112
107
}
113
114
// Flags for _yr_arena_allocate_memory.
115
1.82k
#define YR_ARENA_ZERO_MEMORY 1
116
117
////////////////////////////////////////////////////////////////////////////////
118
// Allocates memory in a buffer within the arena.
119
//
120
// Args:
121
//   arena: Pointer to the arena.
122
//   flags: Flags. The only supported flag so far is YR_ARENA_ZERO_MEMORY.
123
//   buffer_id: Buffer number.
124
//   size : Size of the region to be allocated.
125
//   [out] ref: Pointer to a YR_ARENA_REF that will be updated with the
126
//              reference to the newly allocated region. The pointer can be
127
//              NULL.
128
// Returns:
129
//   ERROR_SUCCESS
130
//   ERROR_INVALID_ARGUMENT
131
//   ERROR_INSUFFICIENT_MEMORY
132
//
133
static int _yr_arena_allocate_memory(
134
    YR_ARENA* arena,
135
    int flags,
136
    uint32_t buffer_id,
137
    size_t size,
138
    YR_ARENA_REF* ref)
139
15.4k
{
140
15.4k
  if (buffer_id > arena->num_buffers)
141
0
    return ERROR_INVALID_ARGUMENT;
142
143
15.4k
  YR_ARENA_BUFFER* b = &arena->buffers[buffer_id];
144
145
  // If the new data doesn't fit in the remaining space the buffer must be
146
  // re-sized. This implies moving the buffer to a different memory location
147
  // and adjusting the pointers listed in the relocation list.
148
149
15.4k
  if (b->size - b->used < size)
150
1.78k
  {
151
1.78k
    size_t new_size = (b->size == 0) ? arena->initial_buffer_size : b->size * 2;
152
153
1.78k
    while (new_size < b->used + size) new_size *= 2;
154
155
    // Make sure that buffer size if not larger than 4GB.
156
1.78k
    if (new_size > 1ULL << 32)
157
0
      return ERROR_INSUFFICIENT_MEMORY;
158
159
1.78k
    uint8_t* new_data = yr_realloc(b->data, new_size);
160
161
1.78k
    if (new_data == NULL)
162
0
      return ERROR_INSUFFICIENT_MEMORY;
163
164
// When yr_realloc uses the Windows API (HeapAlloc, HeapReAlloc) it is not
165
// necessary to set the memory to zero because the API is called with the
166
// HEAP_ZERO_MEMORY flag.
167
1.78k
#if !defined(_WIN32) && !defined(__CYGWIN__)
168
1.78k
    if (flags & YR_ARENA_ZERO_MEMORY)
169
42
      memset(new_data + b->used, 0, new_size - b->used);
170
1.78k
#endif
171
172
    // Fix pointers in the relocation list if the old buffer was not empty
173
    // and it was actually moved by yr_realloc.
174
1.78k
    if (b->data != NULL && b->data != new_data)
175
0
    {
176
0
      YR_RELOC* reloc = arena->reloc_list_head;
177
178
0
      while (reloc != NULL)
179
0
      {
180
        // If the reloc entry is for the same buffer that is being relocated,
181
        // the base pointer that we use to access the buffer must be new_data
182
        // as arena->buffers[reloc->buffer_id].data (which is the same than
183
        // b->data) can't be accessed anymore after the call to yr_realloc.
184
0
        uint8_t* base = buffer_id == reloc->buffer_id
185
0
                            ? new_data
186
0
                            : arena->buffers[reloc->buffer_id].data;
187
188
        // reloc_target is the value of the relocatable pointer.
189
0
        void* reloc_target;
190
0
        memcpy(&reloc_target, base + reloc->offset, sizeof(reloc_target));
191
192
0
        if ((uint8_t*) reloc_target >= b->data &&
193
0
            (uint8_t*) reloc_target < b->data + b->used)
194
0
        {
195
          // reloc_target points to some data inside the buffer being moved, so
196
          // the pointer needs to be adjusted.
197
0
          void* new_target = (uint8_t*) reloc_target - b->data + new_data;
198
0
          memcpy(base + reloc->offset, &new_target, sizeof(new_target));
199
0
        }
200
201
0
        reloc = reloc->next;
202
0
      }
203
0
    }
204
205
1.78k
    b->size = new_size;
206
1.78k
    b->data = new_data;
207
1.78k
  }
208
209
15.4k
  if (ref != NULL)
210
15.4k
  {
211
15.4k
    ref->buffer_id = buffer_id;
212
15.4k
    ref->offset = (uint32_t) b->used;
213
15.4k
  }
214
215
15.4k
  b->used += size;
216
217
15.4k
  return ERROR_SUCCESS;
218
15.4k
}
219
220
////////////////////////////////////////////////////////////////////////////////
221
// Creates an arena with the specified number of buffers.
222
//
223
// Args:
224
//   num_buffers: Number of buffers.
225
//   initial_buffer_size: Initial size of each buffer.
226
//   [out] arena: Address of a YR_ARENA* pointer that will receive the address
227
//                of the newly created arena.
228
// Returns:
229
//   ERROR_SUCCESS
230
//   ERROR_INSUFFICIENT_MEMORY
231
//
232
int yr_arena_create(
233
    uint32_t num_buffers,
234
    size_t initial_buffer_size,
235
    YR_ARENA** arena)
236
16.5k
{
237
16.5k
  YR_ARENA* new_arena = (YR_ARENA*) yr_calloc(1, sizeof(YR_ARENA));
238
239
16.5k
  if (new_arena == NULL)
240
0
    return ERROR_INSUFFICIENT_MEMORY;
241
242
16.5k
  new_arena->xrefs = 1;
243
16.5k
  new_arena->num_buffers = num_buffers;
244
16.5k
  new_arena->initial_buffer_size = initial_buffer_size;
245
246
16.5k
  *arena = new_arena;
247
248
16.5k
  return ERROR_SUCCESS;
249
16.5k
}
250
251
void yr_arena_acquire(YR_ARENA* arena)
252
9
{
253
9
  arena->xrefs++;
254
9
}
255
256
////////////////////////////////////////////////////////////////////////////////
257
// Releases the arena.
258
//
259
// Decrements the cross-references counter for the arena, if the number of
260
// cross-references reaches zero the arena is destroyed and all its resources
261
// are freed.
262
//
263
// Args:
264
//   arena :Pointer to the arena.
265
//
266
// Returns:
267
//   ERROR_SUCCESS
268
//   ERROR_INSUFFICIENT_MEMORY
269
//
270
int yr_arena_release(YR_ARENA* arena)
271
16.5k
{
272
16.5k
  arena->xrefs--;
273
274
16.5k
  if (arena->xrefs > 0)
275
9
    return ERROR_SUCCESS;
276
277
33.1k
  for (uint32_t i = 0; i < arena->num_buffers; i++)
278
16.5k
  {
279
16.5k
    if (arena->buffers[i].data != NULL)
280
1.71k
      yr_free(arena->buffers[i].data);
281
16.5k
  }
282
283
16.5k
  YR_RELOC* reloc = arena->reloc_list_head;
284
285
16.5k
  while (reloc != NULL)
286
0
  {
287
0
    YR_RELOC* next = reloc->next;
288
0
    yr_free(reloc);
289
0
    reloc = next;
290
0
  }
291
292
16.5k
  yr_free(arena);
293
294
16.5k
  return ERROR_SUCCESS;
295
16.5k
}
296
297
////////////////////////////////////////////////////////////////////////////////
298
// Allocates memory in a buffer within the arena.
299
//
300
// Args:
301
//   arena: Pointer to the arena.
302
//   buffer_id: Buffer number.
303
//   size : Size of the region to be allocated.
304
//   [out] offset: Pointer to a variable where the function puts the offset
305
//                 within the buffer of the allocated region. The pointer can
306
//                 be NULL.
307
// Returns:
308
//   ERROR_SUCCESS
309
//   ERROR_INVALID_ARGUMENT
310
//   ERROR_INSUFFICIENT_MEMORY
311
//
312
int yr_arena_allocate_memory(
313
    YR_ARENA* arena,
314
    uint32_t buffer_id,
315
    size_t size,
316
    YR_ARENA_REF* ref)
317
0
{
318
0
  return _yr_arena_allocate_memory(arena, 0, buffer_id, size, ref);
319
0
}
320
321
////////////////////////////////////////////////////////////////////////////////
322
// Allocates memory in a buffer within the arena and fill it with zeroes.
323
//
324
// Args:
325
//   arena: Pointer to the arena.
326
//   buffer_id: Buffer number.
327
//   size : Size of the region to be allocated.
328
//   [out] offset: Pointer to a variable where the function puts the offset
329
//                 within the buffer of the allocated region. The pointer can
330
//                 be NULL.
331
// Returns:
332
//   ERROR_SUCCESS
333
//   ERROR_INVALID_ARGUMENT
334
//   ERROR_INSUFFICIENT_MEMORY
335
//
336
int yr_arena_allocate_zeroed_memory(
337
    YR_ARENA* arena,
338
    uint32_t buffer_id,
339
    size_t size,
340
    YR_ARENA_REF* ref)
341
18
{
342
18
  return _yr_arena_allocate_memory(
343
18
      arena, YR_ARENA_ZERO_MEMORY, buffer_id, size, ref);
344
18
}
345
346
////////////////////////////////////////////////////////////////////////////////
347
// Allocates a structure within the arena. This function is similar to
348
// yr_arena_allocate_memory but additionally receives a variable-length
349
// list of offsets within the structure where pointers reside. This allows
350
// the arena to keep track of pointers that must be adjusted when memory
351
// is relocated. This is an example on how to invoke this function:
352
//
353
//  yr_arena_allocate_struct(
354
//        arena,
355
//        0,
356
//        sizeof(MY_STRUCTURE),
357
//        &ref,
358
//        offsetof(MY_STRUCTURE, field_1),
359
//        offsetof(MY_STRUCTURE, field_2),
360
//        ..
361
//        offsetof(MY_STRUCTURE, field_N),
362
//        EOL);
363
//
364
// Args:
365
//   arena: Pointer to the arena.
366
//   buffer_id: Buffer number.
367
//   size: Size of the region to be allocated.
368
//   [out] ref: Pointer to a reference that will point to the newly allocated
369
//              structure when the function returns. The pointer can be NULL
370
//              if you don't need the reference.
371
//   ...   Variable number of offsets relative to the beginning of the struct.
372
//         These offsets are of type size_t.
373
//
374
// Returns:
375
//   ERROR_SUCCESS
376
//   ERROR_INVALID_ARGUMENT
377
//   ERROR_INSUFFICIENT_MEMORY
378
//
379
int yr_arena_allocate_struct(
380
    YR_ARENA* arena,
381
    uint32_t buffer_id,
382
    size_t size,
383
    YR_ARENA_REF* ref,
384
    ...)
385
24
{
386
24
  YR_ARENA_REF r;
387
388
24
  int result = _yr_arena_allocate_memory(
389
24
      arena, YR_ARENA_ZERO_MEMORY, buffer_id, size, &r);
390
391
24
  if (result != ERROR_SUCCESS)
392
0
    return result;
393
394
24
  va_list field_offsets;
395
24
  va_start(field_offsets, ref);
396
397
24
  result = _yr_arena_make_ptr_relocatable(
398
24
      arena, buffer_id, r.offset, field_offsets);
399
400
24
  va_end(field_offsets);
401
402
24
  if (result == ERROR_SUCCESS && ref != NULL)
403
24
  {
404
24
    ref->buffer_id = r.buffer_id;
405
24
    ref->offset = r.offset;
406
24
  }
407
408
24
  return result;
409
24
}
410
411
void* yr_arena_get_ptr(
412
    YR_ARENA* arena,
413
    uint32_t buffer_id,
414
    yr_arena_off_t offset)
415
16.8k
{
416
16.8k
  assert(buffer_id < arena->num_buffers);
417
16.8k
  assert(offset <= arena->buffers[buffer_id].used);
418
419
16.8k
  if (arena->buffers[buffer_id].data == NULL)
420
14.8k
    return NULL;
421
422
1.98k
  return arena->buffers[buffer_id].data + offset;
423
16.8k
}
424
425
yr_arena_off_t yr_arena_get_current_offset(YR_ARENA* arena, uint32_t buffer_id)
426
22
{
427
22
  assert(buffer_id < arena->num_buffers);
428
429
22
  return (yr_arena_off_t) arena->buffers[buffer_id].used;
430
22
}
431
432
int yr_arena_ptr_to_ref(YR_ARENA* arena, const void* address, YR_ARENA_REF* ref)
433
70.0k
{
434
70.0k
  *ref = YR_ARENA_NULL_REF;
435
436
70.0k
  if (address == NULL)
437
0
    return 1;
438
439
384k
  for (uint32_t i = 0; i < arena->num_buffers; ++i)
440
384k
  {
441
    // If the buffer is completetly empty, skip it.
442
384k
    if (arena->buffers[i].data == NULL)
443
122k
      continue;
444
445
    // If the address falls within the limits of the buffer, then we found
446
    // the buffer that contains the data and return a reference to it.
447
262k
    if ((uint8_t*) address >= arena->buffers[i].data &&
448
192k
        (uint8_t*) address < arena->buffers[i].data + arena->buffers[i].used)
449
70.0k
    {
450
70.0k
      ref->buffer_id = i;
451
70.0k
      ref->offset =
452
70.0k
          (yr_arena_off_t) ((uint8_t*) address - arena->buffers[i].data);
453
454
70.0k
      return 1;
455
70.0k
    }
456
262k
  }
457
458
0
  return 0;
459
70.0k
}
460
461
void* yr_arena_ref_to_ptr(YR_ARENA* arena, YR_ARENA_REF* ref)
462
180
{
463
180
  if (YR_ARENA_IS_NULL_REF(*ref))
464
18
    return NULL;
465
466
#if defined(__arm__)
467
  YR_ARENA_REF tmp_ref;
468
  memcpy(&tmp_ref, ref, sizeof(YR_ARENA_REF));
469
  ref = &tmp_ref;
470
#endif
471
472
162
  return yr_arena_get_ptr(arena, ref->buffer_id, ref->offset);
473
180
}
474
475
////////////////////////////////////////////////////////////////////////////////
476
// Tells the arena that certain addresses contains a relocatable pointer.
477
//
478
// Args:
479
//   arena: Pointer to the arena.
480
//   buffer_id: Buffer number.
481
//   ... : Variable number of size_t arguments with offsets within the buffer.
482
//
483
// Returns:
484
//    ERROR_SUCCESS if succeed or the corresponding error code otherwise.
485
//
486
int yr_arena_make_ptr_relocatable(YR_ARENA* arena, uint32_t buffer_id, ...)
487
83
{
488
83
  int result;
489
490
83
  va_list offsets;
491
83
  va_start(offsets, buffer_id);
492
493
83
  result = _yr_arena_make_ptr_relocatable(arena, buffer_id, 0, offsets);
494
495
83
  va_end(offsets);
496
497
83
  return result;
498
83
}
499
500
int yr_arena_write_data(
501
    YR_ARENA* arena,
502
    uint32_t buffer_id,
503
    const void* data,
504
    size_t size,
505
    YR_ARENA_REF* ref)
506
15.4k
{
507
15.4k
  YR_ARENA_REF r;
508
509
  // Allocate space in the buffer.
510
15.4k
  FAIL_ON_ERROR(_yr_arena_allocate_memory(arena, 0, buffer_id, size, &r));
511
512
  // Copy the data into the allocated space.
513
15.4k
  memcpy(arena->buffers[buffer_id].data + r.offset, data, size);
514
515
15.4k
  if (ref != NULL)
516
166
  {
517
166
    ref->buffer_id = r.buffer_id;
518
166
    ref->offset = r.offset;
519
166
  }
520
521
15.4k
  return ERROR_SUCCESS;
522
15.4k
}
523
524
int yr_arena_write_string(
525
    YR_ARENA* arena,
526
    uint32_t buffer_id,
527
    const char* string,
528
    YR_ARENA_REF* ref)
529
0
{
530
0
  return yr_arena_write_data(arena, buffer_id, string, strlen(string) + 1, ref);
531
0
}
532
533
int yr_arena_write_uint32(
534
    YR_ARENA* arena,
535
    uint32_t buffer_id,
536
    uint32_t integer,
537
    YR_ARENA_REF* ref)
538
0
{
539
0
  return yr_arena_write_data(arena, buffer_id, &integer, sizeof(integer), ref);
540
0
}
541
542
int yr_arena_load_stream(YR_STREAM* stream, YR_ARENA** arena)
543
0
{
544
0
  YR_ARENA_FILE_HEADER hdr;
545
546
0
  if (yr_stream_read(&hdr, sizeof(hdr), 1, stream) != 1)
547
0
    return ERROR_INVALID_FILE;
548
549
0
  if (hdr.magic[0] != 'Y' || hdr.magic[1] != 'A' || hdr.magic[2] != 'R' ||
550
0
      hdr.magic[3] != 'A')
551
0
  {
552
0
    return ERROR_INVALID_FILE;
553
0
  }
554
555
0
  if (hdr.version != YR_ARENA_FILE_VERSION)
556
0
    return ERROR_UNSUPPORTED_FILE_VERSION;
557
558
0
  if (hdr.num_buffers > YR_MAX_ARENA_BUFFERS)
559
0
    return ERROR_INVALID_FILE;
560
561
0
  YR_ARENA_FILE_BUFFER buffers[YR_MAX_ARENA_BUFFERS];
562
563
0
  size_t read = yr_stream_read(
564
0
      buffers, sizeof(buffers[0]), hdr.num_buffers, stream);
565
566
0
  if (read != hdr.num_buffers)
567
0
    return ERROR_CORRUPT_FILE;
568
569
0
  YR_ARENA* new_arena;
570
571
0
  FAIL_ON_ERROR(yr_arena_create(hdr.num_buffers, 10485, &new_arena))
572
573
0
  for (int i = 0; i < hdr.num_buffers; ++i)
574
0
  {
575
0
    if (buffers[i].size == 0)
576
0
      continue;
577
578
0
    YR_ARENA_REF ref;
579
580
0
    FAIL_ON_ERROR_WITH_CLEANUP(
581
0
        yr_arena_allocate_memory(new_arena, i, buffers[i].size, &ref),
582
0
        yr_arena_release(new_arena))
583
584
0
    void* ptr = yr_arena_get_ptr(new_arena, i, ref.offset);
585
586
0
    if (yr_stream_read(ptr, buffers[i].size, 1, stream) != 1)
587
0
    {
588
0
      yr_arena_release(new_arena);
589
0
      return ERROR_CORRUPT_FILE;
590
0
    }
591
0
  }
592
593
0
  YR_ARENA_REF reloc_ref;
594
595
0
  while (yr_stream_read(&reloc_ref, sizeof(reloc_ref), 1, stream) == 1)
596
0
  {
597
0
    if (reloc_ref.buffer_id >= new_arena->num_buffers)
598
0
    {
599
0
      yr_arena_release(new_arena);
600
0
      return ERROR_CORRUPT_FILE;
601
0
    }
602
603
0
    YR_ARENA_BUFFER* b = &new_arena->buffers[reloc_ref.buffer_id];
604
605
0
    if (b->data == NULL ||
606
0
        b->used < sizeof(YR_ARENA_REF) ||
607
0
        b->used > b->size ||
608
0
        reloc_ref.offset > b->used - sizeof(YR_ARENA_REF))
609
0
    {
610
0
      yr_arena_release(new_arena);
611
0
      return ERROR_CORRUPT_FILE;
612
0
    }
613
614
0
    YR_ARENA_REF ref;
615
616
0
    memcpy(&ref, b->data + reloc_ref.offset, sizeof(ref));
617
618
    // ref is the relocation target read from the loaded buffer, so its
619
    // buffer_id and offset come straight from the stream. yr_arena_get_ptr
620
    // only guards them with assert, which is a no-op under NDEBUG and would
621
    // index past the buffers array, so reject an out-of-range target here.
622
0
    if (!YR_ARENA_IS_NULL_REF(ref) &&
623
0
        (ref.buffer_id >= new_arena->num_buffers ||
624
0
         ref.offset > new_arena->buffers[ref.buffer_id].used))
625
0
    {
626
0
      yr_arena_release(new_arena);
627
0
      return ERROR_CORRUPT_FILE;
628
0
    }
629
630
0
    void* reloc_ptr = yr_arena_ref_to_ptr(new_arena, &ref);
631
632
0
    memcpy(b->data + reloc_ref.offset, &reloc_ptr, sizeof(reloc_ptr));
633
634
0
    FAIL_ON_ERROR_WITH_CLEANUP(
635
0
        yr_arena_make_ptr_relocatable(
636
0
            new_arena, reloc_ref.buffer_id, reloc_ref.offset, EOL),
637
0
        yr_arena_release(new_arena))
638
0
  }
639
640
0
  *arena = new_arena;
641
642
0
  return ERROR_SUCCESS;
643
0
}
644
645
int yr_arena_save_stream(YR_ARENA* arena, YR_STREAM* stream)
646
0
{
647
0
  YR_ARENA_FILE_HEADER hdr;
648
649
0
  hdr.magic[0] = 'Y';
650
0
  hdr.magic[1] = 'A';
651
0
  hdr.magic[2] = 'R';
652
0
  hdr.magic[3] = 'A';
653
654
0
  hdr.version = YR_ARENA_FILE_VERSION;
655
0
  hdr.num_buffers = arena->num_buffers;
656
657
0
  if (yr_stream_write(&hdr, sizeof(hdr), 1, stream) != 1)
658
0
    return ERROR_WRITING_FILE;
659
660
  // The first buffer in the file is after the header and the buffer table,
661
  // calculate its offset accordingly.
662
0
  uint64_t offset = sizeof(YR_ARENA_FILE_HEADER) +
663
0
                    sizeof(YR_ARENA_FILE_BUFFER) * arena->num_buffers;
664
665
0
  for (uint32_t i = 0; i < arena->num_buffers; ++i)
666
0
  {
667
0
    YR_ARENA_FILE_BUFFER buffer = {
668
0
        .offset = offset,
669
0
        .size = (uint32_t) arena->buffers[i].used,
670
0
    };
671
672
0
    if (yr_stream_write(&buffer, sizeof(buffer), 1, stream) != 1)
673
0
      return ERROR_WRITING_FILE;
674
675
0
    offset += buffer.size;
676
0
  }
677
678
  // Iterate the relocation list and replace all the relocatable pointers by
679
  // references to the buffer and offset where they are pointing to. All
680
  // relocatable pointers are expected to be null or point to data stored in
681
  // some of the arena's buffers. If a relocatable pointer points outside the
682
  // arena that's an error.
683
0
  YR_RELOC* reloc = arena->reloc_list_head;
684
685
0
  while (reloc != NULL)
686
0
  {
687
    // reloc_ptr is the pointer that will be replaced by the reference.
688
0
    void* reloc_ptr;
689
690
    // Move the pointer from the buffer to reloc_ptr.
691
0
    memcpy(
692
0
        &reloc_ptr,
693
0
        arena->buffers[reloc->buffer_id].data + reloc->offset,
694
0
        sizeof(reloc_ptr));
695
696
0
    YR_ARENA_REF ref;
697
698
0
#if !defined(NDEBUG)
699
0
    int found = yr_arena_ptr_to_ref(arena, reloc_ptr, &ref);
700
    // yr_arena_ptr_to_ref returns 0 if the relocatable pointer is pointing
701
    // outside the arena, this should not happen.
702
0
    assert(found);
703
#else
704
    yr_arena_ptr_to_ref(arena, reloc_ptr, &ref);
705
#endif
706
707
    // Replace the relocatable pointer with a reference that holds information
708
    // about the buffer and offset where the relocatable pointer is pointing to.
709
0
    memcpy(
710
0
        arena->buffers[reloc->buffer_id].data + reloc->offset,
711
0
        &ref,
712
0
        sizeof(ref));
713
714
0
    reloc = reloc->next;
715
0
  }
716
717
  // Now that all relocatable pointers are converted to references, write the
718
  // buffers.
719
0
  for (uint32_t i = 0; i < arena->num_buffers; ++i)
720
0
  {
721
0
    YR_ARENA_BUFFER* b = &arena->buffers[i];
722
723
0
    if (b->used > 0)
724
0
      if (yr_stream_write(b->data, b->used, 1, stream) != 1)
725
0
        return ERROR_WRITING_FILE;
726
0
  }
727
728
  // Write the relocation list and restore the pointers back.
729
0
  reloc = arena->reloc_list_head;
730
731
0
  while (reloc != NULL)
732
0
  {
733
0
    YR_ARENA_REF ref = {
734
0
        .buffer_id = reloc->buffer_id,
735
0
        .offset = reloc->offset,
736
0
    };
737
738
    // Write the relocation entry, which consists in a reference to the place
739
    // where the pointer that needs to be relocated is stored.
740
0
    if (yr_stream_write(&ref, sizeof(ref), 1, stream) != 1)
741
0
      return ERROR_WRITING_FILE;
742
743
    // Move the reference that is going to be replaced by the corresponding
744
    // pointer to the ref variable. Notice that ref is being reused for a
745
    // different reference here.
746
0
    memcpy(
747
0
        &ref,
748
0
        arena->buffers[reloc->buffer_id].data + reloc->offset,
749
0
        sizeof(ref));
750
751
    // Convert the reference to its corresponding pointer.
752
0
    void* reloc_ptr = yr_arena_ref_to_ptr(arena, &ref);
753
754
    // Copy the pointer back to where the reference was stored.
755
0
    memcpy(
756
0
        arena->buffers[reloc->buffer_id].data + reloc->offset,
757
0
        &reloc_ptr,
758
0
        sizeof(reloc_ptr));
759
760
0
    reloc = reloc->next;
761
0
  }
762
763
0
  return ERROR_SUCCESS;
764
0
}