KittyPtrValidator.cpp
1 #include "KittyPtrValidator.hpp" 2 3 #ifdef __APPLE__ 4 5 bool KittyPtrValidator::_findRegion(uintptr_t addr, RegionInfo *region) 6 { 7 if (!use_cache_) 8 { 9 vm_address_t address = addr & ~(page_size_ - 1); 10 vm_size_t size = 0; 11 natural_t nesting_depth = 0; 12 vm_region_submap_short_info_data_64_t info{}; 13 mach_msg_type_number_t info_count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; 14 kern_return_t kret = vm_region_recurse_64(task_, 15 &address, 16 &size, 17 &nesting_depth, 18 (vm_region_recurse_info_t)&info, 19 &info_count); 20 if (kret != KERN_SUCCESS) 21 return false; 22 23 bool readable = (info.protection & VM_PROT_READ) != 0; 24 bool writable = (info.protection & VM_PROT_WRITE) != 0; 25 bool executable = (info.protection & VM_PROT_EXECUTE) != 0; 26 *region = RegionInfo(address, address + size, readable, writable, executable); 27 return address <= addr && addr < address + size; 28 } 29 30 if (!cachedRegions_.empty()) 31 { 32 if (last_region_index_ < cachedRegions_.size() && cachedRegions_[last_region_index_].start <= addr && 33 addr < cachedRegions_[last_region_index_].end) 34 { 35 *region = cachedRegions_[last_region_index_]; 36 return true; 37 } 38 39 size_t left = 0; 40 size_t right = cachedRegions_.size(); 41 size_t best_match = right; 42 43 while (left < right) 44 { 45 size_t mid = left + (right - left) / 2; 46 if (cachedRegions_[mid].end <= addr) 47 { 48 left = mid + 1; 49 } 50 else 51 { 52 best_match = mid; 53 right = mid; 54 } 55 } 56 57 if (best_match < cachedRegions_.size() && cachedRegions_[best_match].start <= addr && 58 addr < cachedRegions_[best_match].end) 59 { 60 last_region_index_ = best_match; 61 *region = cachedRegions_[best_match]; 62 return true; 63 } 64 } 65 66 return false; 67 } 68 69 void KittyPtrValidator::refreshRegionCache() 70 { 71 cachedRegions_.clear(); 72 vm_address_t address = 0; 73 74 while (true) 75 { 76 vm_size_t size = 0; 77 natural_t nesting_depth = 0; 78 vm_region_submap_short_info_data_64_t info{}; 79 mach_msg_type_number_t info_count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; 80 kern_return_t kret = vm_region_recurse_64(task_, 81 &address, 82 &size, 83 &nesting_depth, 84 (vm_region_recurse_info_t)&info, 85 &info_count); 86 if (kret != KERN_SUCCESS) 87 break; 88 89 bool readable = (info.protection & VM_PROT_READ) != 0; 90 bool writable = (info.protection & VM_PROT_WRITE) != 0; 91 bool executable = (info.protection & VM_PROT_EXECUTE) != 0; 92 RegionInfo new_region(address, address + size, readable, writable, executable); 93 94 if (!cachedRegions_.empty() && cachedRegions_.back().canMergeWith(new_region)) 95 { 96 cachedRegions_.back().end = new_region.end; 97 } 98 else 99 { 100 cachedRegions_.emplace_back(new_region); 101 } 102 103 address += size; 104 } 105 106 if (!cachedRegions_.empty()) 107 { 108 std::sort(cachedRegions_.begin(), cachedRegions_.end(), [](const RegionInfo &a, const RegionInfo &b) { 109 return a.start < b.start; 110 }); 111 } 112 113 last_region_index_ = 0; 114 } 115 116 #else 117 118 bool KittyPtrValidator::_parseMapsLine(const char *line, RegionInfo *region) 119 { 120 if (!line || *line == '\0') 121 return false; 122 123 char *endPtr; 124 uintptr_t start = (uintptr_t)strtoull(line, &endPtr, 16); 125 if (*endPtr != '-') 126 return false; 127 128 uintptr_t end = (uintptr_t)strtoull(endPtr + 1, &endPtr, 16); 129 130 while (*endPtr == ' ') 131 endPtr++; 132 133 if (!endPtr[0] || !endPtr[1] || !endPtr[2]) 134 return false; 135 136 *region = RegionInfo(start, end, endPtr[0] == 'r', endPtr[1] == 'w', endPtr[2] == 'x'); 137 return true; 138 } 139 140 bool KittyPtrValidator::_findRegion(uintptr_t addr, RegionInfo *out) 141 { 142 addr = KittyUtils::untagHeepPtr(addr); 143 if (!use_cache_) 144 { 145 bool found = false; 146 147 char filePath[0xff] = {}; 148 snprintf(filePath, sizeof(filePath), "/proc/%d/maps", pid_); 149 FILE *fp = fopen(filePath, "r"); 150 if (!fp) 151 return false; 152 153 char line[512]; 154 while (fgets(line, sizeof(line), fp)) 155 { 156 RegionInfo region(0, 0, false, false, false); 157 if (_parseMapsLine(line, ®ion)) 158 { 159 if (addr >= region.start && addr < region.end) 160 { 161 *out = region; 162 found = true; 163 break; 164 } 165 } 166 } 167 168 fclose(fp); 169 return found; 170 } 171 172 if (!cachedRegions_.empty()) 173 { 174 if (last_region_index_ < cachedRegions_.size()) 175 { 176 const auto &last = cachedRegions_[last_region_index_]; 177 if (addr >= last.start && addr < last.end) 178 { 179 *out = last; 180 return true; 181 } 182 } 183 184 auto it = std::lower_bound(cachedRegions_.begin(), 185 cachedRegions_.end(), 186 addr, 187 [](const RegionInfo &r, uintptr_t val) { return r.end <= val; }); 188 189 if (it != cachedRegions_.end() && addr >= it->start && addr < it->end) 190 { 191 *out = *it; 192 last_region_index_ = std::distance(cachedRegions_.begin(), it); 193 return true; 194 } 195 } 196 197 return false; 198 } 199 200 void KittyPtrValidator::refreshRegionCache() 201 { 202 cachedRegions_.clear(); 203 last_region_index_ = 0; 204 205 char filePath[0xff] = {}; 206 snprintf(filePath, sizeof(filePath), "/proc/%d/maps", pid_); 207 208 FILE *fp = fopen(filePath, "r"); 209 if (!fp) 210 return; 211 212 char line[512]; 213 while (fgets(line, sizeof(line), fp)) 214 { 215 RegionInfo region(0, 0, false, false, false); 216 217 if (_parseMapsLine(line, ®ion)) 218 { 219 if (!cachedRegions_.empty() && cachedRegions_.back().canMergeWith(region)) 220 { 221 cachedRegions_.back().end = region.end; 222 } 223 else 224 { 225 cachedRegions_.emplace_back(region); 226 } 227 } 228 } 229 230 fclose(fp); 231 232 if (!cachedRegions_.empty()) 233 { 234 std::sort(cachedRegions_.begin(), cachedRegions_.end(), [](const RegionInfo &a, const RegionInfo &b) { 235 return a.start < b.start; 236 }); 237 } 238 } 239 240 241 #endif 242 243 bool KittyPtrValidator::isPtrReadable(uintptr_t ptr, size_t len) 244 { 245 if (ptr == 0 || ptr + len < ptr) 246 return false; 247 248 ptr = KittyUtils::untagHeepPtr(ptr); 249 250 uintptr_t end = ptr + len; 251 RegionInfo region(0, 0, false, false, false); 252 while (region.end < end) 253 { 254 if (!_findRegion(ptr, ®ion) || !region.readable) 255 return false; 256 257 ptr = region.end; 258 } 259 260 return true; 261 } 262 263 bool KittyPtrValidator::isPtrWritable(uintptr_t ptr, size_t len) 264 { 265 if (ptr == 0 || ptr + len < ptr) 266 return false; 267 268 ptr = KittyUtils::untagHeepPtr(ptr); 269 270 uintptr_t end = ptr + len; 271 RegionInfo region(0, 0, false, false, false); 272 while (region.end < end) 273 { 274 if (!_findRegion(ptr, ®ion) || !region.writable) 275 return false; 276 277 ptr = region.end; 278 } 279 280 return true; 281 } 282 283 bool KittyPtrValidator::isPtrExecutable(uintptr_t ptr, size_t len) 284 { 285 if (ptr == 0 || ptr + len < ptr) 286 return false; 287 288 ptr = KittyUtils::untagHeepPtr(ptr); 289 290 uintptr_t end = ptr + len; 291 RegionInfo region(0, 0, false, false, false); 292 while (region.end < end) 293 { 294 if (!_findRegion(ptr, ®ion) || !region.executable) 295 return false; 296 297 ptr = region.end; 298 } 299 300 return true; 301 }