Coverage Report

Created: 2026-02-10 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/trafficserver/include/proxy/hdrs/HdrHeap.h
Line
Count
Source
1
/** @file
2
3
  A brief file description
4
5
  @section license License
6
7
  Licensed to the Apache Software Foundation (ASF) under one
8
  or more contributor license agreements.  See the NOTICE file
9
  distributed with this work for additional information
10
  regarding copyright ownership.  The ASF licenses this file
11
  to you under the Apache License, Version 2.0 (the
12
  "License"); you may not use this file except in compliance
13
  with the License.  You may obtain a copy of the License at
14
15
      http://www.apache.org/licenses/LICENSE-2.0
16
17
  Unless required by applicable law or agreed to in writing, software
18
  distributed under the License is distributed on an "AS IS" BASIS,
19
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
  See the License for the specific language governing permissions and
21
  limitations under the License.
22
 */
23
24
/****************************************************************************
25
26
   HdrHeap.h
27
28
   Description:
29
30
31
 ****************************************************************************/
32
33
#pragma once
34
35
#include "tscore/Ptr.h"
36
#include "tscore/ink_assert.h"
37
#include "swoc/Scalar.h"
38
#include "proxy/hdrs/HdrToken.h"
39
40
// Objects in the heap must currently be aligned to 8 byte boundaries,
41
// so their (address & HDR_PTR_ALIGNMENT_MASK) == 0
42
43
static constexpr size_t HDR_PTR_SIZE           = sizeof(uint64_t);
44
static constexpr size_t HDR_PTR_ALIGNMENT_MASK = HDR_PTR_SIZE - 1L;
45
using HdrHeapMarshalBlocks                     = swoc::Scalar<HDR_PTR_SIZE>;
46
47
// A many of the operations regarding read-only str
48
//  heaps are hand unrolled in the code.  Changing
49
//  this value requires a full pass through HdrBuf.cc
50
//  to fix the unrolled operations
51
static constexpr unsigned HDR_BUF_RONLY_HEAPS = 3;
52
53
class IOBufferBlock;
54
55
enum class HdrHeapObjType : uint8_t {
56
  EMPTY            = 0,
57
  RAW              = 1,
58
  URL              = 2,
59
  HTTP_HEADER      = 3,
60
  MIME_HEADER      = 4,
61
  FIELD_BLOCK      = 5,
62
  FIELD_STANDALONE = 6, // not a type that lives in HdrHeaps
63
  FIELD_SDK_HANDLE = 7, // not a type that lives in HdrHeaps
64
};
65
66
struct HdrHeapObjImpl {
67
  uint32_t m_type      : 8;
68
  uint32_t m_length    : 20;
69
  uint32_t m_obj_flags : 4;
70
};
71
72
/*-------------------------------------------------------------------------
73
  -------------------------------------------------------------------------*/
74
75
extern void obj_describe(HdrHeapObjImpl *obj, bool recurse);
76
77
inline int
78
obj_is_aligned(HdrHeapObjImpl *obj)
79
0
{
80
0
  return (((((uintptr_t)obj) & HDR_PTR_ALIGNMENT_MASK) == 0) && ((obj->m_length & HDR_PTR_ALIGNMENT_MASK) == 0));
81
0
}
82
83
inline void
84
obj_clear_data(HdrHeapObjImpl *obj)
85
0
{
86
0
  char *ptr        = (char *)obj;
87
0
  int   hdr_length = sizeof(HdrHeapObjImpl);
88
0
  memset(ptr + hdr_length, '\0', obj->m_length - hdr_length);
89
0
}
90
91
inline void
92
obj_copy_data(HdrHeapObjImpl *s_obj, HdrHeapObjImpl *d_obj)
93
0
{
94
0
  char *src, *dst;
95
96
0
  ink_assert((s_obj->m_length == d_obj->m_length) && (s_obj->m_type == d_obj->m_type));
97
98
0
  int hdr_length = sizeof(HdrHeapObjImpl);
99
0
  src            = (char *)s_obj + hdr_length;
100
0
  dst            = (char *)d_obj + hdr_length;
101
0
  memcpy(dst, src, d_obj->m_length - hdr_length);
102
0
}
103
104
inline void
105
obj_copy(HdrHeapObjImpl *s_obj, char *d_addr)
106
0
{
107
0
  memcpy(d_addr, (char *)s_obj, s_obj->m_length);
108
0
}
109
110
inline void
111
obj_init_header(HdrHeapObjImpl *obj, HdrHeapObjType type, uint32_t nbytes, uint32_t obj_flags)
112
0
{
113
0
  obj->m_type      = static_cast<uint32_t>(type);
114
0
  obj->m_length    = nbytes;
115
0
  obj->m_obj_flags = obj_flags;
116
0
}
117
118
/*-------------------------------------------------------------------------
119
  -------------------------------------------------------------------------*/
120
121
enum class HdrBufMagic : uint32_t { ALIVE = 0xabcdfeed, MARSHALED = 0xdcbafeed, DEAD = 0xabcddead, CORRUPT = 0xbadbadcc };
122
123
class HdrStrHeap : public RefCountObj
124
{
125
public:
126
  static constexpr int DEFAULT_SIZE = 2048;
127
128
  void free() override;
129
130
  char *allocate(int nbytes);
131
  char *expand(char *ptr, int old_size, int new_size);
132
  uint32_t
133
  space_avail() const
134
0
  {
135
0
    return _avail_size;
136
0
  }
137
  uint32_t
138
  total_size() const
139
0
  {
140
0
    return _total_size;
141
0
  }
142
143
  bool contains(const char *str) const;
144
145
  static HdrStrHeap *alloc(int heap_size);
146
147
private:
148
0
  HdrStrHeap(uint32_t total_size) : _total_size{total_size} {}
149
150
  uint32_t const _total_size;
151
  uint32_t       _avail_size;
152
};
153
154
inline bool
155
HdrStrHeap::contains(const char *str) const
156
0
{
157
0
  return reinterpret_cast<char const *>(this + 1) <= str && str < reinterpret_cast<char const *>(this) + _total_size;
158
0
}
159
160
struct StrHeapDesc {
161
  StrHeapDesc() = default;
162
163
  Ptr<RefCountObj> m_ref_count_ptr;
164
  char const      *m_heap_start = nullptr;
165
  int32_t          m_heap_len   = 0;
166
  bool             m_locked     = false;
167
168
  bool
169
  contains(const char *str) const
170
0
  {
171
0
    return (str >= m_heap_start && str < (m_heap_start + m_heap_len));
172
0
  }
173
};
174
175
class HdrHeap
176
{
177
public:
178
  static constexpr int DEFAULT_SIZE = 2048;
179
180
  void init();
181
  void destroy();
182
183
  // PtrHeap allocation
184
  HdrHeapObjImpl *allocate_obj(int nbytes, HdrHeapObjType type);
185
  void            deallocate_obj(HdrHeapObjImpl *obj);
186
187
  // StrHeap allocation
188
  char *allocate_str(int nbytes);
189
  char *expand_str(const char *old_str, int old_len, int new_len);
190
  char *duplicate_str(const char *str, int nbytes);
191
  void  free_string(const char *s, int len);
192
193
  // Marshalling
194
  int marshal_length();
195
  int marshal(char *buf, int length);
196
  int unmarshal(int buf_length, int obj_type, HdrHeapObjImpl **found_obj, RefCountObj *block_ref);
197
  /// Computes the valid data size of an unmarshalled instance.
198
  /// Callers should round up to HDR_PTR_SIZE to get the actual footprint.
199
  int unmarshal_size() const; // TBD - change this name, it's confusing.
200
  // One option - overload marshal_length to return this value if @a magic is HdrBufMagic::MARSHALED.
201
202
  void inherit_string_heaps(const HdrHeap *inherit_from);
203
  int  attach_block(IOBufferBlock *b, const char *use_start);
204
  void set_ronly_str_heap_end(int slot, const char *end);
205
206
  // Lock read only str heaps so that can't be moved around
207
  //  by a heap consolidation.  Does NOT lock for Multi-Threaded
208
  //  access!
209
  void
210
  lock_ronly_str_heap(unsigned i)
211
0
  {
212
0
    m_ronly_heap[i].m_locked = true;
213
0
  }
214
215
  void
216
  unlock_ronly_str_heap(unsigned i)
217
0
  {
218
0
    m_ronly_heap[i].m_locked = false;
219
0
    // INKqa11238
220
0
    // Move slot i to the first available slot in m_ronly_heap[].
221
0
    // The move is necessary because the rest of the code assumes
222
0
    // heaps are always allocated in order.
223
0
    for (unsigned j = 0; j < i; j++) {
224
0
      if (m_ronly_heap[j].m_heap_start == nullptr) {
225
0
        // move slot i to slot j
226
0
        m_ronly_heap[j].m_ref_count_ptr = m_ronly_heap[i].m_ref_count_ptr;
227
0
        m_ronly_heap[j].m_heap_start    = m_ronly_heap[i].m_heap_start;
228
0
        m_ronly_heap[j].m_heap_len      = m_ronly_heap[i].m_heap_len;
229
0
        m_ronly_heap[j].m_locked        = m_ronly_heap[i].m_locked;
230
0
        m_ronly_heap[i].m_ref_count_ptr = nullptr;
231
0
        m_ronly_heap[i].m_heap_start    = nullptr;
232
0
        m_ronly_heap[i].m_heap_len      = 0;
233
0
        m_ronly_heap[i].m_locked        = false;
234
0
        break; // Did the move, time to go.
235
0
      }
236
0
    }
237
0
  }
238
239
  // Working function to copy strings into a new heap
240
  // Unlike the HDR_MOVE_STR macro, this function will call
241
  // allocate_str which will update the new_heap to create more space
242
  // if there is not originally sufficient space
243
  inline std::string_view
244
  localize(const std::string_view &string)
245
0
  {
246
0
    auto length = string.length();
247
0
    if (length > 0) {
248
0
      char *new_str = this->allocate_str(length);
249
0
      if (new_str) {
250
0
        memcpy(new_str, string.data(), length);
251
0
      } else {
252
0
        length = 0;
253
0
      }
254
0
      return {new_str, length};
255
0
    }
256
0
    return {nullptr, 0};
257
0
  }
258
259
  // Sanity Check Functions
260
  void sanity_check_strs();
261
  bool check_marshalled(uint32_t buf_length);
262
263
  // Debugging functions
264
  void dump_heap(int len = -1);
265
266
  HdrBufMagic m_magic;
267
  char       *m_free_start;
268
  char       *m_data_start;
269
  uint32_t    m_size;
270
271
  bool m_writeable;
272
273
  // Overflow block ptr
274
  //   Overflow blocks are necessary because we can
275
  //     run out of space in the header heap and the
276
  //     heap is not rellocatable
277
  //   Overflow blocks have the HdrHeap full structure
278
  //    header on them, although only first block can
279
  //    point to string heaps
280
  HdrHeap *m_next;
281
282
  // HdrBuf heap pointers
283
  uint32_t m_free_size;
284
285
  int    demote_rw_str_heap();
286
  void   coalesce_str_heaps(int incoming_size = 0);
287
  void   evacuate_from_str_heaps(HdrStrHeap *new_heap);
288
  size_t required_space_for_evacuation();
289
  bool   attach_str_heap(char const *h_start, int h_len, RefCountObj *h_ref_obj, int *index);
290
291
  uint64_t total_used_size() const;
292
293
  /** Struct to prevent garbage collection on heaps.
294
      This bumps the reference count to the heap containing the pointer
295
      while the instance of this class exists. When it goes out of scope
296
      the reference is dropped. This is useful inside a method or block
297
      to keep the required heap data around until leaving the scope.
298
  */
299
  class HeapGuard
300
  {
301
  public:
302
    /// Construct the protection.
303
    HeapGuard(HdrHeap *heap, const char *str)
304
0
    {
305
0
      if (heap->m_read_write_heap && heap->m_read_write_heap->contains(str)) {
306
0
        _ptr = heap->m_read_write_heap.get();
307
0
      } else {
308
0
        for (auto &i : heap->m_ronly_heap) {
309
0
          if (i.contains(str)) {
310
0
            _ptr = i.m_ref_count_ptr;
311
0
            break;
312
0
          }
313
0
        }
314
0
      }
315
0
    }
316
317
    // There's no need to have a destructor here, the default dtor will take care of
318
    // releasing the (potentially) locked heap.
319
320
  private:
321
    /// The heap we protect (if any)
322
    Ptr<RefCountObj> _ptr;
323
  };
324
325
  // String Heap access
326
  Ptr<HdrStrHeap> m_read_write_heap;
327
  StrHeapDesc     m_ronly_heap[HDR_BUF_RONLY_HEAPS];
328
  int             m_lost_string_space;
329
};
330
331
static constexpr HdrHeapMarshalBlocks HDR_HEAP_HDR_SIZE{swoc::round_up(sizeof(HdrHeap))};
332
static constexpr size_t               HDR_MAX_ALLOC_SIZE = HdrHeap::DEFAULT_SIZE - HDR_HEAP_HDR_SIZE;
333
334
inline void
335
HdrHeap::free_string(const char *s, int len)
336
0
{
337
0
  if (s && len > 0) {
338
0
    m_lost_string_space += len;
339
0
  }
340
0
}
341
342
inline int
343
HdrHeap::unmarshal_size() const
344
0
{
345
0
  return m_size + m_ronly_heap[0].m_heap_len;
346
0
}
347
348
//
349
struct MarshalXlate {
350
  char const *start  = nullptr;
351
  char const *end    = nullptr;
352
  char const *offset = nullptr;
353
0
  MarshalXlate() {}
354
};
355
356
struct HeapCheck {
357
  char const *start;
358
  char const *end;
359
};
360
361
// Nasty macro to do string marshalling
362
#define HDR_MARSHAL_STR(ptr, table, nentries)                 \
363
0
  if (ptr) {                                                  \
364
0
    int found = 0;                                            \
365
0
    for (int i = 0; i < nentries; i++) {                      \
366
0
      if (ptr >= table[i].start && ptr <= table[i].end) {     \
367
0
        ptr   = (((char *)ptr) - (uintptr_t)table[i].offset); \
368
0
        found = 1;                                            \
369
0
        break;                                                \
370
0
      }                                                       \
371
0
    }                                                         \
372
0
    ink_assert(found);                                        \
373
0
    if (found == 0) {                                         \
374
0
      return -1;                                              \
375
0
    }                                                         \
376
0
  }
377
378
// Nasty macro to do string marshalling
379
#define HDR_MARSHAL_STR_1(ptr, table)                       \
380
0
  if (ptr) {                                                \
381
0
    int found = 0;                                          \
382
0
    if (ptr >= table[0].start && ptr <= table[0].end) {     \
383
0
      ptr   = (((char *)ptr) - (uintptr_t)table[0].offset); \
384
0
      found = 1;                                            \
385
0
    }                                                       \
386
0
    ink_assert(found);                                      \
387
0
    if (found == 0) {                                       \
388
0
      return -1;                                            \
389
0
    }                                                       \
390
0
  }
391
392
#define HDR_MARSHAL_PTR(ptr, type, table, nentries)                       \
393
0
  if (ptr) {                                                              \
394
0
    int found = 0;                                                        \
395
0
    for (int i = 0; i < nentries; i++) {                                  \
396
0
      if ((char *)ptr >= table[i].start && (char *)ptr <= table[i].end) { \
397
0
        ptr   = (type *)(((char *)ptr) - (uintptr_t)table[i].offset);     \
398
0
        found = 1;                                                        \
399
0
        break;                                                            \
400
0
      }                                                                   \
401
0
    }                                                                     \
402
0
    ink_assert(found);                                                    \
403
0
    if (found == 0) {                                                     \
404
0
      return -1;                                                          \
405
0
    }                                                                     \
406
0
  }
407
408
#define HDR_MARSHAL_PTR_1(ptr, type, table)                             \
409
0
  if (ptr) {                                                            \
410
0
    int found = 0;                                                      \
411
0
    if ((char *)ptr >= table[0].start && (char *)ptr <= table[0].end) { \
412
0
      ptr   = (type *)(((char *)ptr) - (uintptr_t)table[0].offset);     \
413
0
      found = 1;                                                        \
414
0
    }                                                                   \
415
0
    ink_assert(found);                                                  \
416
0
    if (found == 0) {                                                   \
417
0
      return -1;                                                        \
418
0
    }                                                                   \
419
0
  }
420
421
#define HDR_UNMARSHAL_STR(ptr, offset) \
422
0
  if (ptr) {                           \
423
0
    ptr = ((char *)ptr) + offset;      \
424
0
  }
425
426
#define HDR_UNMARSHAL_PTR(ptr, type, offset) \
427
0
  if (ptr) {                                 \
428
0
    ptr = (type *)(((char *)ptr) + offset);  \
429
0
  }
430
431
// Nasty macro to do string evacuation.  Assumes
432
//   new heap = new_heap
433
#define HDR_MOVE_STR(str, len)                 \
434
0
  {                                            \
435
0
    if (str) {                                 \
436
0
      char *new_str = new_heap->allocate(len); \
437
0
      if (new_str)                             \
438
0
        memcpy(new_str, str, len);             \
439
0
      str = new_str;                           \
440
0
    }                                          \
441
0
  }
442
443
// Nasty macro to do verify all strings it
444
//   in attached heaps
445
#define CHECK_STR(str, len, _heaps, _num_heaps)                    \
446
0
  {                                                                \
447
0
    if (str) {                                                     \
448
0
      int found = 0;                                               \
449
0
      for (int i = 0; i < _num_heaps; i++) {                       \
450
0
        if (str >= _heaps[i].start && str + len <= heaps[i].end) { \
451
0
          found = 1;                                               \
452
0
        }                                                          \
453
0
      }                                                            \
454
0
      ink_release_assert(found);                                   \
455
0
    }                                                              \
456
0
  }
457
458
// struct HdrHeapSDKHandle()
459
//
460
//   Handle to a HdrHeap.
461
//
462
//   Intended to be subclassed and contain a
463
//     object pointer that points into the heap
464
//
465
struct HdrHeapSDKHandle {
466
public:
467
0
  HdrHeapSDKHandle() {}
468
0
  ~HdrHeapSDKHandle() { clear(); }
469
  // clear() only deallocates chained SDK return values
470
  //   The underlying MBuffer is left untouched
471
  void clear();
472
473
  // destroy() frees the underlying MBuffer and deallocates all chained
474
  //    SDK return values
475
  void destroy();
476
477
  void        set(const HdrHeapSDKHandle *from);
478
  const char *make_sdk_string(const char *raw_str, int raw_str_len);
479
480
  HdrHeap *m_heap = nullptr;
481
482
  // In order to prevent gratitous refcounting,
483
  //  automatic C++ copies are disabled!
484
  HdrHeapSDKHandle(const HdrHeapSDKHandle &r)            = delete;
485
  HdrHeapSDKHandle &operator=(const HdrHeapSDKHandle &r) = delete;
486
};
487
488
inline void
489
HdrHeapSDKHandle::destroy()
490
0
{
491
0
  if (m_heap) {
492
0
    m_heap->destroy();
493
0
  }
494
0
  clear();
495
0
}
496
497
inline void
498
HdrHeapSDKHandle::clear()
499
0
{
500
0
  m_heap = nullptr;
501
0
}
502
503
inline void
504
HdrHeapSDKHandle::set(const HdrHeapSDKHandle *from)
505
0
{
506
0
  clear();
507
0
  m_heap = from->m_heap;
508
0
}
509
510
HdrHeap *new_HdrHeap(int size = HdrHeap::DEFAULT_SIZE);
511
512
void hdr_heap_test();