mirror of
				https://github.com/GrapheneOS/hardened_malloc.git
				synced 2025-10-26 14:16:33 +01:00 
			
		
		
		
	memtag_test: improve tag_distinctness test
- check that tag distinctess checks are actually reached (it was previously verified manually by looking at the now-removed printf output) - check that only non-reserved tags are used - check that all of non-reserved tags are used - print tag usage statistics at the end of run
This commit is contained in:
		
							parent
							
								
									14f4e927ca
								
							
						
					
					
						commit
						1adfed0252
					
				
					 1 changed files with 104 additions and 23 deletions
				
			
		|  | @ -35,49 +35,93 @@ void *untag_pointer(void *ptr) { | ||||||
|     return (void *) ((uintptr_t) ptr & mask); |     return (void *) ((uintptr_t) ptr & mask); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // This test checks that slab slot allocation uses tag that is distint from tags of its neighbors
 | ||||||
|  | // and from the tag of the previous allocation that used the same slot
 | ||||||
| void tag_distinctness() { | void tag_distinctness() { | ||||||
|     const size_t cnt = 3000; |     // 0 and 15 are reserved
 | ||||||
|     const size_t iter_cnt = 5; |     const int min_tag = 1; | ||||||
|     const size_t alloc_cnt = cnt * iter_cnt; |     const int max_tag = 14; | ||||||
| 
 | 
 | ||||||
|     const int sizes[] = { 16, 160, 10240, 20480 }; |     struct SizeClass { | ||||||
|  |         int size; | ||||||
|  |         int slot_cnt; | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     for (size_t size_idx = 0; size_idx < sizeof(sizes) / sizeof(int); ++size_idx) { |     // values from size_classes[] and size_class_slots[] in h_malloc.c
 | ||||||
|         const size_t full_alloc_size = sizes[size_idx]; |     SizeClass size_classes[] = { | ||||||
|  |         { .size = 16,    .slot_cnt = 256, }, | ||||||
|  |         { .size = 32,    .slot_cnt = 128, }, | ||||||
|  |         // this size class is used by allocations that are made by the addr_tag_map, which breaks
 | ||||||
|  |         // tag distinctess checks
 | ||||||
|  |         // { .size = 48,    .slot_cnt = 85,  },
 | ||||||
|  |         { .size = 64,    .slot_cnt = 64,  }, | ||||||
|  |         { .size = 80,    .slot_cnt = 51,  }, | ||||||
|  |         { .size = 96,    .slot_cnt = 42,  }, | ||||||
|  |         { .size = 112,   .slot_cnt = 36,  }, | ||||||
|  |         { .size = 128,   .slot_cnt = 64,  }, | ||||||
|  |         { .size = 160,   .slot_cnt = 51,  }, | ||||||
|  |         { .size = 192,   .slot_cnt = 64,  }, | ||||||
|  |         { .size = 224,   .slot_cnt = 54,  }, | ||||||
|  |         { .size = 10240, .slot_cnt = 6,   }, | ||||||
|  |         { .size = 20480, .slot_cnt = 1,   }, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     int tag_usage[max_tag + 1]; | ||||||
|  | 
 | ||||||
|  |     for (size_t sc_idx = 0; sc_idx < sizeof(size_classes) / sizeof(SizeClass); ++sc_idx) { | ||||||
|  |         SizeClass &sc = size_classes[sc_idx]; | ||||||
|  | 
 | ||||||
|  |         const size_t full_alloc_size = sc.size; | ||||||
|         const size_t alloc_size = full_alloc_size - CANARY_SIZE; |         const size_t alloc_size = full_alloc_size - CANARY_SIZE; | ||||||
| 
 | 
 | ||||||
|         unordered_map<uptr, u8> map; |         // "tdc" is short for "tag distinctness check"
 | ||||||
|         map.reserve(alloc_cnt); |         int left_neighbor_tdc_cnt = 0; | ||||||
|  |         int right_neighbor_tdc_cnt = 0; | ||||||
|  |         int prev_alloc_tdc_cnt = 0; | ||||||
| 
 | 
 | ||||||
|         for (size_t iter = 0; iter < iter_cnt; ++iter) { |         int iter_cnt = 600; | ||||||
|             uptr allocations[cnt]; |  | ||||||
| 
 | 
 | ||||||
|             for (size_t i = 0; i < cnt; ++i) { |         unordered_map<uptr, u8> addr_tag_map; | ||||||
|  |         addr_tag_map.reserve(iter_cnt * sc.slot_cnt); | ||||||
|  | 
 | ||||||
|  |         u64 seen_tags = 0; | ||||||
|  | 
 | ||||||
|  |         for (int iter = 0; iter < iter_cnt; ++iter) { | ||||||
|  |             uptr allocations[256]; // 256 is max slot count
 | ||||||
|  | 
 | ||||||
|  |             for (int i = 0; i < sc.slot_cnt; ++i) { | ||||||
|                 u8 *p = (u8 *) malloc(alloc_size); |                 u8 *p = (u8 *) malloc(alloc_size); | ||||||
|  |                 assert(p); | ||||||
|                 uptr addr = (uptr) untag_pointer(p); |                 uptr addr = (uptr) untag_pointer(p); | ||||||
|                 u8 tag = get_pointer_tag(p); |                 u8 tag = get_pointer_tag(p); | ||||||
|                 assert(tag >= 1 && tag <= 14); | 
 | ||||||
|  |                 assert(tag >= min_tag && tag <= max_tag); | ||||||
|  |                 seen_tags |= 1 << tag; | ||||||
|  |                 ++tag_usage[tag]; | ||||||
| 
 | 
 | ||||||
|                 // check most recent tags of left and right neighbors
 |                 // check most recent tags of left and right neighbors
 | ||||||
| 
 | 
 | ||||||
|                 auto left = map.find(addr - full_alloc_size); |                 auto left = addr_tag_map.find(addr - full_alloc_size); | ||||||
|                 if (left != map.end()) { |                 if (left != addr_tag_map.end()) { | ||||||
|                     assert(left->second != tag); |                     assert(left->second != tag); | ||||||
|  |                     ++left_neighbor_tdc_cnt; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 auto right = map.find(addr + full_alloc_size); |                 auto right = addr_tag_map.find(addr + full_alloc_size); | ||||||
|                 if (right != map.end()) { |                 if (right != addr_tag_map.end()) { | ||||||
|                     assert(right->second != tag); |                     assert(right->second != tag); | ||||||
|  |                     ++right_neighbor_tdc_cnt; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 // check previous tag of this slot
 |                 // check previous tag of this slot
 | ||||||
|                 auto prev = map.find(addr); |                 auto prev = addr_tag_map.find(addr); | ||||||
|                 if (prev != map.end()) { |                 if (prev != addr_tag_map.end()) { | ||||||
|                     assert(prev->second != tag); |                     assert(prev->second != tag); | ||||||
|                     map.erase(addr); |                     ++prev_alloc_tdc_cnt; | ||||||
|  |                     addr_tag_map.erase(addr); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 map.emplace(addr, tag); |                 addr_tag_map.emplace(addr, tag); | ||||||
| 
 | 
 | ||||||
|                 for (size_t j = 0; j < alloc_size; ++j) { |                 for (size_t j = 0; j < alloc_size; ++j) { | ||||||
|                     // check that slot is zeroed
 |                     // check that slot is zeroed
 | ||||||
|  | @ -87,15 +131,52 @@ void tag_distinctness() { | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 allocations[i] = addr; |                 allocations[i] = addr; | ||||||
|                 // async tag check failures are reported on context switch
 |  | ||||||
|                 do_context_switch(); |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             for (size_t i = 0; i < cnt; ++i) { |             // free some of allocations to allow their slots to be reused
 | ||||||
|  |             for (int i = sc.slot_cnt - 1; i >= 0; i -= 2) { | ||||||
|                 free((void *) allocations[i]); |                 free((void *) allocations[i]); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         // check that all of the tags were used, except reserved ones
 | ||||||
|  |         assert(seen_tags == (0xffff & ~(1 << 0 | 1 << 15))); | ||||||
|  | 
 | ||||||
|  |         printf("size_class\t%i\t" "tdc_left %i\t" "tdc_right %i\t" "tdc_prev_alloc %i\n", | ||||||
|  |                sc.size, left_neighbor_tdc_cnt, right_neighbor_tdc_cnt, prev_alloc_tdc_cnt); | ||||||
|  | 
 | ||||||
|  |         // make sure tag distinctess checks were actually performed
 | ||||||
|  |         int min_tdc_cnt = sc.slot_cnt * iter_cnt / 5; | ||||||
|  | 
 | ||||||
|  |         assert(prev_alloc_tdc_cnt > min_tdc_cnt); | ||||||
|  | 
 | ||||||
|  |         if (sc.slot_cnt > 1) { | ||||||
|  |             assert(left_neighbor_tdc_cnt > min_tdc_cnt); | ||||||
|  |             assert(right_neighbor_tdc_cnt > min_tdc_cnt); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // async tag check failures are reported on context switch
 | ||||||
|  |         do_context_switch(); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     printf("\nTag use counters:\n"); | ||||||
|  | 
 | ||||||
|  |     int min = INT_MAX; | ||||||
|  |     int max = 0; | ||||||
|  |     double geomean = 0.0; | ||||||
|  |     for (int i = min_tag; i <= max_tag; ++i) { | ||||||
|  |         int v = tag_usage[i]; | ||||||
|  |         geomean += log(v); | ||||||
|  |         min = std::min(min, v); | ||||||
|  |         max = std::max(max, v); | ||||||
|  |         printf("%i\t%i\n", i, tag_usage[i]); | ||||||
|  |     } | ||||||
|  |     int tag_cnt = 1 + max_tag - min_tag; | ||||||
|  |     geomean = exp(geomean / tag_cnt); | ||||||
|  | 
 | ||||||
|  |     double max_deviation = std::max((double) max - geomean, geomean - min); | ||||||
|  | 
 | ||||||
|  |     printf("geomean: %.2f, max deviation from geomean: %.2f%%\n", geomean, (100.0 * max_deviation) / geomean); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| u8* alloc_default() { | u8* alloc_default() { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue