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    HdrBuf.cc
27 
28    Description:
29 
30 
31  ****************************************************************************/
32 
33 #include "tscore/ink_platform.h"
34 #include "HdrHeap.h"
35 #include "URL.h"
36 #include "MIME.h"
37 #include "HTTP.h"
38 #include "I_EventSystem.h"
39 
40 static constexpr size_t MAX_LOST_STR_SPACE        = 1024;
41 static constexpr uint32_t MAX_HDR_HEAP_OBJ_LENGTH = (1 << 20) - 1; ///< m_length is 20 bit
42 
43 Allocator hdrHeapAllocator("hdrHeap", HdrHeap::DEFAULT_SIZE);
44 Allocator strHeapAllocator("hdrStrHeap", HdrStrHeap::DEFAULT_SIZE);
45 
46 /*-------------------------------------------------------------------------
47   -------------------------------------------------------------------------*/
48 
49 void
obj_describe(HdrHeapObjImpl * obj,bool recurse)50 obj_describe(HdrHeapObjImpl *obj, bool recurse)
51 {
52   static const char *obj_names[] = {"EMPTY", "RAW", "URL", "HTTP_HEADER", "MIME_HEADER", "FIELD_BLOCK"};
53 
54   Debug("http", "%s %p: [T: %d, L: %4d, OBJFLAGS: %X]  ", obj_names[obj->m_type], obj, obj->m_type, obj->m_length,
55         obj->m_obj_flags);
56 
57   extern void url_describe(HdrHeapObjImpl * obj, bool recurse);
58   extern void http_hdr_describe(HdrHeapObjImpl * obj, bool recurse);
59   extern void mime_hdr_describe(HdrHeapObjImpl * obj, bool recurse);
60   extern void mime_field_block_describe(HdrHeapObjImpl * obj, bool recurse);
61 
62   switch (obj->m_type) {
63   case HDR_HEAP_OBJ_EMPTY:
64     break;
65   case HDR_HEAP_OBJ_RAW:
66     break;
67   case HDR_HEAP_OBJ_MIME_HEADER:
68     mime_hdr_describe(obj, recurse);
69     break;
70   case HDR_HEAP_OBJ_FIELD_BLOCK:
71     mime_field_block_describe(obj, recurse);
72     break;
73   case HDR_HEAP_OBJ_HTTP_HEADER:
74     http_hdr_describe(obj, recurse);
75     break;
76   case HDR_HEAP_OBJ_URL:
77     url_describe(obj, recurse);
78     break;
79   default:
80     break;
81   }
82 }
83 
84 /*-------------------------------------------------------------------------
85   -------------------------------------------------------------------------*/
86 
87 inline void
init()88 HdrHeap::init()
89 {
90   m_data_start = m_free_start = (reinterpret_cast<char *>(this)) + HDR_HEAP_HDR_SIZE;
91   m_magic                     = HDR_BUF_MAGIC_ALIVE;
92   m_writeable                 = true;
93 
94   m_next      = nullptr;
95   m_free_size = m_size - HDR_HEAP_HDR_SIZE;
96 
97   // We need to clear m_ptr directly since it's garbage and
98   //  using the operator functions will to free() what ever
99   //  garbage it is pointing to
100   m_read_write_heap.detach();
101 
102   for (auto &i : m_ronly_heap) {
103     i.m_heap_start = nullptr;
104     i.m_ref_count_ptr.detach();
105     i.m_locked   = false;
106     i.m_heap_len = 0;
107   }
108   m_lost_string_space = 0;
109 
110   ink_assert(m_free_size > 0);
111 }
112 
113 HdrHeap *
new_HdrHeap(int size)114 new_HdrHeap(int size)
115 {
116   HdrHeap *h;
117   if (size <= HdrHeap::DEFAULT_SIZE) {
118     size = HdrHeap::DEFAULT_SIZE;
119     h    = static_cast<HdrHeap *>(THREAD_ALLOC(hdrHeapAllocator, this_ethread()));
120   } else {
121     h = static_cast<HdrHeap *>(ats_malloc(size));
122   }
123 
124   h->m_size = size;
125   h->init();
126 
127   return h;
128 }
129 
130 HdrStrHeap *
new_HdrStrHeap(int requested_size)131 new_HdrStrHeap(int requested_size)
132 {
133   // The callee is asking for a string heap to be created
134   //  that can allocate at least size bytes.  As such we,
135   //  need to include the size of the string heap header in
136   //  our calculations
137 
138   int alloc_size = requested_size + sizeof(HdrStrHeap);
139 
140   HdrStrHeap *sh;
141   if (alloc_size <= HdrStrHeap::DEFAULT_SIZE) {
142     alloc_size = HdrStrHeap::DEFAULT_SIZE;
143     sh         = static_cast<HdrStrHeap *>(THREAD_ALLOC(strHeapAllocator, this_ethread()));
144   } else {
145     alloc_size = ts::round_up<HdrStrHeap::DEFAULT_SIZE * 2>(alloc_size);
146     sh         = static_cast<HdrStrHeap *>(ats_malloc(alloc_size));
147   }
148 
149   //    Debug("hdrs", "Allocated string heap in size %d", alloc_size);
150 
151   // Placement new the HdrStrHeap.
152   sh = new (sh) HdrStrHeap();
153 
154   sh->m_heap_size  = alloc_size;
155   sh->m_free_size  = alloc_size - sizeof(HdrStrHeap);
156   sh->m_free_start = reinterpret_cast<char *>(sh + 1);
157 
158   ink_assert(sh->refcount() == 0);
159 
160   ink_assert(sh->m_free_size > 0);
161 
162   return sh;
163 }
164 
165 void
destroy()166 HdrHeap::destroy()
167 {
168   if (m_next) {
169     m_next->destroy();
170   }
171 
172   m_read_write_heap = nullptr;
173   for (auto &i : m_ronly_heap) {
174     i.m_ref_count_ptr = nullptr;
175   }
176 
177   if (m_size == HdrHeap::DEFAULT_SIZE) {
178     THREAD_FREE(this, hdrHeapAllocator, this_thread());
179   } else {
180     ats_free(this);
181   }
182 }
183 
184 HdrHeapObjImpl *
allocate_obj(int nbytes,int type)185 HdrHeap::allocate_obj(int nbytes, int type)
186 {
187   char *new_space;
188   HdrHeapObjImpl *obj;
189 
190   ink_assert(m_writeable);
191 
192   nbytes = HdrHeapMarshalBlocks{ts::round_up(nbytes)};
193 
194   if (nbytes > static_cast<int>(HDR_MAX_ALLOC_SIZE)) {
195     ink_assert(!"alloc too big");
196     return nullptr;
197   }
198 
199   HdrHeap *h = this;
200 
201   while (true) {
202     if (static_cast<unsigned>(nbytes) <= (h->m_free_size)) {
203       new_space = h->m_free_start;
204       h->m_free_start += nbytes;
205       h->m_free_size -= nbytes;
206 
207       obj = reinterpret_cast<HdrHeapObjImpl *>(new_space);
208       obj_init_header(obj, type, nbytes, 0);
209       ink_assert(obj_is_aligned(obj));
210 
211       return obj;
212     }
213 
214     if (h->m_next == nullptr) {
215       // Allocate our next pointer heap
216       //   twice as large as this one so
217       //   number of pointer heaps is O(log n)
218       //   with regard to number of bytes allocated
219       h->m_next = new_HdrHeap(h->m_size * 2);
220     }
221 
222     h = h->m_next;
223   }
224 }
225 
226 void
deallocate_obj(HdrHeapObjImpl * obj)227 HdrHeap::deallocate_obj(HdrHeapObjImpl *obj)
228 {
229   ink_assert(m_writeable);
230   obj->m_type = HDR_HEAP_OBJ_EMPTY;
231 }
232 
233 char *
allocate_str(int nbytes)234 HdrHeap::allocate_str(int nbytes)
235 {
236   int last_size   = 0;
237   char *new_space = nullptr;
238   ink_assert(m_writeable);
239 
240   // INKqa08287 - We could get infinite build up
241   //   of dead strings on header merge.  To prevent
242   //   this we keep track of the dead string space
243   //   and force a heap coalesce if it is too large.
244   //   Ideally this should be done on free_string()
245   //   but I already no that this code path is
246   //   safe for forcing a str coalesce so I'm doing
247   //   it here for sanity's sake
248   if (m_lost_string_space > static_cast<int>(MAX_LOST_STR_SPACE)) {
249     goto FAILED;
250   }
251 
252 RETRY:
253   // First check to see if we have a read/write
254   //   string heap
255   if (!m_read_write_heap) {
256     int next_size     = (last_size * 2) - sizeof(HdrStrHeap);
257     next_size         = next_size > nbytes ? next_size : nbytes;
258     m_read_write_heap = new_HdrStrHeap(next_size);
259   }
260   // Try to allocate of our read/write string heap
261   new_space = m_read_write_heap->allocate(nbytes);
262 
263   if (new_space) {
264     return new_space;
265   }
266 
267   last_size = m_read_write_heap->m_heap_size;
268 
269   // Our existing rw str heap doesn't have sufficient
270   //  capacity.  We need to move the current rw heap
271   //  out of the way and create a new one
272   if (demote_rw_str_heap() == 0) {
273     goto RETRY;
274   }
275 
276 FAILED:
277   // We failed to demote.  We'll have to coalesce
278   //  the heaps
279   coalesce_str_heaps();
280   goto RETRY;
281 }
282 
283 // char* HdrHeap::expand_str(const char* old_str, int old_len, int new_len)
284 //
285 //   Attempt to grow an already allocated string.  For this to work,
286 //      the string  has to be the last one in the read-write string
287 //      heap and there has to be enough space that string heap
288 //   If expansion succeeds, we return old_str.  If it fails, we
289 //      return NULL
290 //
291 char *
expand_str(const char * old_str,int old_len,int new_len)292 HdrHeap::expand_str(const char *old_str, int old_len, int new_len)
293 {
294   if (m_read_write_heap && m_read_write_heap->contains(old_str)) {
295     return m_read_write_heap->expand(const_cast<char *>(old_str), old_len, new_len);
296   }
297 
298   return nullptr;
299 }
300 
301 // char* HdrHeap::duplicate_str(char* str, int nbytes)
302 //
303 //  Allocates a new string and copies the old data.
304 //  Returns the new string pointer.
305 //
306 char *
duplicate_str(const char * str,int nbytes)307 HdrHeap::duplicate_str(const char *str, int nbytes)
308 {
309   HeapGuard guard(this, str); // Don't let the source get de-allocated.
310   char *new_str = allocate_str(nbytes);
311 
312   memcpy(new_str, str, nbytes);
313   return (new_str);
314 }
315 
316 // int HdrHeap::demote_rw_str_heap()
317 //
318 //  Returns 0 on success and non-zero failure
319 //   Failure means all the read only heap slots
320 //   were full
321 //
322 int
demote_rw_str_heap()323 HdrHeap::demote_rw_str_heap()
324 {
325   // First, see if we have any open slots for read
326   //  only heaps
327   for (auto &i : m_ronly_heap) {
328     if (i.m_heap_start == nullptr) {
329       // We've found a slot
330       i.m_ref_count_ptr = m_read_write_heap.object();
331       i.m_heap_start    = reinterpret_cast<char *>(m_read_write_heap.get());
332       i.m_heap_len      = m_read_write_heap->m_heap_size - m_read_write_heap->m_free_size;
333 
334       //          Debug("hdrs", "Demoted rw heap of %d size", m_read_write_heap->m_heap_size);
335       m_read_write_heap = nullptr;
336       return 0;
337     }
338   }
339 
340   // No open slots
341   return 1;
342 }
343 
344 // void HdrHeap::coalesce_heaps()
345 //
346 //    Take existing stringheaps and combine them to free up
347 //      slots in the heap array
348 //
349 //  FIX ME: Should we combine a subset of the heaps
350 //     or all of them?  Current plan is combine all heaps
351 //     since saves doing bounds checks every string.  At
352 //     expense of doing far more copying
353 //
354 void
coalesce_str_heaps(int incoming_size)355 HdrHeap::coalesce_str_heaps(int incoming_size)
356 {
357   int new_heap_size = incoming_size;
358   ink_assert(incoming_size >= 0);
359   ink_assert(m_writeable);
360 
361   new_heap_size += required_space_for_evacuation();
362 
363   HdrStrHeap *new_heap = new_HdrStrHeap(new_heap_size);
364   evacuate_from_str_heaps(new_heap);
365   m_lost_string_space = 0;
366 
367   // At this point none of the currently used string
368   //  heaps are needed since everything is in the
369   //  new string heap.  So deallocate all the old heaps
370   m_read_write_heap = new_heap;
371 
372   int heaps_removed = 0;
373   for (auto &j : m_ronly_heap) {
374     if (j.m_heap_start != nullptr && j.m_locked == false) {
375       j.m_ref_count_ptr = nullptr;
376       j.m_heap_start    = nullptr;
377       j.m_heap_len      = 0;
378       heaps_removed++;
379     }
380   }
381 
382   // This function is presumed to free up read only
383   //   string heap slots or be for incoming heaps
384   //   If we don't have any free heaps, we are screwed
385   ink_assert(heaps_removed > 0 || incoming_size > 0 || m_ronly_heap[0].m_heap_start == nullptr);
386 }
387 
388 void
evacuate_from_str_heaps(HdrStrHeap * new_heap)389 HdrHeap::evacuate_from_str_heaps(HdrStrHeap *new_heap)
390 {
391   //    printf("Str Evac\n");
392   // Loop over the objects in heap and call the evacuation
393   //  function in each one
394   HdrHeap *h = this;
395   ink_assert(m_writeable);
396 
397   while (h) {
398     char *data = h->m_data_start;
399 
400     while (data < h->m_free_start) {
401       HdrHeapObjImpl *obj = reinterpret_cast<HdrHeapObjImpl *>(data);
402 
403       // Object length cannot be 0 by design, otherwise something is wrong + infinite loop here!
404       ink_release_assert(0 != obj->m_length);
405 
406       switch (obj->m_type) {
407       case HDR_HEAP_OBJ_URL:
408         ((URLImpl *)obj)->move_strings(new_heap);
409         break;
410       case HDR_HEAP_OBJ_HTTP_HEADER:
411         ((HTTPHdrImpl *)obj)->move_strings(new_heap);
412         break;
413       case HDR_HEAP_OBJ_MIME_HEADER:
414         ((MIMEHdrImpl *)obj)->move_strings(new_heap);
415         break;
416       case HDR_HEAP_OBJ_FIELD_BLOCK:
417         ((MIMEFieldBlockImpl *)obj)->move_strings(new_heap);
418         break;
419       case HDR_HEAP_OBJ_EMPTY:
420       case HDR_HEAP_OBJ_RAW:
421         // Nothing to do
422         break;
423       default:
424         ink_release_assert(0);
425       }
426 
427       data = data + obj->m_length;
428     }
429 
430     h = h->m_next;
431   }
432 }
433 
434 size_t
required_space_for_evacuation()435 HdrHeap::required_space_for_evacuation()
436 {
437   size_t ret = 0;
438   HdrHeap *h = this;
439   while (h) {
440     char *data               = h->m_data_start;
441     HdrHeapObjImpl *prev_obj = nullptr;
442 
443     while (data < h->m_free_start) {
444       HdrHeapObjImpl *obj = reinterpret_cast<HdrHeapObjImpl *>(data);
445 
446       // Object length cannot be 0 by design, otherwise something is wrong + infinite loop here!
447       ink_release_assert(0 != obj->m_length);
448 
449       switch (obj->m_type) {
450       case HDR_HEAP_OBJ_URL:
451         ret += ((URLImpl *)obj)->strings_length();
452         break;
453       case HDR_HEAP_OBJ_HTTP_HEADER:
454         ret += ((HTTPHdrImpl *)obj)->strings_length();
455         break;
456       case HDR_HEAP_OBJ_MIME_HEADER:
457         ret += ((MIMEHdrImpl *)obj)->strings_length();
458         break;
459       case HDR_HEAP_OBJ_FIELD_BLOCK:
460         ret += ((MIMEFieldBlockImpl *)obj)->strings_length();
461         break;
462       case HDR_HEAP_OBJ_EMPTY:
463       case HDR_HEAP_OBJ_RAW:
464         // Nothing to do
465         break;
466       default:
467         ink_release_assert(0);
468       }
469 
470       // coalesce empty objects next to each other
471       if (obj->m_type == HDR_HEAP_OBJ_EMPTY) {
472         if (prev_obj != nullptr && prev_obj->m_length < (MAX_HDR_HEAP_OBJ_LENGTH - obj->m_length)) {
473           prev_obj->m_length += obj->m_length;
474           ink_release_assert(prev_obj->m_length > 0);
475         } else {
476           prev_obj = obj;
477         }
478       } else {
479         prev_obj = nullptr;
480       }
481 
482       data = data + obj->m_length;
483     }
484     h = h->m_next;
485   }
486   return ret;
487 }
488 
489 void
sanity_check_strs()490 HdrHeap::sanity_check_strs()
491 {
492   int num_heaps = 0;
493   struct HeapCheck heaps[HDR_BUF_RONLY_HEAPS + 1];
494 
495   // Build up a string check table
496   if (m_read_write_heap) {
497     heaps[num_heaps].start = (reinterpret_cast<char *>(m_read_write_heap.get())) + sizeof(HdrStrHeap);
498 
499     int heap_size = m_read_write_heap->m_heap_size - (sizeof(HdrStrHeap) + m_read_write_heap->m_free_size);
500 
501     heaps[num_heaps].end = heaps[num_heaps].start + heap_size;
502     num_heaps++;
503   }
504 
505   for (auto &i : m_ronly_heap) {
506     if (i.m_heap_start != nullptr) {
507       heaps[num_heaps].start = i.m_heap_start;
508       heaps[num_heaps].end   = i.m_heap_start + i.m_heap_len;
509       num_heaps++;
510     }
511   }
512 
513   // Loop over the objects in heap call the check
514   //   function on each one
515   HdrHeap *h = this;
516 
517   while (h) {
518     char *data = h->m_data_start;
519 
520     while (data < h->m_free_start) {
521       HdrHeapObjImpl *obj = reinterpret_cast<HdrHeapObjImpl *>(data);
522 
523       // Object length cannot be 0 by design, otherwise something is wrong + infinite loop here!
524       ink_release_assert(0 != obj->m_length);
525 
526       switch (obj->m_type) {
527       case HDR_HEAP_OBJ_URL:
528         ((URLImpl *)obj)->check_strings(heaps, num_heaps);
529         break;
530       case HDR_HEAP_OBJ_HTTP_HEADER:
531         ((HTTPHdrImpl *)obj)->check_strings(heaps, num_heaps);
532         break;
533       case HDR_HEAP_OBJ_MIME_HEADER:
534         ((MIMEHdrImpl *)obj)->check_strings(heaps, num_heaps);
535         break;
536       case HDR_HEAP_OBJ_FIELD_BLOCK:
537         ((MIMEFieldBlockImpl *)obj)->check_strings(heaps, num_heaps);
538         break;
539       case HDR_HEAP_OBJ_EMPTY:
540       case HDR_HEAP_OBJ_RAW:
541         // Nothing to do
542         break;
543       default:
544         ink_release_assert(0);
545       }
546 
547       data = data + obj->m_length;
548     }
549 
550     h = h->m_next;
551   }
552 }
553 
554 // int HdrHeap::marshal_length()
555 //
556 //  Determines what the length of a buffer needs to
557 //   be to marshal this header
558 //
559 int
marshal_length()560 HdrHeap::marshal_length()
561 {
562   int len;
563 
564   // If there is more than one HdrHeap block, we'll
565   //  coalesce the HdrHeap blocks together so we
566   //  only need one block header
567   len        = HDR_HEAP_HDR_SIZE;
568   HdrHeap *h = this;
569 
570   while (h) {
571     len += static_cast<int>(h->m_free_start - h->m_data_start);
572     h = h->m_next;
573   }
574 
575   // Since when we unmarshal, we won't have a writable string
576   //  heap, we can drop the header on the read/write
577   //  string heap
578   if (m_read_write_heap) {
579     len += m_read_write_heap->m_heap_size - (sizeof(HdrStrHeap) + m_read_write_heap->m_free_size);
580   }
581 
582   for (auto &j : m_ronly_heap) {
583     if (j.m_heap_start != nullptr) {
584       len += j.m_heap_len;
585     }
586   }
587 
588   len = HdrHeapMarshalBlocks(ts::round_up(len));
589   return len;
590 }
591 
592 #ifdef HDR_HEAP_CHECKSUMS
593 static uint32_t
compute_checksum(void * buf,int len)594 compute_checksum(void *buf, int len)
595 {
596   uint32_t cksum = 0;
597 
598   while (len > 4) {
599     cksum += *((uint32_t *)buf);
600     buf = ((char *)buf) + 4;
601     len -= 4;
602   }
603 
604   if (len > 0) {
605     uint32_t tmp = 0;
606     memcpy((char *)&tmp, buf, len);
607     cksum += tmp;
608   }
609 
610   return cksum;
611 }
612 #endif
613 
614 // int HdrHeap::marshal(char* buf, int len)
615 //
616 //   Creates a marshalled representation of the contents
617 //     of HdrHeap.  The marshalled representation is ususable
618 //     as a read-only HdrHeap after an unmarshal operation which
619 //     only swizzles offsets to pointer.  Special care needs to be
620 //     taken not to mess up the alignment of objects in
621 //     the heap to make this representation usable in the read-only
622 //     form
623 //
624 int
marshal(char * buf,int len)625 HdrHeap::marshal(char *buf, int len)
626 {
627   ink_assert((((uintptr_t)buf) & HDR_PTR_ALIGNMENT_MASK) == 0);
628 
629   HdrHeap *marshal_hdr = reinterpret_cast<HdrHeap *>(buf);
630   char *b              = buf + HDR_HEAP_HDR_SIZE;
631 
632   // Variables for the ptr translation table
633   int ptr_xl_size = 2;
634   MarshalXlate static_table[2];
635   MarshalXlate *ptr_xlation = static_table;
636   // need to initialize it here because of those gotos
637   MarshalXlate str_xlation[HDR_BUF_RONLY_HEAPS + 1];
638 
639   // Let's start by skipping over the header block
640   //  and copying the pointer blocks to marshalled
641   //  buffer
642   int ptr_heap_size = 0;
643   int str_size      = 0;
644   int ptr_heaps     = 0;
645   int str_heaps     = 0;
646 
647   // Variables used later on.  Sunpro doesn't like
648   //   bypassing initializations with gotos
649   int used;
650 
651   HdrHeap *unmarshal_hdr = this;
652 
653   do {
654     int copy_size = static_cast<int>(unmarshal_hdr->m_free_start - unmarshal_hdr->m_data_start);
655 
656     if (copy_size > len) {
657       goto Failed;
658     }
659     memcpy(b, unmarshal_hdr->m_data_start, copy_size);
660 
661     // Expand ptr xlation table if necessary - shameless hackery
662     if (ptr_heaps >= ptr_xl_size) {
663       MarshalXlate *tmp_xl = static_cast<MarshalXlate *>(alloca(sizeof(MarshalXlate) * ptr_xl_size * 2));
664       memcpy(tmp_xl, ptr_xlation, sizeof(MarshalXlate) * ptr_xl_size);
665       ptr_xlation = tmp_xl;
666       ptr_xl_size *= 2;
667     }
668     // Add translation table entry for pointer heaps
669     //   FIX ME - possible offset overflow issues?
670     ptr_xlation[ptr_heaps].start  = unmarshal_hdr->m_data_start;
671     ptr_xlation[ptr_heaps].end    = unmarshal_hdr->m_free_start;
672     ptr_xlation[ptr_heaps].offset = unmarshal_hdr->m_data_start - (b - buf);
673 
674     ptr_heap_size += copy_size;
675     b += copy_size;
676     len -= copy_size;
677     ptr_heaps++;
678 
679     unmarshal_hdr = unmarshal_hdr->m_next;
680   } while (unmarshal_hdr);
681 
682   // Now that we've got the pointer blocks marshaled
683   //  we can fill in the header on marshalled block
684   marshal_hdr->m_free_start = nullptr;
685   marshal_hdr->m_data_start = reinterpret_cast<char *>(HDR_HEAP_HDR_SIZE.value()); // offset
686   marshal_hdr->m_magic      = HDR_BUF_MAGIC_MARSHALED;
687   marshal_hdr->m_writeable  = false;
688   marshal_hdr->m_size       = ptr_heap_size + HDR_HEAP_HDR_SIZE;
689   marshal_hdr->m_next       = nullptr;
690   marshal_hdr->m_free_size  = 0;
691   marshal_hdr->m_read_write_heap.detach();
692   marshal_hdr->m_lost_string_space = this->m_lost_string_space;
693 
694   // We'have one read-only string heap after marshalling
695   marshal_hdr->m_ronly_heap[0].m_heap_start = (char *)static_cast<intptr_t>(marshal_hdr->m_size); // offset
696   marshal_hdr->m_ronly_heap[0].m_ref_count_ptr.detach();
697 
698   for (unsigned i = 1; i < HDR_BUF_RONLY_HEAPS; ++i) {
699     marshal_hdr->m_ronly_heap[i].m_heap_start = nullptr;
700   }
701 
702   // Next order of business is to copy over string heaps
703   //   As we are copying over the string heaps, build
704   //   translation table for string marshaling in the heap
705   //   objects
706   //
707   // FIX ME - really ought to check to see if lost_string_space
708   //   is too big and only copy over live strings if it is.  May
709   //   not be too much of a problem since I've prevented too much
710   //   lost string space both in string alloc and inherit
711 
712   if (m_read_write_heap) {
713     char *copy_start = (reinterpret_cast<char *>(m_read_write_heap.get())) + sizeof(HdrStrHeap);
714     int nto_copy     = m_read_write_heap->m_heap_size - (sizeof(HdrStrHeap) + m_read_write_heap->m_free_size);
715 
716     if (nto_copy > len) {
717       goto Failed;
718     }
719 
720     memcpy(b, copy_start, nto_copy);
721 
722     // FIX ME - possible offset overflow issues?
723     str_xlation[str_heaps].start  = copy_start;
724     str_xlation[str_heaps].end    = copy_start + nto_copy;
725     str_xlation[str_heaps].offset = copy_start - (b - buf);
726 
727     b += nto_copy;
728     len -= nto_copy;
729     str_size += nto_copy;
730     str_heaps++;
731   }
732 
733   for (auto &i : m_ronly_heap) {
734     if (i.m_heap_start != nullptr) {
735       if (i.m_heap_len > len) {
736         goto Failed;
737       }
738 
739       memcpy(b, i.m_heap_start, i.m_heap_len);
740 
741       // Add translation table entry for string heaps
742       //   FIX ME - possible offset overflow issues?
743       str_xlation[str_heaps].start  = i.m_heap_start;
744       str_xlation[str_heaps].end    = i.m_heap_start + i.m_heap_len;
745       str_xlation[str_heaps].offset = str_xlation[str_heaps].start - (b - buf);
746       ink_assert(str_xlation[str_heaps].start <= str_xlation[str_heaps].end);
747 
748       str_heaps++;
749       b += i.m_heap_len;
750       len -= i.m_heap_len;
751       str_size += i.m_heap_len;
752     }
753   }
754 
755   // Patch the str heap len
756   marshal_hdr->m_ronly_heap[0].m_heap_len = str_size;
757 
758   // Take our translation tables and loop over the objects
759   //    and call the object marshal function to patch live
760   //    strings pointers & live object pointers to offsets
761   {
762     char *obj_data  = (reinterpret_cast<char *>(marshal_hdr)) + HDR_HEAP_HDR_SIZE;
763     char *mheap_end = (reinterpret_cast<char *>(marshal_hdr)) + marshal_hdr->m_size;
764 
765     while (obj_data < mheap_end) {
766       HdrHeapObjImpl *obj = reinterpret_cast<HdrHeapObjImpl *>(obj_data);
767       ink_assert(obj_is_aligned(obj));
768 
769       switch (obj->m_type) {
770       case HDR_HEAP_OBJ_URL:
771         if (((URLImpl *)obj)->marshal(str_xlation, str_heaps) < 0) {
772           goto Failed;
773         }
774         break;
775       case HDR_HEAP_OBJ_HTTP_HEADER:
776         if (((HTTPHdrImpl *)obj)->marshal(ptr_xlation, ptr_heaps, str_xlation, str_heaps) < 0) {
777           goto Failed;
778         }
779         break;
780       case HDR_HEAP_OBJ_FIELD_BLOCK:
781         if (((MIMEFieldBlockImpl *)obj)->marshal(ptr_xlation, ptr_heaps, str_xlation, str_heaps) < 0) {
782           goto Failed;
783         }
784         break;
785       case HDR_HEAP_OBJ_MIME_HEADER:
786         if (((MIMEHdrImpl *)obj)->marshal(ptr_xlation, ptr_heaps, str_xlation, str_heaps)) {
787           goto Failed;
788         }
789         break;
790       case HDR_HEAP_OBJ_EMPTY:
791       case HDR_HEAP_OBJ_RAW:
792         // Check to make sure we aren't stuck
793         //   in an infinite loop
794         if (obj->m_length <= 0) {
795           ink_assert(0);
796           goto Failed;
797         }
798         // Nothing to do
799         break;
800       default:
801         ink_release_assert(0);
802       }
803 
804       obj_data = obj_data + obj->m_length;
805     }
806   }
807 
808   // Add up the total bytes used
809   used = ptr_heap_size + str_size + HDR_HEAP_HDR_SIZE;
810   used = HdrHeapMarshalBlocks(ts::round_up(used));
811 
812 #ifdef HDR_HEAP_CHECKSUMS
813   {
814     uint32_t chksum           = compute_checksum(buf, used);
815     marshal_hdr->m_free_start = (char *)chksum;
816   }
817 #endif
818 
819   return used;
820 
821 Failed:
822   marshal_hdr->m_magic = HDR_BUF_MAGIC_CORRUPT;
823   return -1;
824 }
825 
826 // bool HdrHeap::check_marshalled(char* buf, int buf_length)
827 //
828 //   Takes in marshalled buffer and verifies whether stuff appears
829 //     to be sane.  Returns true is sane.  Returns false if corrupt
830 //
831 bool
check_marshalled(uint32_t buf_length)832 HdrHeap::check_marshalled(uint32_t buf_length)
833 {
834   if (this->m_magic != HDR_BUF_MAGIC_MARSHALED) {
835     return false;
836   }
837 
838   if (this->m_size < (uint32_t)HDR_HEAP_HDR_SIZE) {
839     return false;
840   }
841 
842   if (this->m_size != (uintptr_t)this->m_ronly_heap[0].m_heap_start) {
843     return false;
844   }
845 
846   if ((uintptr_t)(this->m_size + m_ronly_heap[0].m_heap_start) > buf_length) {
847     return false;
848   }
849 
850   if (this->m_writeable != false) {
851     return false;
852   }
853 
854   if (this->m_free_size != 0) {
855     return false;
856   }
857 
858   if (this->m_ronly_heap[0].m_heap_start == nullptr) {
859     return false;
860   }
861 
862   return true;
863 }
864 
865 // int HdrHeap::unmarshal(int buf_length, int obj_type,
866 //                       HdrHeapObjImpl** found_obj,
867 //                       RefCountObj* block_ref)
868 //
869 //   Takes a marshalled representation and swizzles offsets
870 //     so they become live pointers and make the heap usable.
871 //     Sets *found_obj to first occurrence of object of
872 //     type obj_type in the heap
873 //
874 //   Return value is the number of bytes unmarshalled or -1
875 //     if error.  Caller is responsible for memory
876 //     management policy
877 //
878 int
unmarshal(int buf_length,int obj_type,HdrHeapObjImpl ** found_obj,RefCountObj * block_ref)879 HdrHeap::unmarshal(int buf_length, int obj_type, HdrHeapObjImpl **found_obj, RefCountObj *block_ref)
880 {
881   *found_obj = nullptr;
882 
883   // Check out this heap and make sure it is OK
884   if (m_magic != HDR_BUF_MAGIC_MARSHALED) {
885     ink_assert(!"HdrHeap::unmarshal bad magic");
886     return -1;
887   }
888 
889   int unmarshal_size = this->unmarshal_size();
890   if (unmarshal_size > buf_length) {
891     ink_assert(!"HdrHeap::unmarshal truncated header");
892     return -1;
893   }
894 #ifdef HDR_HEAP_CHECKSUMS
895   if (m_free_start != NULL) {
896     uint32_t stored_sum = (uint32_t)m_free_start;
897     m_free_start        = NULL;
898     int sum_len         = ROUND(unmarshal_size, HDR_PTR_SIZE);
899     uint32_t new_sum    = compute_checksum((void *)this, sum_len);
900 
901     if (stored_sum != new_sum) {
902       fprintf(stderr, "WARNING: Unmarshal checksum comparison failed\n");
903       dump_heap(unmarshal_size);
904       ink_assert(!"HdrHeap::unmarshal checksum failure");
905       return -1;
906     }
907   }
908 #else
909   // Because checksums could have been enabled in the past
910   //   and then be turned off without clearing the cache,
911   //   always reset our variable we use for checksumming
912   m_free_start = nullptr;
913 #endif
914 
915   ink_release_assert(m_writeable == false);
916   ink_release_assert(m_free_size == 0);
917   ink_release_assert(m_ronly_heap[0].m_heap_start != nullptr);
918 
919   ink_assert(m_free_start == nullptr);
920 
921   // Convert Heap offsets to pointers
922   m_data_start                 = (reinterpret_cast<char *>(this)) + (intptr_t)m_data_start;
923   m_free_start                 = (reinterpret_cast<char *>(this)) + m_size;
924   m_ronly_heap[0].m_heap_start = (reinterpret_cast<char *>(this)) + (intptr_t)m_ronly_heap[0].m_heap_start;
925 
926   // Crazy Invariant - If we are sitting in a ref counted block,
927   //   the HdrHeap lifetime is externally determined.  Whoever
928   //   unmarshalls us should keep the block around as long as
929   //   they want to use the header.  However, the strings can
930   //   live beyond the heap life time because they are copied
931   //   by reference into other header heap therefore we need
932   //   to the set the refcount ptr for the strings.  We don't
933   //   actually increase the refcount here since for the header
934   //   the lifetime is explicit but copies will increase
935   //   the refcount
936   if (block_ref) {
937     m_ronly_heap[0].m_ref_count_ptr.swizzle(block_ref);
938   }
939 
940   // Loop over objects and swizzle there pointer to
941   //  live offsets
942   char *obj_data  = m_data_start;
943   intptr_t offset = (intptr_t)this;
944 
945   while (obj_data < m_free_start) {
946     HdrHeapObjImpl *obj = reinterpret_cast<HdrHeapObjImpl *>(obj_data);
947     ink_assert(obj_is_aligned(obj));
948 
949     // Object length cannot be 0 by design, otherwise something is wrong + infinite loop here!
950     ink_release_assert(0 != obj->m_length);
951 
952     if (obj->m_type == static_cast<unsigned>(obj_type) && *found_obj == nullptr) {
953       *found_obj = obj;
954     }
955 
956     switch (obj->m_type) {
957     case HDR_HEAP_OBJ_HTTP_HEADER:
958       ((HTTPHdrImpl *)obj)->unmarshal(offset);
959       break;
960     case HDR_HEAP_OBJ_URL:
961       ((URLImpl *)obj)->unmarshal(offset);
962       break;
963     case HDR_HEAP_OBJ_FIELD_BLOCK:
964       ((MIMEFieldBlockImpl *)obj)->unmarshal(offset);
965       break;
966     case HDR_HEAP_OBJ_MIME_HEADER:
967       ((MIMEHdrImpl *)obj)->unmarshal(offset);
968       break;
969     case HDR_HEAP_OBJ_EMPTY:
970       // Nothing to do
971       break;
972     default:
973       fprintf(stderr, "WARNING: Unmarshal failed due to unknown obj type %d after %d bytes", static_cast<int>(obj->m_type),
974               static_cast<int>(obj_data - reinterpret_cast<char *>(this)));
975       dump_heap(unmarshal_size);
976       return -1;
977     }
978 
979     obj_data = obj_data + obj->m_length;
980   }
981 
982   m_magic = HDR_BUF_MAGIC_ALIVE;
983 
984   unmarshal_size = HdrHeapMarshalBlocks(ts::round_up(unmarshal_size));
985   return unmarshal_size;
986 }
987 
988 inline bool
attach_str_heap(char const * h_start,int h_len,RefCountObj * h_ref_obj,int * index)989 HdrHeap::attach_str_heap(char const *h_start, int h_len, RefCountObj *h_ref_obj, int *index)
990 {
991   if (*index >= static_cast<int>(HDR_BUF_RONLY_HEAPS)) {
992     return false;
993   }
994 
995   // Loop over existing entries to see if this one is already present
996   for (int z = 0; z < *index; z++) {
997     if (m_ronly_heap[z].m_heap_start == h_start) {
998       ink_assert(m_ronly_heap[z].m_ref_count_ptr.object() == h_ref_obj);
999 
1000       // The lengths could be different because our copy could be
1001       //   read-only and the copy we are attaching from could be
1002       //   read-write and have expanded since the last time
1003       //   to was attached
1004       if (h_len > m_ronly_heap[z].m_heap_len) {
1005         m_ronly_heap[z].m_heap_len = h_len;
1006       }
1007       return true;
1008     }
1009   }
1010 
1011   m_ronly_heap[*index].m_ref_count_ptr = h_ref_obj;
1012   m_ronly_heap[*index].m_heap_start    = h_start;
1013   m_ronly_heap[*index].m_heap_len      = h_len;
1014   m_ronly_heap[*index].m_locked        = false;
1015   *index                               = *index + 1;
1016 
1017   return true;
1018 }
1019 
1020 // void HdrHeap::inhertit_string_heaps(const HdrHeap* inherit_from)
1021 //
1022 //    Inherits all of inherit_from's string heaps as read-only
1023 //     string heaps
1024 //
1025 void
inherit_string_heaps(const HdrHeap * inherit_from)1026 HdrHeap::inherit_string_heaps(const HdrHeap *inherit_from)
1027 {
1028   // if heaps are the same, this is a no-op
1029   if (inherit_from == (const HdrHeap *)this) {
1030     return;
1031   }
1032 
1033   int first_free       = HDR_BUF_RONLY_HEAPS; // default is out of array bounds
1034   int free_slots       = 0;
1035   int inherit_str_size = 0;
1036   ink_assert(m_writeable);
1037 
1038   // Find the number of free heap slots & the first open index
1039   for (unsigned index = 0; index < HDR_BUF_RONLY_HEAPS; ++index) {
1040     if (m_ronly_heap[index].m_heap_start == nullptr) {
1041       if (first_free == HDR_BUF_RONLY_HEAPS) {
1042         first_free = index;
1043       }
1044       free_slots++;
1045     }
1046   }
1047 
1048   // Find out if we have enough slots
1049   if (inherit_from->m_read_write_heap) {
1050     free_slots--;
1051     inherit_str_size = inherit_from->m_read_write_heap->m_heap_size;
1052   }
1053   for (const auto &index : inherit_from->m_ronly_heap) {
1054     if (index.m_heap_start != nullptr) {
1055       free_slots--;
1056       inherit_str_size += index.m_heap_len;
1057     } else {
1058       // Heaps are allocated from the front of the array, so if
1059       //  we hit a NULL, we know we can stop
1060       break;
1061     }
1062   }
1063 
1064   // Find out if we are building up too much lost space
1065   int new_lost_space = m_lost_string_space + inherit_from->m_lost_string_space;
1066 
1067   if (free_slots < 0 || new_lost_space > static_cast<int>(MAX_LOST_STR_SPACE)) {
1068     // Not enough free slots.  We need to force a coalesce of
1069     //  string heaps for both old heaps and the inherited from heaps.
1070     // Coalesce can't know the inherited str size so we pass it
1071     //  it in so that it can allocate a new read-write string heap
1072     //  large enough (INKqa07513).
1073     // INVARIANT: inherit_str_heaps can only be called after
1074     //  all the objects the callee wants to inherit strings for
1075     //  are put into the heap
1076     coalesce_str_heaps(inherit_str_size);
1077   } else {
1078     // Copy over read/write string heap if it exists
1079     if (inherit_from->m_read_write_heap) {
1080       int str_size =
1081         inherit_from->m_read_write_heap->m_heap_size - sizeof(HdrStrHeap) - inherit_from->m_read_write_heap->m_free_size;
1082       ink_release_assert(attach_str_heap(reinterpret_cast<char *>(inherit_from->m_read_write_heap.get() + 1), str_size,
1083                                          inherit_from->m_read_write_heap.get(), &first_free));
1084     }
1085     // Copy over read only string heaps
1086     for (const auto &i : inherit_from->m_ronly_heap) {
1087       if (i.m_heap_start) {
1088         ink_release_assert(attach_str_heap(i.m_heap_start, i.m_heap_len, i.m_ref_count_ptr.get(), &first_free));
1089       }
1090     }
1091 
1092     m_lost_string_space += inherit_from->m_lost_string_space;
1093   }
1094 
1095   return;
1096 }
1097 
1098 // void HdrHeap::dump_heap(int len)
1099 //
1100 //   Debugging function to dump the heap in hex
1101 void
dump_heap(int len)1102 HdrHeap::dump_heap(int len)
1103 {
1104   int count = 0;
1105   char *tmp = reinterpret_cast<char *>(this);
1106   char *end;
1107   uint32_t content;
1108 
1109   if (len < 0) {
1110     len = m_size;
1111   }
1112   end = (reinterpret_cast<char *>(this)) + len;
1113 
1114   fprintf(stderr, "---- Dumping header heap @ 0x%" PRIx64 " - len %d ------", static_cast<uint64_t>((ptrdiff_t)this), len);
1115 
1116   while (tmp < end) {
1117     if (count % 4 == 0) {
1118       fprintf(stderr, "\n0x%" PRIx64 ": ", static_cast<uint64_t>((ptrdiff_t)tmp));
1119     }
1120     count++;
1121 
1122     // Load the content
1123     if (end - tmp > 4) {
1124       content = *(reinterpret_cast<uint32_t *>(tmp));
1125     } else {
1126       // Less than 4 bytes available so just
1127       //   grab the bytes we need
1128       content = 0;
1129       memcpy(&content, tmp, (end - tmp));
1130     }
1131 
1132     fprintf(stderr, "0x%x ", content);
1133     tmp += 4;
1134   }
1135 
1136   fprintf(stderr, "\n-------------- End header heap dump -----------\n");
1137 }
1138 
1139 uint64_t
total_used_size() const1140 HdrHeap::total_used_size() const
1141 {
1142   uint64_t size    = 0;
1143   const HdrHeap *h = this;
1144 
1145   while (h) {
1146     size += (h->m_free_start - h->m_data_start);
1147     h = h->m_next;
1148   }
1149 
1150   return size;
1151 }
1152 
1153 //
1154 // HdrStrHeap
1155 //
1156 
1157 void
free()1158 HdrStrHeap::free()
1159 {
1160   if (m_heap_size == HdrStrHeap::DEFAULT_SIZE) {
1161     THREAD_FREE(this, strHeapAllocator, this_thread());
1162   } else {
1163     ats_free(this);
1164   }
1165 }
1166 
1167 // char* HdrStrHeap::allocate(int nbytes)
1168 //
1169 //   Allocates nbytes from the str heap
1170 //   Return NULL on allocation failure
1171 //
1172 char *
allocate(int nbytes)1173 HdrStrHeap::allocate(int nbytes)
1174 {
1175   char *new_space;
1176 
1177   if (m_free_size >= static_cast<unsigned>(nbytes)) {
1178     new_space = m_free_start;
1179     m_free_start += nbytes;
1180     m_free_size -= nbytes;
1181     return new_space;
1182   } else {
1183     return nullptr;
1184   }
1185 }
1186 
1187 // char* HdrStrHeap::expand(char* ptr, int old_size, int new_size)
1188 //
1189 //   Try to expand str in the heap.  If we succeed to
1190 //     we return ptr, otherwise we return NULL
1191 //
1192 char *
expand(char * ptr,int old_size,int new_size)1193 HdrStrHeap::expand(char *ptr, int old_size, int new_size)
1194 {
1195   unsigned int expand_size = new_size - old_size;
1196 
1197   ink_assert(ptr >= reinterpret_cast<char const *>(this + 1));
1198   ink_assert(ptr < reinterpret_cast<char const *>(this) + m_heap_size);
1199 
1200   if (ptr + old_size == m_free_start && expand_size <= m_free_size) {
1201     m_free_start += expand_size;
1202     m_free_size -= expand_size;
1203     return ptr;
1204   } else {
1205     return nullptr;
1206   }
1207 }
1208