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