1 /* Copyright (C) 2007-2010 The Android Open Source Project 2 ** 3 ** This software is licensed under the terms of the GNU General Public 4 ** License version 2, as published by the Free Software Foundation, and 5 ** may be copied, distributed, and modified under those terms. 6 ** 7 ** This program is distributed in the hope that it will be useful, 8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 ** GNU General Public License for more details. 11 */ 12 13 /* 14 * Contains implementation of routines that implement a red-black tree of 15 * memory blocks allocated by the guest system. 16 */ 17 18 #include "memcheck_malloc_map.h" 19 #include "memcheck_util.h" 20 #include "memcheck_logging.h" 21 22 /* Global flag, indicating whether or not __ld/__stx_mmu should be instrumented 23 * for checking for access violations. If read / write access violation check. 24 * Defined in memcheck.c 25 */ 26 extern int memcheck_instrument_mmu; 27 28 /* Allocation descriptor stored in the map. */ 29 typedef struct AllocMapEntry { 30 /* R-B tree entry. */ 31 RB_ENTRY(AllocMapEntry) rb_entry; 32 33 /* Allocation descriptor for this entry. */ 34 MallocDescEx desc; 35 } AllocMapEntry; 36 37 // ============================================================================= 38 // Inlines 39 // ============================================================================= 40 41 /* Gets address of the beginning of an allocation block for the given entry in 42 * the map. 43 * Param: 44 * adesc - Entry in the allocation descriptors map. 45 * Return: 46 * Address of the beginning of an allocation block for the given entry in the 47 * map. 48 */ 49 static inline target_ulong 50 allocmapentry_alloc_begins(const AllocMapEntry* adesc) 51 { 52 return adesc->desc.malloc_desc.ptr; 53 } 54 55 /* Gets address of the end of an allocation block for the given entry in 56 * the map. 57 * Param: 58 * adesc - Entry in the allocation descriptors map. 59 * Return: 60 * Address of the end of an allocation block for the given entry in the map. 61 */ 62 static inline target_ulong 63 allocmapentry_alloc_ends(const AllocMapEntry* adesc) 64 { 65 return mallocdesc_get_alloc_end(&adesc->desc.malloc_desc); 66 } 67 68 // ============================================================================= 69 // R-B Tree implementation 70 // ============================================================================= 71 72 /* Compare routine for the allocation descriptors map. 73 * Param: 74 * d1 - First map entry to compare. 75 * d2 - Second map entry to compare. 76 * Return: 77 * 0 - Descriptors are equal. Note that descriptors are considered to be 78 * equal iff memory blocks they describe intersect in any part. 79 * 1 - d1 is greater than d2 80 * -1 - d1 is less than d2. 81 */ 82 static inline int 83 cmp_rb(AllocMapEntry* d1, AllocMapEntry* d2) 84 { 85 const target_ulong start1 = allocmapentry_alloc_begins(d1); 86 const target_ulong start2 = allocmapentry_alloc_begins(d2); 87 88 if (start1 < start2) { 89 return (allocmapentry_alloc_ends(d1) - 1) < start2 ? -1 : 0; 90 } 91 return (allocmapentry_alloc_ends(d2) - 1) < start1 ? 1 : 0; 92 } 93 94 /* Expands RB macros here. */ 95 RB_GENERATE(AllocMap, AllocMapEntry, rb_entry, cmp_rb); 96 97 // ============================================================================= 98 // Static routines 99 // ============================================================================= 100 101 /* Inserts new (or replaces existing) entry into allocation descriptors map. 102 * See comments on allocmap_insert routine in the header file for details 103 * about this routine. 104 */ 105 static RBTMapResult 106 allocmap_insert_desc(AllocMap* map, 107 AllocMapEntry* adesc, 108 MallocDescEx* replaced) 109 { 110 AllocMapEntry* existing = AllocMap_RB_INSERT(map, adesc); 111 if (existing == NULL) { 112 return RBT_MAP_RESULT_ENTRY_INSERTED; 113 } 114 115 // Matching entry exists. Lets see if we need to replace it. 116 if (replaced == NULL) { 117 return RBT_MAP_RESULT_ENTRY_ALREADY_EXISTS; 118 } 119 120 /* Copy existing entry to the provided buffer and replace it 121 * with the new one. */ 122 memcpy(replaced, &existing->desc, sizeof(MallocDescEx)); 123 AllocMap_RB_REMOVE(map, existing); 124 qemu_free(existing); 125 AllocMap_RB_INSERT(map, adesc); 126 return RBT_MAP_RESULT_ENTRY_REPLACED; 127 } 128 129 /* Finds an entry in the allocation descriptors map that matches the given 130 * address range. 131 * Param: 132 * map - Allocation descriptors map where to search for an entry. 133 * address - Virtual address in the guest's user space to find matching 134 * entry for. 135 * Return: 136 * Address of an allocation descriptors map entry that matches the given 137 * address, or NULL if no such entry has been found. 138 */ 139 static inline AllocMapEntry* 140 allocmap_find_entry(const AllocMap* map, 141 target_ulong address, 142 uint32_t block_size) 143 { 144 AllocMapEntry adesc; 145 adesc.desc.malloc_desc.ptr = address; 146 adesc.desc.malloc_desc.requested_bytes = block_size; 147 adesc.desc.malloc_desc.prefix_size = 0; 148 adesc.desc.malloc_desc.suffix_size = 0; 149 return AllocMap_RB_FIND((AllocMap*)map, &adesc); 150 } 151 152 // ============================================================================= 153 // Map API 154 // ============================================================================= 155 156 void 157 allocmap_init(AllocMap* map) 158 { 159 RB_INIT(map); 160 } 161 162 RBTMapResult 163 allocmap_insert(AllocMap* map, const MallocDescEx* desc, MallocDescEx* replaced) 164 { 165 RBTMapResult ret; 166 167 // Allocate and initialize new map entry. 168 AllocMapEntry* adesc = qemu_malloc(sizeof(AllocMapEntry)); 169 if (adesc == NULL) { 170 ME("memcheck: Unable to allocate new AllocMapEntry on insert."); 171 return RBT_MAP_RESULT_ERROR; 172 } 173 memcpy(&adesc->desc, desc, sizeof(MallocDescEx)); 174 175 // Insert new entry into the map. 176 ret = allocmap_insert_desc(map, adesc, replaced); 177 if (ret == RBT_MAP_RESULT_ENTRY_ALREADY_EXISTS || 178 ret == RBT_MAP_RESULT_ERROR) { 179 /* Another descriptor already exists for this block, or an error 180 * occurred. We have to tree new descriptor, as it wasn't inserted. */ 181 qemu_free(adesc); 182 } 183 return ret; 184 } 185 186 MallocDescEx* 187 allocmap_find(const AllocMap* map, target_ulong address, uint32_t block_size) 188 { 189 AllocMapEntry* adesc = allocmap_find_entry(map, address, block_size); 190 return adesc != NULL ? &adesc->desc : NULL; 191 } 192 193 int 194 allocmap_pull(AllocMap* map, target_ulong address, MallocDescEx* pulled) 195 { 196 AllocMapEntry* adesc = allocmap_find_entry(map, address, 1); 197 if (adesc != NULL) { 198 memcpy(pulled, &adesc->desc, sizeof(MallocDescEx)); 199 AllocMap_RB_REMOVE(map, adesc); 200 qemu_free(adesc); 201 return 0; 202 } else { 203 return -1; 204 } 205 } 206 207 int 208 allocmap_pull_first(AllocMap* map, MallocDescEx* pulled) 209 { 210 AllocMapEntry* first = RB_MIN(AllocMap, map); 211 if (first != NULL) { 212 memcpy(pulled, &first->desc, sizeof(MallocDescEx)); 213 AllocMap_RB_REMOVE(map, first); 214 qemu_free(first); 215 return 0; 216 } else { 217 return -1; 218 } 219 } 220 221 int 222 allocmap_copy(AllocMap* to, 223 const AllocMap* from, 224 uint32_t set_flags, 225 uint32_t clear_flags) 226 { 227 AllocMapEntry* entry; 228 RB_FOREACH(entry, AllocMap, (AllocMap*)from) { 229 RBTMapResult ins_res; 230 AllocMapEntry* new_entry = 231 (AllocMapEntry*)qemu_malloc(sizeof(AllocMapEntry)); 232 if (new_entry == NULL) { 233 ME("memcheck: Unable to allocate new AllocMapEntry on copy."); 234 return -1; 235 } 236 memcpy(new_entry, entry, sizeof(AllocMapEntry)); 237 new_entry->desc.flags &= ~clear_flags; 238 new_entry->desc.flags |= set_flags; 239 if (entry->desc.call_stack_count) { 240 new_entry->desc.call_stack = 241 qemu_malloc(entry->desc.call_stack_count * sizeof(target_ulong)); 242 memcpy(new_entry->desc.call_stack, entry->desc.call_stack, 243 entry->desc.call_stack_count * sizeof(target_ulong)); 244 } else { 245 new_entry->desc.call_stack = NULL; 246 } 247 new_entry->desc.call_stack_count = entry->desc.call_stack_count; 248 ins_res = allocmap_insert_desc(to, new_entry, NULL); 249 if (ins_res == RBT_MAP_RESULT_ENTRY_INSERTED) { 250 if (memcheck_instrument_mmu) { 251 // Invalidate TLB cache for inserted entry. 252 invalidate_tlb_cache(new_entry->desc.malloc_desc.ptr, 253 mallocdesc_get_alloc_end(&new_entry->desc.malloc_desc)); 254 } 255 } else { 256 ME("memcheck: Unable to insert new map entry on copy. Insert returned %u", 257 ins_res); 258 if (new_entry->desc.call_stack != NULL) { 259 qemu_free(new_entry->desc.call_stack); 260 } 261 qemu_free(new_entry); 262 return -1; 263 } 264 } 265 266 return 0; 267 } 268 269 int 270 allocmap_empty(AllocMap* map) 271 { 272 MallocDescEx pulled; 273 int removed = 0; 274 275 while (!allocmap_pull_first(map, &pulled)) { 276 removed++; 277 if (pulled.call_stack != NULL) { 278 qemu_free(pulled.call_stack); 279 } 280 } 281 282 return removed; 283 } 284