1""" Please make sure you read the README file COMPLETELY BEFORE reading anything below. 2 It is very critical that you read coding guidelines in Section E in README file. 3""" 4from __future__ import absolute_import, division, print_function 5 6from builtins import hex 7from builtins import range 8 9import sys 10from xnu import * 11from utils import * 12from process import * 13from bank import * 14from waitq import * 15from ioreg import * 16from memory import * 17import xnudefines 18import kmemory 19 20# ruff: noqa: F405 F403 21 22# from osfmk/ipc/ipc_entry.h 23IE_BITS_ROLL_MASK = 0x03000000 24 25# from osfmk/mach/mach_types.h 26TASK_FLAVOR_CONTROL = 0 27TASK_FLAVOR_READ = 1 28TASK_FLAVOR_INSPECT = 2 29TASK_FLAVOR_NAME = 3 30 31 32def IsKObjectType(ty): 33 return ty >= GetEnumValue("ipc_object_type_t", "__IKOT_FIRST") 34 35 36def IPCPortTypeToString(ty): 37 s = GetEnumName("ipc_object_type_t", ty, "IKOT_") 38 if s.startswith("IOT_"): 39 s = s[4:].replace("_PORT", "") 40 return s.lower() 41 42 43def IsPortType(ty, name): 44 return ty == GetEnumValue("ipc_object_type_t", name) 45 46 47def IsPortSetType(ty): 48 return IsPortType(ty, "IOT_PORT_SET") 49 50 51def GetPortLabel(port, ty): 52 addr = kern.StripKernelPAC(port.xGetScalarByPath(".ip_object.iol_pointer")) 53 return port.xCreateValueFromAddress(None, addr, gettype(ty)) 54 55 56@lldb_type_summary(["struct ipc_entry_table *", "ipc_entry_table_t"]) 57def PrintIpcEntryTable(array): 58 t, s = kalloc_array_decode(array, "struct ipc_entry") 59 return "ptr = {:#x}, size = {:d}, elem_type = struct ipc_entry".format( 60 unsigned(t), s 61 ) 62 63 64@lldb_type_summary(["struct ipc_port_requests_table *", "ipc_port_requests_table_t"]) 65def PrintIpcPortRequestTable(array): 66 t, s = kalloc_array_decode(array, "struct ipc_port_requests") 67 return "ptr = {:#x}, size = {:d}, elem_type = struct ipc_port_requests".format( 68 unsigned(t), s 69 ) 70 71 72def GetSpaceTable(space): 73 """Return the tuple of (entries, size) of the table for a space""" 74 table = space.is_table.__smr_ptr 75 if table: 76 return kalloc_array_decode(table, "struct ipc_entry") 77 return (None, 0) 78 79 80def GetSpaceEntriesWithBits(is_tableval, num_entries, mask): 81 base = is_tableval.GetSBValue().Dereference() 82 return ( 83 (index, iep) 84 for index, iep in enumerate(base.xIterSiblings(1, num_entries), 1) 85 if iep.xGetIntegerByName("ie_bits") & mask 86 ) 87 88 89def GetSpaceObjectsWithBits(is_tableval, num_entries, mask, ty): 90 base = is_tableval.GetSBValue().Dereference() 91 return ( 92 iep.xCreateValueFromAddress( 93 None, 94 iep.xGetIntegerByName("ie_object"), 95 ty, 96 ) 97 for iep in base.xIterSiblings(1, num_entries) 98 if iep.xGetIntegerByName("ie_bits") & mask 99 ) 100 101 102def GetGenFromIEBits(ie_bits): 103 return (ie_bits >> 24) | 3 104 105 106def GetNameFromIndexAndIEBits(index, ie_bits): 107 return (index << 8) | GetGenFromIEBits(ie_bits) 108 109 110@header( 111 "{0: <20s} {1: <6s} {2: <6s} {3: <10s} {4: <32s}".format( 112 "task", "pid", "#acts", "tablesize", "command" 113 ) 114) 115def GetTaskIPCSummary(task, show_busy=False): 116 """Display a task's ipc summary. 117 params: 118 task : core.value represeting a Task in kernel 119 returns 120 str - string of ipc info for the task 121 """ 122 out_string = "" 123 format_string = "{0: <#20x} {1: <6d} {2: <6d} {3: <10d} {4: <32s}" 124 busy_format = " {0: <10d} {1: <6d}" 125 proc_name = "" 126 if not task.active: 127 proc_name = "terminated: " 128 if task.halting: 129 proc_name += "halting: " 130 proc_name += GetProcNameForTask(task) 131 _, table_size = GetSpaceTable(task.itk_space) 132 out_string += format_string.format( 133 task, GetProcPIDForTask(task), task.thread_count, table_size, proc_name 134 ) 135 if show_busy: 136 nbusy, nmsgs = GetTaskBusyPortsSummary(task) 137 out_string += busy_format.format(nbusy, nmsgs) 138 return (out_string, table_size, nbusy, nmsgs) 139 return (out_string, table_size) 140 141 142@header( 143 "{0: <20s} {1: <6s} {2: <6s} {3: <10s} {4: <32s} {5: <10s} {6: <6s}".format( 144 "task", "pid", "#acts", "tablesize", "command", "#busyports", "#kmsgs" 145 ) 146) 147def GetTaskBusyIPCSummary(task): 148 return GetTaskIPCSummary(task, True) 149 150 151def GetTaskBusyPortsSummary(task): 152 is_tableval, num_entries = GetSpaceTable(task.itk_space) 153 gettype("struct ipc_port") 154 nbusy = 0 155 nmsgs = 0 156 157 if is_tableval: 158 ports = GetSpaceObjectsWithBits( 159 is_tableval, num_entries, 0x00020000, gettype("struct ipc_port") 160 ) 161 162 for port in ports: 163 if not port or port == xnudefines.MACH_PORT_DEAD: 164 continue 165 count = port.xGetIntegerByPath(".ip_messages.imq_msgcount") 166 if count: 167 nbusy += 1 168 nmsgs += count 169 170 return (nbusy, nmsgs) 171 172 173@header( 174 "{:<20s} {:<20s} {:<10s} {:>6s} {:<20s} {:>8s} {:<20s} {:s}".format( 175 "port", 176 "waitqueue", 177 "recvname", 178 "refs", 179 "receiver", 180 "nmsgs", 181 "kind", 182 "dest/kobject", 183 ) 184) 185def PrintPortSummary(port, show_kmsg_summary=True, show_sets=False, prefix="", O=None): 186 """Display a port's summary 187 params: 188 port : core.value representing a port in the kernel 189 returns 190 str : string of ipc info for the given port 191 """ 192 193 format_string = "{:<#20x} {:<#20x} {:#010x} {:>6d} {:<#20x} {:>8d} {:<20s} {:<s}" 194 receiver_name = port.ip_messages.imq_receiver_name 195 space = 0 196 kind = IPCPortTypeToString(port.ip_object.io_type) 197 198 if unsigned(port.ip_object.io_state): 199 if receiver_name: 200 space = unsigned(port.ip_receiver) 201 202 _, dest_str, service_str = GetPortDestProc(port) 203 else: 204 dest_str = "inactive-port" 205 service_str = "" 206 207 print( 208 prefix 209 + format_string.format( 210 unsigned(port), 211 addressof(port.ip_waitq), 212 unsigned(receiver_name), 213 port.ip_object.io_references, 214 space, 215 port.ip_messages.imq_msgcount, 216 kind, 217 dest_str + " " + service_str, 218 ) 219 ) 220 221 if show_kmsg_summary: 222 with O.table(prefix + GetKMsgSummary.header): 223 for kmsgp in IterateCircleQueue( 224 port.ip_messages.imq_messages, "ipc_kmsg", "ikm_link" 225 ): 226 print(prefix + GetKMsgSummary(kmsgp, prefix)) 227 228 wq = Waitq(addressof(port.ip_waitq)) 229 if show_sets and wq.hasSets(): 230 231 def doit(wq): 232 for wqs in Waitq(addressof(port.ip_waitq)).iterateSets(): 233 PrintPortSetSummary( 234 wqs.asPset(), space=port.ip_receiver, verbose=False, O=O 235 ) 236 237 if O is None: 238 print(PrintPortSetSummary.header) 239 doit(wq) 240 else: 241 with O.table(PrintPortSetSummary.header, indent=True): 242 doit(wq) 243 print("") 244 245 246def GetPortDispositionString(disp): 247 if disp < 0: ## use negative numbers for request ports 248 if disp == -1: 249 disp_str = "reqNS" 250 elif disp == -2: 251 disp_str = "reqPD" 252 elif disp == -3: 253 disp_str = "reqSPa" 254 elif disp == -4: 255 disp_str = "reqSPr" 256 elif disp == -5: 257 disp_str = "reqSPra" 258 else: 259 disp_str = "-X" 260 ## These dispositions should match those found in osfmk/mach/message.h 261 elif disp == 16: 262 disp_str = "R" ## receive 263 elif disp == 24: 264 disp_str = "dR" ## dispose receive 265 elif disp == 17: 266 disp_str = "S" ## (move) send 267 elif disp == 19: 268 disp_str = "cS" ## copy send 269 elif disp == 20: 270 disp_str = "mS" ## make send 271 elif disp == 25: 272 disp_str = "dS" ## dispose send 273 elif disp == 18: 274 disp_str = "O" ## send-once 275 elif disp == 21: 276 disp_str = "mO" ## make send-once 277 elif disp == 26: 278 disp_str = "dO" ## dispose send-once 279 ## faux dispositions used to string-ify IPC entry types 280 elif disp == 100: 281 disp_str = "PS" ## port set 282 elif disp == 101: 283 disp_str = "dead" ## dead name 284 elif disp == 102: 285 disp_str = "L" ## LABELH 286 elif disp == 103: 287 disp_str = "V" ## Thread voucher (thread->ith_voucher->iv_port) 288 ## Catch-all 289 else: 290 disp_str = "X" ## invalid 291 return disp_str 292 293 294def GetPortPDRequest(port): 295 """Returns the port-destroyed notification port if any""" 296 if port.ip_has_watchport: 297 return port.ip_twe.twe_pdrequest 298 if not IsPortType(port.ip_object.io_type, "IOT_SPECIAL_REPLY_PORT"): 299 return port.ip_pdrequest 300 return 0 301 302 303def GetKmsgHeader(kmsgp): 304 """Helper to get mach message header of a kmsg. 305 Assumes the kmsg has not been put to user. 306 params: 307 kmsgp : core.value representing the given ipc_kmsg_t struct 308 returns: 309 Mach message header for kmsgp 310 """ 311 if kmsgp.ikm_type == GetEnumValue("ipc_kmsg_type_t", "IKM_TYPE_ALL_INLINED"): 312 return kern.GetValueFromAddress( 313 int(addressof(kmsgp.ikm_big_data)), "mach_msg_header_t *" 314 ) 315 if kmsgp.ikm_type == GetEnumValue("ipc_kmsg_type_t", "IKM_TYPE_UDATA_OOL"): 316 return kern.GetValueFromAddress( 317 int(addressof(kmsgp.ikm_small_data)), "mach_msg_header_t *" 318 ) 319 return kern.GetValueFromAddress(unsigned(kmsgp.ikm_kdata), "mach_msg_header_t *") 320 321 322@header( 323 "{:<20s} {:<20s} {:<20s} {:<10s} {:>6s} {:<20s} {:<8s} {:<26s} {:<12s} {:<20s}".format( 324 "", 325 "kmsg", 326 "header", 327 "msgid", 328 "size", 329 "reply-port", 330 "disp", 331 "source", 332 "destname", 333 "destination", 334 ) 335) 336def GetKMsgSummary(kmsgp, prefix_str=""): 337 """Display a summary for type ipc_kmsg_t 338 params: 339 kmsgp : core.value representing the given ipc_kmsg_t struct 340 returns: 341 str : string of summary info for the given ipc_kmsg_t instance 342 """ 343 kmsghp = GetKmsgHeader(kmsgp) 344 kmsgh = dereference(kmsghp) 345 out_string = "" 346 out_string += "{:<20s} {:<#20x} {:<#20x} {kmsgh.msgh_id:#010x} {kmsgh.msgh_size:>6d} {kmsgh.msgh_local_port:<#20x} ".format( 347 "", unsigned(kmsgp), unsigned(kmsghp), kmsgh=kmsghp 348 ) 349 prefix_str = "{:<20s} ".format(" ") + prefix_str 350 disposition = "" 351 bits = kmsgh.msgh_bits & 0xFF 352 353 # remote port 354 if bits == 17: 355 disposition = "rS" 356 elif bits == 18: 357 disposition = "rO" 358 else: 359 disposition = "rX" # invalid 360 361 out_string += "{:<2s}".format(disposition) 362 363 # local port 364 disposition = "" 365 bits = (kmsgh.msgh_bits & 0xFF00) >> 8 366 367 if bits == 17: 368 disposition = "lS" 369 elif bits == 18: 370 disposition = "lO" 371 elif bits == 0: 372 disposition = "l-" 373 else: 374 disposition = "lX" # invalid 375 376 out_string += "{:<2s}".format(disposition) 377 378 # voucher 379 disposition = "" 380 bits = (kmsgh.msgh_bits & 0xFF0000) >> 16 381 382 if bits == 17: 383 disposition = "vS" 384 elif bits == 0: 385 disposition = "v-" 386 else: 387 disposition = "vX" 388 389 out_string += "{:<2s}".format(disposition) 390 391 # complex message 392 if kmsgh.msgh_bits & 0x80000000: 393 out_string += "{0: <1s}".format("c") 394 else: 395 out_string += "{0: <1s}".format("s") 396 397 # importance boost 398 if kmsgh.msgh_bits & 0x20000000: 399 out_string += "{0: <1s}".format("I") 400 else: 401 out_string += "{0: <1s}".format("-") 402 403 dest_port = GetKmsgHeader(kmsgp).msgh_remote_port 404 if dest_port: 405 name_str, dest_str, _ = GetPortDestProc(dest_port) 406 else: 407 name_str = dest_str = "" 408 409 out_string += " {:<26s} {:<12s} {:<20s}".format( 410 GetKMsgSrc(kmsgp), name_str, dest_str 411 ) 412 413 if kmsgh.msgh_bits & 0x80000000: # MACH_MSGH_BITS_COMPLEX 414 out_string += "\n" + prefix_str + "\t" + GetKMsgComplexBodyDesc.header 415 out_string += ( 416 "\n" + prefix_str + "\t" + GetKMsgComplexBodyDesc(kmsgp, prefix_str + "\t") 417 ) 418 419 return out_string + "\n" 420 421 422@header("{: <20s} {: <20s} {: <10s}".format("descriptor", "address", "size")) 423def GetMachMsgOOLDescriptorSummary(desc): 424 """Returns description for mach_msg_ool_descriptor_t * object""" 425 format_string = "{: <#20x} {: <#20x} {:#010x}" 426 out_string = format_string.format(desc, desc.address, desc.size) 427 return out_string 428 429 430def GetKmsgDescriptors(kmsgp): 431 """Get a list of descriptors in a complex message""" 432 kmsghp = GetKmsgHeader(kmsgp) 433 kmsgh = dereference(kmsghp) 434 if not (kmsgh.msgh_bits & 0x80000000): # pragma pylint: disable=superfluous-parens 435 return [] 436 ## Something in the python/lldb types is not getting alignment correct here. 437 ## I'm grabbing a pointer to the body manually, and using tribal knowledge 438 ## of the location of the descriptor count to get this correct 439 body = Cast( 440 addressof(Cast(addressof(kmsgh), "char *")[sizeof(kmsgh)]), "mach_msg_body_t *" 441 ) 442 # dsc_count = body.msgh_descriptor_count 443 dsc_count = dereference(Cast(body, "uint32_t *")) 444 # dschead = Cast(addressof(body[1]), 'mach_msg_descriptor_t *') 445 dschead = Cast( 446 addressof(Cast(addressof(body[0]), "char *")[sizeof("uint32_t")]), 447 "mach_msg_descriptor_t *", 448 ) 449 dsc_list = [] 450 for i in range(dsc_count): 451 dsc_list.append(dschead[i]) 452 return (body, dschead, dsc_list) 453 454 455def GetKmsgTotalDescSize(kmsgp): 456 """Helper to get total descriptor size of a kmsg. 457 Assumes the kmsg has full kernel representation (header and descriptors) 458 params: 459 kmsgp : core.value representing the given ipc_kmsg_t struct 460 returns: 461 Total descriptor size 462 """ 463 kmsghp = GetKmsgHeader(kmsgp) 464 kmsgh = dereference(kmsghp) 465 dsc_count = 0 466 467 if kmsgh.msgh_bits & 0x80000000: # MACH_MSGH_BITS_COMPLEX 468 (body, _, _) = GetKmsgDescriptors(kmsgp) 469 dsc_count = dereference(Cast(body, "uint32_t *")) 470 471 return dsc_count * sizeof("mach_msg_descriptor_t") 472 473 474@header( 475 "{: <20s} {: <20s} {: >6s} {: <20s}".format( 476 "kmsgheader", "body", "descs", "dsc_head" 477 ) 478) 479def GetKMsgComplexBodyDesc(kmsgp, prefix_str=""): 480 """Routine that prints a complex kmsg's body""" 481 kmsghp = GetKmsgHeader(kmsgp) 482 kmsgh = dereference(kmsghp) 483 if not (kmsgh.msgh_bits & 0x80000000): # pragma pylint: disable=superfluous-parens 484 return "" 485 format_string = "{: <#20x} {: <#20x} {:6d} {: <#20x}" 486 out_string = "" 487 488 (body, dschead, dsc_list) = GetKmsgDescriptors(kmsgp) 489 out_string += format_string.format(kmsghp, body, len(dsc_list), dschead) 490 for dsc in dsc_list: 491 try: 492 dsc_type = unsigned(dsc.type.type) 493 out_string += ( 494 "\n" 495 + prefix_str 496 + "Descriptor: " 497 + xnudefines.mach_msg_type_descriptor_strings[dsc_type] 498 ) 499 if dsc_type == 0: 500 # its a port. 501 p = dsc.port.name 502 dstr = GetPortDispositionString(dsc.port.disposition) 503 out_string += " disp:{:s}, name:{: <#20x}".format(dstr, p) 504 elif unsigned(dsc.type.type) in (1, 3): 505 # its OOL DESCRIPTOR or OOL VOLATILE DESCRIPTOR 506 ool = dsc.out_of_line 507 out_string += " " + GetMachMsgOOLDescriptorSummary(addressof(ool)) 508 except Exception: 509 out_string += "\n" + prefix_str + "Invalid Descriptor: {}".format(dsc) 510 return out_string 511 512 513def GetKmsgTrailer(kmsgp): 514 """Helper to get trailer address of a kmsg 515 params: 516 kmsgp : core.value representing the given ipc_kmsg_t struct 517 returns: 518 Trailer address 519 """ 520 kmsghp = GetKmsgHeader(kmsgp) 521 kmsgh = dereference(kmsghp) 522 523 if kmsgp.ikm_type == int( 524 GetEnumValue("ipc_kmsg_type_t", "IKM_TYPE_ALL_INLINED") 525 ) or kmsgp.ikm_type == int(GetEnumValue("ipc_kmsg_type_t", "IKM_TYPE_KDATA_OOL")): 526 return kern.GetValueFromAddress( 527 unsigned(kmsghp) + kmsgh.msgh_size, "mach_msg_max_trailer_t *" 528 ) 529 else: 530 if kmsgh.msgh_bits & 0x80000000: # MACH_MSGH_BITS_COMPLEX 531 content_size = ( 532 kmsgh.msgh_size 533 - sizeof("mach_msg_base_t") 534 - GetKmsgTotalDescSize(kmsgp) 535 ) 536 else: 537 content_size = kmsgh.msgh_size - sizeof("mach_msg_header_t") 538 return kern.GetValueFromAddress( 539 unsigned(kmsgp.ikm_udata) + content_size, "mach_msg_max_trailer_t *" 540 ) 541 542 543def GetKMsgSrc(kmsgp): 544 """Routine that prints a kmsg's source process and pid details 545 params: 546 kmsgp : core.value representing the given ipc_kmsg_t struct 547 returns: 548 str : string containing the name and pid of the kmsg's source proc 549 """ 550 trailer = GetKmsgTrailer(kmsgp) 551 kmsgpid = Cast(trailer, "uint *")[10] # audit_token.val[5] 552 return "{0:s}({1:d})".format(GetProcNameForPid(kmsgpid), kmsgpid) 553 554 555@header( 556 "{:<20s} {:<20s} {:<10s} {:>6s} {:<6s}".format( 557 "portset", "waitqueue", "name", "refs", "flags" 558 ) 559) 560def PrintPortSetSummary(pset, space=0, verbose=True, O=None): 561 """Display summary for a given struct ipc_pset * 562 params: 563 pset : core.value representing a pset in the kernel 564 returns: 565 str : string of summary information for the given pset 566 """ 567 show_kmsg_summary = False 568 if config["verbosity"] > vHUMAN: 569 show_kmsg_summary = True 570 571 ips_wqset = pset.ips_wqset 572 wqs = Waitq(addressof(ips_wqset)) 573 574 local_name = unsigned(ips_wqset.wqset_index) << 8 575 dest = "-" 576 if space: 577 is_tableval, _ = GetSpaceTable(space) 578 if is_tableval: 579 entry_val = GetObjectAtIndexFromArray(is_tableval, local_name >> 8) 580 local_name |= GetGenFromIEBits(unsigned(entry_val.ie_bits)) 581 dest = GetSpaceProcDesc(space) 582 else: 583 for wq in wqs.iterateMembers(): 584 dest = GetSpaceProcDesc(wq.asPort().ip_receiver) 585 586 if unsigned(pset.ips_object.io_state): 587 pass 588 else: 589 pass 590 591 print( 592 "{:<#20x} {:<#20x} {:#010x} {:>6d} {:<6s} {:<20s}".format( 593 unsigned(pset), 594 addressof(pset.ips_wqset), 595 local_name, 596 pset.ips_object.io_references, 597 "ASet", 598 dest, 599 ) 600 ) 601 602 if verbose and wqs.hasThreads(): 603 with O.table("{:<20s} {:<20s}".format("waiter", "event"), indent=True): 604 for thread in wqs.iterateThreads(): 605 print("{:<#20x} {:<#20x}".format(unsigned(thread), thread.wait_event)) 606 print("") 607 608 if verbose and wqs.hasMembers(): 609 with O.table(PrintPortSummary.header, indent=True): 610 for wq in wqs.iterateMembers(): 611 PrintPortSummary(wq.asPort(), show_kmsg_summary=show_kmsg_summary, O=O) 612 print("") 613 614 615# Macro: showipc 616 617 618@lldb_command("showipc") 619def ShowIPC(cmd_args=None): 620 """Routine to print data for the given IPC space 621 Usage: showipc <address of ipc space> 622 """ 623 if cmd_args is None or len(cmd_args) == 0: 624 raise ArgumentError("No arguments passed") 625 626 ipc = kern.GetValueFromAddress(cmd_args[0], "ipc_space *") 627 if not ipc: 628 print("unknown arguments:", str(cmd_args)) 629 return False 630 print(PrintIPCInformation.header) 631 PrintIPCInformation(ipc, False, False) 632 return True 633 634 635# EndMacro: showipc 636 637# Macro: showtaskipc 638 639 640@lldb_command("showtaskipc") 641def ShowTaskIPC(cmd_args=None): 642 """Routine to print IPC summary of given task 643 Usage: showtaskipc <address of task> 644 """ 645 if cmd_args is None or len(cmd_args) == 0: 646 raise ArgumentError("No arguments passed") 647 648 tval = kern.GetValueFromAddress(cmd_args[0], "task *") 649 if not tval: 650 print("unknown arguments:", str(cmd_args)) 651 return False 652 print(GetTaskSummary.header + " " + GetProcSummary.header) 653 pval = GetProcFromTask(tval) 654 print(GetTaskSummary(tval) + " " + GetProcSummary(pval)) 655 print(GetTaskBusyIPCSummary.header) 656 summary, _, _, _ = GetTaskBusyIPCSummary(tval) 657 print(summary) 658 return True 659 660 661# EndMacro: showtaskipc 662 663# Macro: showallipc 664 665 666@lldb_command("showallipc") 667def ShowAllIPC(cmd_args=None): 668 """Routine to print IPC summary of all tasks 669 Usage: showallipc 670 """ 671 for t in kern.tasks: 672 print(GetTaskSummary.header + " " + GetProcSummary.header) 673 pval = GetProcFromTask(t) 674 print(GetTaskSummary(t) + " " + GetProcSummary(pval)) 675 print(PrintIPCInformation.header) 676 PrintIPCInformation(t.itk_space, False, False) 677 print("\n\n") 678 679 680# EndMacro: showallipc 681 682 683@lldb_command("showipcsummary", fancy=True) 684def ShowIPCSummary(cmd_args=None, cmd_options={}, O=None): 685 """Summarizes the IPC state of all tasks. 686 This is a convenient way to dump some basic clues about IPC messaging. You can use the output to determine 687 tasks that are candidates for further investigation. 688 """ 689 with O.table(GetTaskIPCSummary.header): 690 ipc_table_size = 0 691 692 l = [GetTaskIPCSummary(t) for t in kern.tasks] 693 l.sort(key=lambda e: e[1], reverse=True) 694 695 for e in l: 696 print(e[0]) 697 ipc_table_size += e[1] 698 699 for t in kern.terminated_tasks: 700 ipc_table_size += GetTaskIPCSummary(t)[1] 701 702 print("Total Table size: {:d}".format(ipc_table_size)) 703 704 705def GetKObjectFromPort(portval): 706 """Get Kobject description from the port. 707 params: portval - core.value representation of 'ipc_port *' object 708 returns: str - string of kobject information 709 """ 710 if not portval or portval == xnudefines.MACH_PORT_DEAD: 711 return "MACH_PORT_DEAD" 712 713 otype = unsigned(portval.ip_object.io_type) 714 if not IsKObjectType(otype): 715 return "not a kobject" 716 717 kobject_addr = kern.StripKernelPAC(unsigned(portval.ip_kobject)) 718 objtype_str = IPCPortTypeToString(otype) 719 720 if kobject_addr and (objtype_str == 'iokit_object' or objtype_str == 'uext_object' or objtype_str == 'iokit_connect'): 721 iokit_object = CastIOKitClass(portval.ip_kobject, 'IOMachPort *').object 722 desc_str = "{:<#20x}".format(kern.StripKernelPAC(unsigned(iokit_object))) 723 else: 724 desc_str = "{:<#20x}".format(kobject_addr) 725 726 if not kobject_addr: 727 pass 728 729 elif objtype_str == 'iokit_object' or objtype_str == 'uext_object' or objtype_str == 'iokit_connect' : 730 iokit_classnm = GetObjectTypeStr(iokit_object) 731 if not iokit_classnm: 732 desc_str += " <unknown class>" 733 else: 734 desc_str += re.sub(r"vtable for ", r" ", iokit_classnm) 735 736 elif objtype_str[:5] == "task_" and objtype_str != "task_id_token": 737 task = value( 738 portval.GetSBValue() 739 .xCreateValueFromAddress(None, kobject_addr, gettype("struct task")) 740 .AddressOf() 741 ) 742 if GetProcFromTask(task) is not None: 743 desc_str += " {:s}({:d})".format( 744 GetProcNameForTask(task), GetProcPIDForTask(task) 745 ) 746 747 return desc_str 748 749 750def GetSpaceProcDesc(space): 751 """Display the name and pid of a space's task 752 params: 753 space: core.value representing a pointer to a space 754 returns: 755 str : string containing receiver's name and pid 756 """ 757 task = space.is_task 758 if GetProcFromTask(task) is None: 759 return "task {:<#20x}".format(unsigned(task)) 760 return "{:s}({:d})".format(GetProcNameForTask(task), GetProcPIDForTask(task)) 761 762 763def GetPortDestProc(port): 764 """Display the name and pid of a given port's receiver 765 params: 766 port : core.value representing a pointer to a port in the kernel 767 returns: 768 a tuple of: 769 str : the name of that port in its destination (or dead/in-transit) 770 str : the destination/kobject value for this port 771 str : the service name for this port 772 """ 773 774 name = unsigned(port.ip_messages.imq_receiver_name) 775 otype = unsigned(port.ip_object.io_type) 776 dest_str = None 777 state = port.ip_object.io_state 778 779 if state == GetEnumValue("ipc_object_state_t", "IO_STATE_INACTIVE"): 780 name_str = "dead" 781 dest_str = "" 782 elif state == GetEnumValue("ipc_object_state_t", "IO_STATE_IN_LIMBO"): 783 name_str = "in-limbo" 784 dest_str = "" 785 elif state == GetEnumValue("ipc_object_state_t", "IO_STATE_IN_LIMBO_PD"): 786 name_str = "in-limbo-pd" 787 dest_str = "" 788 elif state == GetEnumValue("ipc_object_state_t", "IO_STATE_IN_TRANSIT"): 789 name_str = "in-transit" 790 dest_str = "{:<#20x}".format(port.ip_destination) 791 elif state == GetEnumValue("ipc_object_state_t", "IO_STATE_IN_TRANSIT_PD"): 792 name_str = "in-transit-pd" 793 dest_str = "{:<#20x}".format(port.ip_destination) 794 else: # in-space / in-space immovable 795 if name == 1: # kobjects 796 name_str = "" 797 else: 798 name_str = "{:<#12x}".format(name) 799 800 if IsKObjectType(otype): 801 return (name_str, GetKObjectFromPort(port), "") 802 803 service = "" 804 if port.ip_object.io_type == GetEnumValue("ipc_object_type_t", "IOT_SERVICE_PORT"): 805 try: 806 splabel = GetPortLabel(port.GetSBValue(), "struct ipc_service_port_label") 807 service = splabel.xGetCStringByName("ispl_service_name") 808 except Exception: 809 service = "unknown" 810 811 if dest_str is None: 812 dest_str = GetSpaceProcDesc(port.ip_receiver) 813 814 return (name_str, dest_str, service) 815 816 817@lldb_type_summary(["ipc_entry_t"]) 818@header( 819 "{: <20s} {: <12s} {: <8s} {: <8s} {: <8s} {: <8s} {: <12s} {: <20s} {: <20s}".format( 820 "object", 821 "name", 822 "rite", 823 "urefs", 824 "nsets", 825 "nmsgs", 826 "destname", 827 "kind", 828 "dest/kobject", 829 ) 830) 831def GetIPCEntrySummary(entry, ipc_name="", rights_filter=0): 832 """Get summary of a ipc entry. 833 params: 834 entry - core.value representing ipc_entry_t in the kernel 835 ipc_name - str of format '0x0123' for display in summary. 836 returns: 837 str - string of ipc entry related information 838 839 types of rights: 840 'Dead' : Dead name 841 'Set' : Port set 842 'S' : Send right 843 'R' : Receive right 844 'O' : Send-once right 845 'm' : Immovable send port 846 'i' : Immovable receive port 847 types of notifications: 848 'd' : Dead-Name notification requested 849 's' : Send-Possible notification armed 850 'r' : Send-Possible notification requested 851 'n' : No-Senders notification requested 852 'x' : Port-destroy notification requested 853 """ 854 out_str = "" 855 int(hex(entry), 16) 856 right_str = "" 857 name_str = "" 858 destname_str = "" 859 service_str = "" 860 861 ie_object = entry.ie_object 862 ie_bits = int(entry.ie_bits) 863 urefs = int(ie_bits & 0xFFFF) 864 nsets = 0 865 nmsgs = 0 866 867 if ie_bits & 0x00100000: 868 right_str = "Dead" 869 kind = "" 870 elif ie_bits & 0x00080000: 871 right_str = "Set" 872 kind = "" 873 psetval = kern.CreateTypedPointerFromAddress( 874 unsigned(ie_object), "struct ipc_pset" 875 ) 876 wqs = Waitq(addressof(psetval.ips_wqset)) 877 members = 0 878 for m in wqs.iterateMembers(): 879 members += 1 880 destname_str = "{:d} Members".format(members) 881 else: 882 if ie_bits & 0x00010000: 883 if ie_bits & 0x00020000: 884 # SEND + RECV 885 right_str = "SR" 886 else: 887 # SEND only 888 right_str = "S" 889 elif ie_bits & 0x00020000: 890 # RECV only 891 right_str = "R" 892 elif ie_bits & 0x00040000: 893 # SEND_ONCE 894 right_str = "O" 895 if ie_bits & xnudefines.IE_BITS_IMMOVABLE_SEND: 896 right_str += "m" 897 portval = kern.CreateTypedPointerFromAddress( 898 unsigned(ie_object), "struct ipc_port" 899 ) 900 if int(entry.ie_request) != 0: 901 requestsval, _ = kalloc_array_decode( 902 portval.ip_requests, "struct ipc_port_request" 903 ) 904 sorightval = requestsval[int(entry.ie_request)].ipr_soright 905 soright_ptr = unsigned(sorightval) 906 if soright_ptr != 0: 907 # dead-name notification requested 908 right_str += "d" 909 # send-possible armed 910 if soright_ptr & 0x1: 911 right_str += "s" 912 # send-possible requested 913 if soright_ptr & 0x2: 914 right_str += "r" 915 # No-senders notification requested 916 if not IsKObjectType(ie_object.io_type) and portval.ip_nsrequest != 0: 917 right_str += "n" 918 # port-destroy notification requested 919 if GetPortPDRequest(portval): 920 right_str += "x" 921 # Immovable receive rights 922 if portval.ip_object.io_state == GetEnumValue( 923 "ipc_object_state_t", "IO_STATE_IN_SPACE_IMMOVABLE" 924 ): 925 right_str += "i" 926 # Immovable send rights 927 # Port with SB filtering on 928 if portval.ip_object.io_filtered != 0: 929 right_str += "f" 930 931 # early-out if the rights-filter doesn't match 932 if rights_filter != 0 and rights_filter not in right_str: 933 return "" 934 935 # now show the port destination part 936 name_str, destname_str, service_str = GetPortDestProc(portval) 937 # Get the number of sets to which this port belongs 938 nsets = len([s for s in Waitq(addressof(portval.ip_waitq)).iterateSets()]) 939 nmsgs = portval.ip_messages.imq_msgcount 940 kind = IPCPortTypeToString(ie_object.io_type) 941 942 # append the generation to the name value 943 # (from osfmk/ipc/ipc_entry.h) 944 # bits rollover period 945 # 0 0 64 946 # 0 1 48 947 # 1 0 32 948 # 1 1 16 949 ie_gen_roll = {0: ".64", 1: ".48", 2: ".32", 3: ".16"} 950 ipc_name = "{:s}{:s}".format( 951 ipc_name.strip(), ie_gen_roll[(ie_bits & IE_BITS_ROLL_MASK) >> 24] 952 ) 953 954 if rights_filter == 0 or rights_filter in right_str: 955 format_string = "{: <#20x} {: <12s} {: <8s} {: <8d} {: <8d} {: <8d} {: <12s} {: <20s} {: <20s}" 956 out_str = format_string.format( 957 ie_object, 958 ipc_name, 959 right_str, 960 urefs, 961 nsets, 962 nmsgs, 963 name_str, 964 kind, 965 destname_str + " " + service_str, 966 ) 967 return out_str 968 969 970@header("{0: >20s}".format("user bt")) 971def GetPortUserStack(port, task): 972 """Get UserStack information for the given port & task. 973 params: port - core.value representation of 'ipc_port *' object 974 task - value representing 'task *' object 975 returns: str - string information on port's userstack 976 """ 977 out_str = "" 978 if not port or port == xnudefines.MACH_PORT_DEAD: 979 return out_str 980 pid = port.ip_made_pid 981 proc_val = GetProcFromTask(task) 982 if port.ip_made_bt: 983 btlib = kmemory.BTLibrary.get_shared() 984 out_str += ( 985 "\n".join(btlib.get_stack(port.ip_made_bt).symbolicated_frames()) + "\n" 986 ) 987 if pid != GetProcPID(proc_val): 988 out_str += " ({:<10d})\n".format(pid) 989 return out_str 990 991 992@lldb_type_summary(["ipc_space *"]) 993@header( 994 "{0: <20s} {1: <20s} {2: <20s} {3: <8s} {4: <10s} {5: >8s} {6: <8s}".format( 995 "ipc_space", "is_task", "is_table", "flags", "ports", "low_mod", "high_mod" 996 ) 997) 998def PrintIPCInformation( 999 space, show_entries=False, show_userstack=False, rights_filter=0 1000): 1001 """Provide a summary of the ipc space""" 1002 out_str = "" 1003 format_string = ( 1004 "{0: <#20x} {1: <#20x} {2: <#20x} {3: <8s} {4: <10d} {5: >8d} {6: <8d}" 1005 ) 1006 is_tableval, num_entries = GetSpaceTable(space) 1007 flags = "" 1008 if is_tableval: 1009 flags += "A" 1010 else: 1011 flags += " " 1012 if (space.is_grower) != 0: 1013 flags += "G" 1014 print( 1015 format_string.format( 1016 space, 1017 space.is_task, 1018 is_tableval if is_tableval else 0, 1019 flags, 1020 num_entries, 1021 space.is_low_mod, 1022 space.is_high_mod, 1023 ) 1024 ) 1025 1026 # should show the each individual entries if asked. 1027 if show_entries and is_tableval: 1028 print("\t" + GetIPCEntrySummary.header) 1029 1030 entries = ( 1031 (index, value(iep.AddressOf())) 1032 for index, iep in GetSpaceEntriesWithBits( 1033 is_tableval, num_entries, 0x001F0000 1034 ) 1035 ) 1036 1037 for index, entryval in entries: 1038 entry_ie_bits = unsigned(entryval.ie_bits) 1039 entry_name = "{0: <#20x}".format( 1040 GetNameFromIndexAndIEBits(index, entry_ie_bits) 1041 ) 1042 entry_str = GetIPCEntrySummary(entryval, entry_name, rights_filter) 1043 if not entry_str: 1044 continue 1045 1046 print("\t" + entry_str) 1047 if show_userstack: 1048 entryport = Cast(entryval.ie_object, "ipc_port *") 1049 if ( 1050 entryval.ie_object 1051 and (int(entry_ie_bits) & 0x00070000) 1052 and entryport.ip_made_bt 1053 ): 1054 print( 1055 GetPortUserStack.header 1056 + GetPortUserStack(entryport, space.is_task) 1057 ) 1058 1059 # done with showing entries 1060 return out_str 1061 1062 1063# Macro: showrights 1064 1065 1066@lldb_command("showrights", "R:") 1067def ShowRights(cmd_args=None, cmd_options={}): 1068 """Routine to print rights information for the given IPC space 1069 Usage: showrights [-R rights_type] <address of ipc space> 1070 -R rights_type : only display rights matching the string 'rights_type' 1071 1072 types of rights: 1073 'Dead' : Dead name 1074 'Set' : Port set 1075 'S' : Send right 1076 'R' : Receive right 1077 'O' : Send-once right 1078 types of notifications: 1079 'd' : Dead-Name notification requested 1080 's' : Send-Possible notification armed 1081 'r' : Send-Possible notification requested 1082 'n' : No-Senders notification requested 1083 'x' : Port-destroy notification requested 1084 """ 1085 if cmd_args is None or len(cmd_args) == 0: 1086 raise ArgumentError("No arguments passed") 1087 1088 ipc = kern.GetValueFromAddress(cmd_args[0], "ipc_space *") 1089 if not ipc: 1090 print("unknown arguments:", str(cmd_args)) 1091 return False 1092 rights_type = 0 1093 if "-R" in cmd_options: 1094 rights_type = cmd_options["-R"] 1095 print(PrintIPCInformation.header) 1096 PrintIPCInformation(ipc, True, False, rights_type) 1097 1098 1099# EndMacro: showrights 1100 1101 1102@lldb_command("showtaskrights", "R:") 1103def ShowTaskRights(cmd_args=None, cmd_options={}): 1104 """Routine to ipc rights information for a task 1105 Usage: showtaskrights [-R rights_type] <task address> 1106 -R rights_type : only display rights matching the string 'rights_type' 1107 1108 types of rights: 1109 'Dead' : Dead name 1110 'Set' : Port set 1111 'S' : Send right 1112 'R' : Receive right 1113 'O' : Send-once right 1114 'm' : Immovable send port 1115 'i' : Immovable receive port 1116 'f' : Port with SB filtering on 1117 types of notifications: 1118 'd' : Dead-Name notification requested 1119 's' : Send-Possible notification armed 1120 'r' : Send-Possible notification requested 1121 'n' : No-Senders notification requested 1122 'x' : Port-destroy notification requested 1123 """ 1124 if cmd_args is None or len(cmd_args) == 0: 1125 raise ArgumentError("No arguments passed") 1126 1127 tval = kern.GetValueFromAddress(cmd_args[0], "task *") 1128 if not tval: 1129 print("unknown arguments:", str(cmd_args)) 1130 return False 1131 rights_type = 0 1132 if "-R" in cmd_options: 1133 rights_type = cmd_options["-R"] 1134 print(GetTaskSummary.header + " " + GetProcSummary.header) 1135 pval = GetProcFromTask(tval) 1136 print(GetTaskSummary(tval) + " " + GetProcSummary(pval)) 1137 print(PrintIPCInformation.header) 1138 PrintIPCInformation(tval.itk_space, True, False, rights_type) 1139 1140 1141# Count the vouchers in a given task's ipc space 1142@header("{: <20s} {: <6s} {: <20s} {: <8s}".format("task", "pid", "name", "#vouchers")) 1143def GetTaskVoucherCount(t): 1144 is_tableval, num_entries = GetSpaceTable(t.itk_space) 1145 count = 0 1146 voucher_kotype = int(GetEnumValue("ipc_object_type_t", "IKOT_VOUCHER")) 1147 1148 if is_tableval: 1149 ports = GetSpaceObjectsWithBits( 1150 is_tableval, num_entries, 0x00070000, gettype("struct ipc_port") 1151 ) 1152 1153 for port in ports: 1154 if port.xGetIntegerByPath(".ip_object.io_type") == voucher_kotype: 1155 count += 1 1156 1157 format_str = "{: <#20x} {: <6d} {: <20s} {: <8d}" 1158 pval = GetProcFromTask(t) 1159 return format_str.format(t, GetProcPID(pval), GetProcNameForTask(t), count) 1160 1161 1162# Macro: countallvouchers 1163@lldb_command("countallvouchers", fancy=True) 1164def CountAllVouchers(cmd_args=None, cmd_options={}, O=None): 1165 """Routine to count the number of vouchers by task. Useful for finding leaks. 1166 Usage: countallvouchers 1167 """ 1168 1169 with O.table(GetTaskVoucherCount.header): 1170 for t in kern.tasks: 1171 print(GetTaskVoucherCount(t)) 1172 1173 1174# Macro: showataskrightsbt 1175 1176 1177@lldb_command("showtaskrightsbt", "R:") 1178def ShowTaskRightsBt(cmd_args=None, cmd_options={}): 1179 """Routine to ipc rights information with userstacks for a task 1180 Usage: showtaskrightsbt [-R rights_type] <task address> 1181 -R rights_type : only display rights matching the string 'rights_type' 1182 1183 types of rights: 1184 'Dead' : Dead name 1185 'Set' : Port set 1186 'S' : Send right 1187 'R' : Receive right 1188 'O' : Send-once right 1189 'm' : Immovable send port 1190 'i' : Immovable receive port 1191 types of notifications: 1192 'd' : Dead-Name notification requested 1193 's' : Send-Possible notification armed 1194 'r' : Send-Possible notification requested 1195 'n' : No-Senders notification requested 1196 'x' : Port-destroy notification requested 1197 """ 1198 if cmd_args is None or len(cmd_args) == 0: 1199 raise ArgumentError("No arguments passed") 1200 1201 tval = kern.GetValueFromAddress(cmd_args[0], "task *") 1202 if not tval: 1203 print("unknown arguments:", str(cmd_args)) 1204 return False 1205 rights_type = 0 1206 if "-R" in cmd_options: 1207 rights_type = cmd_options["-R"] 1208 print(GetTaskSummary.header + " " + GetProcSummary.header) 1209 pval = GetProcFromTask(tval) 1210 print(GetTaskSummary(tval) + " " + GetProcSummary(pval)) 1211 print(PrintIPCInformation.header) 1212 PrintIPCInformation(tval.itk_space, True, True, rights_type) 1213 1214 1215# EndMacro: showtaskrightsbt 1216 1217# Macro: showallrights 1218 1219 1220@lldb_command("showallrights", "R:") 1221def ShowAllRights(cmd_args=None, cmd_options={}): 1222 """Routine to print rights information for IPC space of all tasks 1223 Usage: showallrights [-R rights_type] 1224 -R rights_type : only display rights matching the string 'rights_type' 1225 1226 types of rights: 1227 'Dead' : Dead name 1228 'Set' : Port set 1229 'S' : Send right 1230 'R' : Receive right 1231 'O' : Send-once right 1232 'm' : Immovable send port 1233 'i' : Immovable receive port 1234 types of notifications: 1235 'd' : Dead-Name notification requested 1236 's' : Send-Possible notification armed 1237 'r' : Send-Possible notification requested 1238 'n' : No-Senders notification requested 1239 'x' : Port-destroy notification requested 1240 """ 1241 rights_type = 0 1242 if "-R" in cmd_options: 1243 rights_type = cmd_options["-R"] 1244 for t in kern.tasks: 1245 print(GetTaskSummary.header + " " + GetProcSummary.header) 1246 pval = GetProcFromTask(t) 1247 print(GetTaskSummary(t) + " " + GetProcSummary(pval)) 1248 try: 1249 print(PrintIPCInformation.header) 1250 PrintIPCInformation(t.itk_space, True, False, rights_type) + "\n\n" 1251 except (KeyboardInterrupt, SystemExit): 1252 raise 1253 except Exception: 1254 print( 1255 "Failed to get IPC information. Do individual showtaskrights <task> to find the error. \n\n" 1256 ) 1257 1258 1259# EndMacro: showallrights 1260 1261 1262def GetInTransitPortSummary(port, disp, holding_port, holding_kmsg): 1263 """String-ify the in-transit dispostion of a port.""" 1264 ## This should match the summary generated by GetIPCEntrySummary 1265 ## "object" "name" "rite" "urefs" "nsets" "nmsgs" "destname" "destination" 1266 format_str = ( 1267 "\t{: <#20x} {: <12} {: <8s} {: <8d} {: <8d} {: <8d} p:{: <#19x} k:{: <#19x}" 1268 ) 1269 1270 disp_str = GetPortDispositionString(disp) 1271 1272 out_str = format_str.format( 1273 unsigned(port), 1274 "in-transit", 1275 disp_str, 1276 0, 1277 0, 1278 port.ip_messages.imq_msgcount, 1279 unsigned(holding_port), 1280 unsigned(holding_kmsg), 1281 ) 1282 return out_str 1283 1284 1285def GetDispositionFromEntryType(entry_bits): 1286 """Translate an IPC entry type into an in-transit disposition. This allows 1287 the GetInTransitPortSummary function to be re-used to string-ify IPC 1288 entry types. 1289 """ 1290 ebits = int(entry_bits) 1291 if (ebits & 0x003F0000) == 0: 1292 return 0 1293 1294 if (ebits & 0x00010000) != 0: 1295 return 17 ## MACH_PORT_RIGHT_SEND 1296 elif (ebits & 0x00020000) != 0: 1297 return 16 ## MACH_PORT_RIGHT_RECEIVE 1298 elif (ebits & 0x00040000) != 0: 1299 return 18 ## MACH_PORT_RIGHT_SEND_ONCE 1300 elif (ebits & 0x00080000) != 0: 1301 return 100 ## MACH_PORT_RIGHT_PORT_SET 1302 elif (ebits & 0x00100000) != 0: 1303 return 101 ## MACH_PORT_RIGHT_DEAD_NAME 1304 elif (ebits & 0x00200000) != 0: 1305 return 102 ## MACH_PORT_RIGHT_LABELH 1306 else: 1307 return 0 1308 1309 1310def GetDispositionFromVoucherPort(th_vport): 1311 """Translate a thread's voucher port into a 'disposition'""" 1312 if unsigned(th_vport) > 0: 1313 return 103 ## Voucher type 1314 return 0 1315 1316 1317g_kmsg_prog = 0 1318g_progmeter = { 1319 0: "*", 1320 1: "-", 1321 2: "\\", 1322 3: "|", 1323 4: "/", 1324 5: "-", 1325 6: "\\", 1326 7: "|", 1327 8: "/", 1328} 1329 1330 1331def PrintProgressForKmsg(): 1332 global g_kmsg_prog 1333 global g_progmeter 1334 sys.stderr.write(" {:<1s}\r".format(g_progmeter[g_kmsg_prog % 9])) 1335 g_kmsg_prog += 1 1336 1337 1338def CollectPortsForAnalysis(port, disposition): 1339 """ """ 1340 if not port or port == xnudefines.MACH_PORT_DEAD: 1341 return 1342 p = Cast(port, "struct ipc_port *") 1343 yield (p, disposition) 1344 1345 # no-senders notification port 1346 if not IsKObjectType(p.ip_object.io_type) and p.ip_nsrequest != 0: 1347 PrintProgressForKmsg() 1348 yield (p.ip_nsrequest, -1) 1349 1350 # port-death notification port 1351 pdrequest = GetPortPDRequest(p) 1352 if pdrequest: 1353 PrintProgressForKmsg() 1354 yield (pdrequest, -2) 1355 1356 ## ports can have many send-possible notifications armed: go through the table! 1357 if unsigned(p.ip_requests) != 0: 1358 table, table_sz = kalloc_array_decode(p.ip_requests, "struct ipc_port_request") 1359 for i in range(table_sz): 1360 if i == 0: 1361 continue 1362 ipr = table[i] 1363 if unsigned(ipr.ipr_name) in (0, 0xFFFFFFFE): 1364 # 0xfffffffe is a host notify request 1365 continue 1366 ipr_bits = unsigned(ipr.ipr_soright) & 3 1367 ipr_port = kern.GetValueFromAddress( 1368 int(ipr.ipr_soright) & ~3, "struct ipc_port *" 1369 ) 1370 # skip unused entries in the ipc table to avoid null dereferences 1371 if not ipr_port: 1372 continue 1373 ipr_disp = 0 1374 if ipr_bits & 3: ## send-possible armed and requested 1375 ipr_disp = -5 1376 elif ipr_bits & 2: ## send-possible requested 1377 ipr_disp = -4 1378 elif ipr_bits & 1: ## send-possible armed 1379 ipr_disp = -3 1380 PrintProgressForKmsg() 1381 yield (ipr_port, ipr_disp) 1382 return 1383 1384 1385def CollectKmsgPorts(task, task_port, kmsgp): 1386 """Look through a message, 'kmsgp' destined for 'task' 1387 (enqueued on task_port). Collect any port descriptors, 1388 remote, local, voucher, or other port references 1389 into a (ipc_port_t, disposition) list. 1390 """ 1391 kmsgh = dereference(GetKmsgHeader(kmsgp)) 1392 1393 p_list = [] 1394 1395 PrintProgressForKmsg() 1396 if kmsgh.msgh_remote_port and unsigned(kmsgh.msgh_remote_port) != unsigned( 1397 task_port 1398 ): 1399 disp = kmsgh.msgh_bits & 0x1F 1400 p_list += list(CollectPortsForAnalysis(kmsgh.msgh_remote_port, disp)) 1401 1402 if ( 1403 kmsgh.msgh_local_port 1404 and unsigned(kmsgh.msgh_local_port) != unsigned(task_port) 1405 and unsigned(kmsgh.msgh_local_port) != unsigned(kmsgh.msgh_remote_port) 1406 ): 1407 disp = (kmsgh.msgh_bits & 0x1F00) >> 8 1408 p_list += list(CollectPortsForAnalysis(kmsgh.msgh_local_port, disp)) 1409 1410 if kmsgp.ikm_voucher_port: 1411 p_list += list(CollectPortsForAnalysis(kmsgp.ikm_voucher_port, 0)) 1412 1413 if kmsgh.msgh_bits & 0x80000000: 1414 ## Complex message - look for descriptors 1415 PrintProgressForKmsg() 1416 (body, dschead, dsc_list) = GetKmsgDescriptors(kmsgp) 1417 for dsc in dsc_list: 1418 PrintProgressForKmsg() 1419 dsc_type = unsigned(dsc.type.type) 1420 if dsc_type == 0 or dsc_type == 2: ## 0 == port, 2 == ool port 1421 if dsc_type == 0: 1422 ## its a port descriptor 1423 dsc_disp = dsc.port.disposition 1424 p_list += list(CollectPortsForAnalysis(dsc.port.name, dsc_disp)) 1425 else: 1426 ## it's an ool_ports descriptor which is an array of ports 1427 dsc_disp = dsc.ool_ports.disposition 1428 dispdata = Cast(dsc.ool_ports.address, "struct ipc_port *") 1429 for pidx in range(dsc.ool_ports.count): 1430 PrintProgressForKmsg() 1431 p_list += list( 1432 CollectPortsForAnalysis(dispdata[pidx], dsc_disp) 1433 ) 1434 return p_list 1435 1436 1437def CollectKmsgPortRefs(task, task_port, kmsgp, p_refs): 1438 """Recursively collect all references to ports inside the kmsg 'kmsgp' 1439 into the set 'p_refs' 1440 """ 1441 p_list = CollectKmsgPorts(task, task_port, kmsgp) 1442 1443 ## Iterate over each ports we've collected, to see if they 1444 ## have messages on them, and then recurse! 1445 for p, pdisp in p_list: 1446 ptype = p.ip_object.io_type 1447 p_refs.add((p, pdisp, ptype)) 1448 if IsPortSetType(ptype): 1449 continue 1450 ## If the port that's in-transit has messages already enqueued, 1451 ## go through each of those messages and look for more ports! 1452 for p_kmsgp in IterateCircleQueue( 1453 p.ip_messages.imq_messages, "ipc_kmsg", "ikm_link" 1454 ): 1455 CollectKmsgPortRefs(task, p, p_kmsgp, p_refs) 1456 1457 1458def FindKmsgPortRefs(instr, task, task_port, kmsgp, qport): 1459 """Look through a message, 'kmsgp' destined for 'task'. If we find 1460 any port descriptors, remote, local, voucher, or other port that 1461 matches 'qport', return a short description 1462 which should match the format of GetIPCEntrySummary. 1463 """ 1464 1465 out_str = instr 1466 p_list = CollectKmsgPorts(task, task_port, kmsgp) 1467 1468 ## Run through all ports we've collected looking for 'qport' 1469 for p, pdisp in p_list: 1470 PrintProgressForKmsg() 1471 if unsigned(p) == unsigned(qport): 1472 ## the port we're looking for was found in this message! 1473 if len(out_str) > 0: 1474 out_str += "\n" 1475 out_str += GetInTransitPortSummary(p, pdisp, task_port, kmsgp) 1476 1477 ptype = p.ip_object.io_type 1478 if IsPortSetType(ptype): 1479 continue 1480 1481 ## If the port that's in-transit has messages already enqueued, 1482 ## go through each of those messages and look for more ports! 1483 for p_kmsgp in IterateCircleQueue( 1484 p.ip_messages.imq_messages, "ipc_kmsg", "ikm_link" 1485 ): 1486 out_str = FindKmsgPortRefs(out_str, task, p, p_kmsgp, qport) 1487 1488 return out_str 1489 1490 1491port_iteration_do_print_taskname = False 1492registeredport_idx = -10 1493excports_idx = -20 1494intransit_idx = -1000 1495taskports_idx = -2000 1496thports_idx = -3000 1497 1498 1499def IterateAllPorts(tasklist, func, ctx, include_psets, follow_busyports, should_log): 1500 """Iterate over all ports in the system, calling 'func' 1501 for each entry in 1502 """ 1503 global port_iteration_do_print_taskname 1504 global intransit_idx, taskports_idx, thports_idx, registeredport_idx, excports_idx 1505 1506 ## XXX: also host special ports 1507 1508 entry_port_type_mask = 0x00070000 1509 if include_psets: 1510 entry_port_type_mask = 0x000F0000 1511 1512 if tasklist is None: 1513 tasklist = list(kern.tasks) 1514 tasklist += list(kern.terminated_tasks) 1515 1516 tidx = 1 1517 1518 for t in tasklist: 1519 # Write a progress line. Using stderr avoids automatic newline when 1520 # writing to stdout from lldb. Blank spaces at the end clear out long 1521 # lines. 1522 if should_log: 1523 procname = "" 1524 if not t.active: 1525 procname = "terminated: " 1526 if t.halting: 1527 procname += "halting: " 1528 procname += GetProcNameForTask(t) 1529 sys.stderr.write( 1530 " checking {:s} ({}/{})...{:50s}\r".format( 1531 procname, tidx, len(tasklist), "" 1532 ) 1533 ) 1534 tidx += 1 1535 1536 port_iteration_do_print_taskname = True 1537 space = t.itk_space 1538 is_tableval, num_entries = GetSpaceTable(space) 1539 1540 if not is_tableval: 1541 continue 1542 1543 base = is_tableval.GetSBValue().Dereference() 1544 entries = (value(iep.AddressOf()) for iep in base.xIterSiblings(1, num_entries)) 1545 1546 for idx, entry_val in enumerate(entries, 1): 1547 entry_bits = unsigned(entry_val.ie_bits) 1548 "{:x}".format(GetNameFromIndexAndIEBits(idx, entry_bits)) 1549 1550 entry_disp = GetDispositionFromEntryType(entry_bits) 1551 1552 ## If the entry in the table represents a port of some sort, 1553 ## then make the callback provided 1554 if int(entry_bits) & entry_port_type_mask: 1555 eport = kern.CreateTypedPointerFromAddress( 1556 unsigned(entry_val.ie_object), "struct ipc_port" 1557 ) 1558 ## Make the callback 1559 func(t, space, ctx, idx, entry_val, eport, entry_disp) 1560 1561 ## if the port has pending messages, look through 1562 ## each message for ports (and recurse) 1563 if ( 1564 follow_busyports 1565 and unsigned(eport) > 0 1566 and eport.ip_messages.imq_msgcount > 0 1567 ): 1568 ## collect all port references from all messages 1569 for kmsgp in IterateCircleQueue( 1570 eport.ip_messages.imq_messages, "ipc_kmsg", "ikm_link" 1571 ): 1572 p_refs = set() 1573 CollectKmsgPortRefs(t, eport, kmsgp, p_refs) 1574 for port, pdisp, ptype in p_refs: 1575 func(t, space, ctx, intransit_idx, None, port, pdisp) 1576 ## for idx in xrange(1, num_entries) 1577 1578 ## Task ports (send rights) 1579 if getattr(t, "itk_settable_self", 0) > 0: 1580 func(t, space, ctx, taskports_idx, 0, t.itk_settable_self, 17) 1581 if unsigned(t.itk_host) > 0: 1582 func(t, space, ctx, taskports_idx, 0, t.itk_host, 17) 1583 if unsigned(t.itk_bootstrap) > 0: 1584 func(t, space, ctx, taskports_idx, 0, t.itk_bootstrap, 17) 1585 if unsigned(t.itk_debug_control) > 0: 1586 func(t, space, ctx, taskports_idx, 0, t.itk_debug_control, 17) 1587 if unsigned(t.itk_task_access) > 0: 1588 func(t, space, ctx, taskports_idx, 0, t.itk_task_access, 17) 1589 if unsigned(t.itk_task_ports[TASK_FLAVOR_READ]) > 0: ## task read port 1590 func(t, space, ctx, taskports_idx, 0, t.itk_task_ports[TASK_FLAVOR_READ], 17) 1591 if unsigned(t.itk_task_ports[TASK_FLAVOR_INSPECT]) > 0: ## task inspect port 1592 func(t, space, ctx, taskports_idx, 0, t.itk_task_ports[TASK_FLAVOR_INSPECT], 17) 1593 1594 ## Task name port (not a send right, just a naked ref); TASK_FLAVOR_NAME = 3 1595 if unsigned(t.itk_task_ports[TASK_FLAVOR_NAME]) > 0: 1596 func(t, space, ctx, taskports_idx, 0, t.itk_task_ports[TASK_FLAVOR_NAME], 0) 1597 1598 ## task resume port is a receive right to resume the task 1599 if unsigned(t.itk_resume) > 0: 1600 func(t, space, ctx, taskports_idx, 0, t.itk_resume, 16) 1601 1602 ## registered task ports (all send rights) 1603 tr_idx = 0 1604 tr_max = sizeof(t.itk_registered) // sizeof(t.itk_registered[0]) 1605 while tr_idx < tr_max: 1606 tport = t.itk_registered[tr_idx] 1607 if unsigned(tport) > 0: 1608 try: 1609 func(t, space, ctx, registeredport_idx, 0, tport, 17) 1610 except Exception: 1611 print( 1612 "\texception looking through registered port {:d}/{:d} in {:s}".format( 1613 tr_idx, tr_max, t 1614 ) 1615 ) 1616 pass 1617 tr_idx += 1 1618 1619 ## Task exception ports 1620 exidx = 0 1621 exmax = sizeof(t.exc_actions) // sizeof(t.exc_actions[0]) 1622 while exidx < exmax: ## see: osfmk/mach/[arm|i386]/exception.h 1623 export = t.exc_actions[exidx].port ## send right 1624 if unsigned(export) > 0: 1625 try: 1626 func(t, space, ctx, excports_idx, 0, export, 17) 1627 except Exception: 1628 print( 1629 "\texception looking through exception port {:d}/{:d} in {:s}".format( 1630 exidx, exmax, t 1631 ) 1632 ) 1633 pass 1634 exidx += 1 1635 1636 ## XXX: any ports still valid after clearing IPC space?! 1637 1638 for thval in IterateQueue(t.threads, "thread *", "task_threads"): 1639 ## XXX: look at block reason to see if it's in mach_msg_receive - then look at saved state / message 1640 1641 ## Thread port (send right) 1642 if getattr(thval.t_tro, "tro_settable_self_port", 0) > 0: 1643 thport = thval.t_tro.tro_settable_self_port 1644 func( 1645 t, space, ctx, thports_idx, 0, thport, 17 1646 ) ## see: osfmk/mach/message.h 1647 ## Thread special reply port (send-once right) 1648 if unsigned(thval.ith_special_reply_port) > 0: 1649 thport = thval.ith_special_reply_port 1650 func( 1651 t, space, ctx, thports_idx, 0, thport, 18 1652 ) ## see: osfmk/mach/message.h 1653 ## Thread voucher port 1654 if unsigned(thval.ith_voucher) > 0: 1655 vport = thval.ith_voucher.iv_port 1656 if unsigned(vport) > 0: 1657 vdisp = GetDispositionFromVoucherPort(vport) 1658 func(t, space, ctx, thports_idx, 0, vport, vdisp) 1659 ## Thread exception ports 1660 if unsigned(thval.t_tro.tro_exc_actions) > 0: 1661 exidx = 0 1662 while exidx < exmax: ## see: osfmk/mach/[arm|i386]/exception.h 1663 export = thval.t_tro.tro_exc_actions[exidx].port ## send right 1664 if unsigned(export) > 0: 1665 try: 1666 func(t, space, ctx, excports_idx, 0, export, 17) 1667 except Exception: 1668 print( 1669 "\texception looking through exception port {:d}/{:d} in {:s}".format( 1670 exidx, exmax, t 1671 ) 1672 ) 1673 pass 1674 exidx += 1 1675 ## XXX: the message on a thread (that's currently being received) 1676 ## for (thval in t.threads) 1677 ## for (t in tasklist) 1678 1679 1680# Macro: findportrights 1681def FindPortRightsCallback(task, space, ctx, entry_idx, ipc_entry, ipc_port, port_disp): 1682 """Callback which uses 'ctx' as the (port,rights_types) tuple for which 1683 a caller is seeking references. This should *not* be used from a 1684 recursive call to IterateAllPorts. 1685 """ 1686 global port_iteration_do_print_taskname 1687 1688 (qport, rights_type) = ctx 1689 entry_name = "" 1690 entry_str = "" 1691 if unsigned(ipc_entry) != 0: 1692 entry_bits = unsigned(ipc_entry.ie_bits) 1693 entry_name = "{:x}".format(GetNameFromIndexAndIEBits(entry_idx, entry_bits)) 1694 if (int(entry_bits) & 0x001F0000) != 0 and unsigned( 1695 ipc_entry.ie_object 1696 ) == unsigned(qport): 1697 ## it's a valid entry, and it points to the port 1698 entry_str = "\t" + GetIPCEntrySummary(ipc_entry, entry_name, rights_type) 1699 1700 procname = GetProcNameForTask(task) 1701 if ( 1702 ipc_port 1703 and ipc_port != xnudefines.MACH_PORT_DEAD 1704 and ipc_port.ip_messages.imq_msgcount > 0 1705 ): 1706 sys.stderr.write( 1707 " checking {:s} busy-port {}:{:#x}...{:30s}\r".format( 1708 procname, entry_name, unsigned(ipc_port), "" 1709 ) 1710 ) 1711 ## Search through busy ports to find descriptors which could 1712 ## contain the only reference to this port! 1713 for kmsgp in IterateCircleQueue( 1714 ipc_port.ip_messages.imq_messages, "ipc_kmsg", "ikm_link" 1715 ): 1716 entry_str = FindKmsgPortRefs(entry_str, task, ipc_port, kmsgp, qport) 1717 1718 if len(entry_str) > 0: 1719 sys.stderr.write("{:80s}\r".format("")) 1720 if port_iteration_do_print_taskname: 1721 print("Task: {0: <#x} {1: <s}".format(task, procname)) 1722 print("\t" + GetIPCEntrySummary.header) 1723 port_iteration_do_print_taskname = False 1724 print(entry_str) 1725 1726 1727@lldb_command("findportrights", "R:S:") 1728def FindPortRights(cmd_args=None, cmd_options={}): 1729 """Routine to locate and print all extant rights to a given port 1730 Usage: findportrights [-R rights_type] [-S <ipc_space_t>] <ipc_port_t> 1731 -S ipc_space : only search the specified ipc space 1732 -R rights_type : only display rights matching the string 'rights_type' 1733 1734 types of rights: 1735 'Dead' : Dead name 1736 'Set' : Port set 1737 'S' : Send right 1738 'R' : Receive right 1739 'O' : Send-once right 1740 types of notifications: 1741 'd' : Dead-Name notification requested 1742 's' : Send-Possible notification armed 1743 'r' : Send-Possible notification requested 1744 'n' : No-Senders notification requested 1745 'x' : Port-destroy notification requested 1746 """ 1747 if cmd_args is None or len(cmd_args) == 0: 1748 raise ArgumentError("no port address provided") 1749 port = kern.GetValueFromAddress(cmd_args[0], "struct ipc_port *") 1750 1751 rights_type = 0 1752 if "-R" in cmd_options: 1753 rights_type = cmd_options["-R"] 1754 1755 tasklist = None 1756 if "-S" in cmd_options: 1757 space = kern.GetValueFromAddress(cmd_options["-S"], "struct ipc_space *") 1758 tasklist = [space.is_task] 1759 1760 ## Don't include port sets 1761 ## Don't recurse on busy ports (we do that manually) 1762 ## DO log progress 1763 IterateAllPorts( 1764 tasklist, FindPortRightsCallback, (port, rights_type), False, False, True 1765 ) 1766 sys.stderr.write("{:120s}\r".format(" ")) 1767 1768 print("Done.") 1769 1770 1771# EndMacro: findportrights 1772 1773# Macro: countallports 1774 1775 1776def CountPortsCallback(task, space, ctx, entry_idx, ipc_entry, ipc_port, port_disp): 1777 """Callback which uses 'ctx' as the set of all ports found in the 1778 iteration. This should *not* be used from a recursive 1779 call to IterateAllPorts. 1780 """ 1781 global intransit_idx 1782 1783 (p_set, p_intransit, p_bytask) = ctx 1784 1785 ## Add the port address to the set of all port addresses 1786 ipc_port_addr = unsigned(ipc_port) 1787 p_set.add(ipc_port_addr) 1788 1789 if entry_idx == intransit_idx: 1790 p_intransit.add(ipc_port_addr) 1791 1792 if task.active or (task.halting and not task.active): 1793 if task not in p_bytask: 1794 p_bytask[task] = {"transit": 0, "table": 0, "other": 0} 1795 if entry_idx == intransit_idx: 1796 p_bytask[task]["transit"] += 1 1797 elif entry_idx >= 0: 1798 p_bytask[task]["table"] += 1 1799 else: 1800 p_bytask[task]["other"] += 1 1801 1802 1803@header(f"{'#ports': <10s} {'in transit': <10s} {'Special': <10s}") 1804@lldb_command("countallports", "P", fancy=True) 1805def CountAllPorts(cmd_args=None, cmd_options={}, O=None): 1806 """Routine to search for all as many references to ipc_port structures in the kernel 1807 that we can find. 1808 Usage: countallports [-P] 1809 -P : include port sets in the count (default: NO) 1810 """ 1811 p_set = set() 1812 p_intransit = set() 1813 p_bytask = {} 1814 1815 find_psets = False 1816 if "-P" in cmd_options: 1817 find_psets = True 1818 1819 ## optionally include port sets 1820 ## DO recurse on busy ports 1821 ## DO log progress 1822 IterateAllPorts( 1823 None, CountPortsCallback, (p_set, p_intransit, p_bytask), find_psets, True, True 1824 ) 1825 sys.stderr.write(f"{' ':120s}\r") 1826 1827 # sort by ipc table size 1828 with O.table(GetTaskIPCSummary.header + " " + CountAllPorts.header): 1829 for task, port_summary in sorted( 1830 p_bytask.items(), key=lambda item: item[1]["table"], reverse=True 1831 ): 1832 outstring, _ = GetTaskIPCSummary(task) 1833 outstring += f" {port_summary['table']: <10d} {port_summary['transit']: <10d} {port_summary['other']: <10d}" 1834 print(outstring) 1835 1836 print(f"\nTotal ports found: {len(p_set)}") 1837 print(f"Number of ports In Transit: {len(p_intransit)}") 1838 1839 1840# EndMacro: countallports 1841# Macro: showpipestats 1842 1843 1844@lldb_command("showpipestats") 1845def ShowPipeStats(cmd_args=None): 1846 """Display pipes usage information in the kernel""" 1847 print("Number of pipes: {: d}".format(kern.globals.amountpipes)) 1848 print( 1849 "Memory used by pipes: {:s}".format(sizeof_fmt(int(kern.globals.amountpipekva))) 1850 ) 1851 print( 1852 "Max memory allowed for pipes: {:s}".format( 1853 sizeof_fmt(int(kern.globals.maxpipekva)) 1854 ) 1855 ) 1856 1857 1858# EndMacro: showpipestats 1859# Macro: showtaskbusyports 1860 1861 1862@lldb_command("showtaskbusyports", fancy=True) 1863def ShowTaskBusyPorts(cmd_args=None, cmd_options={}, O=None): 1864 """Routine to print information about receive rights belonging to this task that 1865 have enqueued messages. This is often a sign of a blocked or hung process 1866 Usage: showtaskbusyports <task address> 1867 """ 1868 if cmd_args is None or len(cmd_args) == 0: 1869 raise ArgumentError("No arguments passed. Please pass in the address of a task") 1870 1871 task = kern.GetValueFromAddress(cmd_args[0], "task_t") 1872 is_tableval, num_entries = GetSpaceTable(task.itk_space) 1873 1874 if is_tableval: 1875 ports = GetSpaceObjectsWithBits( 1876 is_tableval, num_entries, 0x00020000, gettype("struct ipc_port") 1877 ) 1878 1879 with O.table(PrintPortSummary.header): 1880 for port in ports: 1881 if port.xGetIntegerByPath(".ip_messages.imq_msgcount"): 1882 PrintPortSummary(value(port.AddressOf()), O=O) 1883 1884 1885# EndMacro: showtaskbusyports 1886# Macro: showallbusyports 1887 1888 1889@lldb_command("showallbusyports", fancy=True) 1890def ShowAllBusyPorts(cmd_args=None, cmd_options={}, O=None): 1891 """Routine to print information about all receive rights on the system that 1892 have enqueued messages. 1893 """ 1894 with O.table(PrintPortSummary.header): 1895 port_ty = gettype("struct ipc_port") 1896 for port in kmemory.Zone("ipc ports").iter_allocated(port_ty): 1897 if port.xGetIntegerByPath(".ip_messages.imq_msgcount") > 0: 1898 PrintPortSummary(value(port.AddressOf()), O=O) 1899 1900 1901# EndMacro: showallbusyports 1902# Macro: showallports 1903 1904 1905@lldb_command("showallports", fancy=True) 1906def ShowAllPorts(cmd_args=None, cmd_options={}, O=None): 1907 """Routine to print information about all allocated ports in the system 1908 1909 usage: showallports 1910 """ 1911 with O.table(PrintPortSummary.header): 1912 port_ty = gettype("struct ipc_port") 1913 for port in kmemory.Zone("ipc ports").iter_allocated(port_ty): 1914 PrintPortSummary(value(port.AddressOf()), show_kmsg_summary=False, O=O) 1915 1916 1917# EndMacro: showallports 1918# Macro: findkobjectport 1919 1920 1921@lldb_command("findkobjectport", fancy=True) 1922def FindKobjectPort(cmd_args=None, cmd_options={}, O=None): 1923 """Locate all ports pointing to a given kobject 1924 1925 usage: findkobjectport <kobject-addr> 1926 """ 1927 if cmd_args is None or len(cmd_args) == 0: 1928 raise ArgumentError() 1929 1930 kobj_addr = unsigned(kern.GetValueFromAddress(cmd_args[0])) 1931 kmem = kmemory.KMem.get_shared() 1932 port_ty = gettype("struct ipc_port") 1933 1934 with O.table(PrintPortSummary.header): 1935 for port in kmemory.Zone("ipc ports").iter_allocated(port_ty): 1936 otype = port.xGetIntegerByPath(".ip_object.io_type") 1937 if not IsKObjectType(otype): 1938 continue 1939 1940 ip_kobject = kmem.make_address(port.xGetScalarByName("ip_kobject")) 1941 if ip_kobject == kobj_addr: 1942 PrintPortSummary(value(port.AddressOf()), show_kmsg_summary=False, O=O) 1943 1944 1945# EndMacro: findkobjectport 1946# Macro: showtaskbusypsets 1947 1948 1949@lldb_command("showtaskbusypsets", fancy=True) 1950def ShowTaskBusyPortSets(cmd_args=None, cmd_options={}, O=None): 1951 """Routine to print information about port sets belonging to this task that 1952 have enqueued messages. This is often a sign of a blocked or hung process 1953 Usage: showtaskbusypsets <task address> 1954 """ 1955 if cmd_args is None or len(cmd_args) == 0: 1956 raise ArgumentError("No arguments passed. Please pass in the address of a task") 1957 1958 task = kern.GetValueFromAddress(cmd_args[0], "task_t") 1959 is_tableval, num_entries = GetSpaceTable(task.itk_space) 1960 1961 if is_tableval: 1962 psets = GetSpaceObjectsWithBits( 1963 is_tableval, num_entries, 0x00080000, gettype("struct ipc_pset") 1964 ) 1965 1966 with O.table(PrintPortSetSummary.header): 1967 for pset in (value(v.AddressOf()) for v in psets): 1968 for wq in Waitq(addressof(pset.ips_wqset)).iterateMembers(): 1969 if wq.asPort().ip_messages.imq_msgcount > 0: 1970 PrintPortSetSummary(pset, space=task.itk_space, O=O) 1971 1972 1973# EndMacro: showtaskbusyports 1974# Macro: showallbusypsets 1975 1976 1977@lldb_command("showallbusypsets", fancy=True) 1978def ShowAllBusyPortSets(cmd_args=None, cmd_options={}, O=None): 1979 """Routine to print information about all port sets on the system that 1980 have enqueued messages. 1981 """ 1982 with O.table(PrintPortSetSummary.header): 1983 pset_ty = gettype("struct ipc_pset") 1984 for pset in kmemory.Zone("ipc port sets").iter_allocated(pset_ty): 1985 pset = value(pset.AddressOf()) 1986 for wq in Waitq(addressof(pset.ips_wqset)).iterateMembers(): 1987 port = wq.asPort() 1988 if port.ip_messages.imq_msgcount > 0: 1989 PrintPortSetSummary(pset, space=port.ip_receiver, O=O) 1990 1991 1992# EndMacro: showallbusyports 1993# Macro: showallpsets 1994 1995 1996@lldb_command("showallpsets", fancy=True) 1997def ShowAllPortSets(cmd_args=None, cmd_options={}, O=None): 1998 """Routine to print information about all allocated psets in the system 1999 2000 usage: showallpsets 2001 """ 2002 with O.table(PrintPortSetSummary.header): 2003 pset_ty = gettype("struct ipc_pset") 2004 for pset in kmemory.Zone("ipc port sets").iter_allocated(pset_ty): 2005 PrintPortSetSummary(value(pset.AddressOf()), O=O) 2006 2007 2008# EndMacro: showallports 2009# Macro: showbusyportsummary 2010 2011 2012@lldb_command("showbusyportsummary") 2013def ShowBusyPortSummary(cmd_args=None): 2014 """Routine to print a summary of information about all receive rights 2015 on the system that have enqueued messages. 2016 """ 2017 2018 ipc_table_size = 0 2019 ipc_busy_ports = 0 2020 ipc_msgs = 0 2021 2022 print(GetTaskBusyIPCSummary.header) 2023 for tsk in kern.tasks: 2024 (summary, table_size, nbusy, nmsgs) = GetTaskBusyIPCSummary(tsk) 2025 ipc_table_size += table_size 2026 ipc_busy_ports += nbusy 2027 ipc_msgs += nmsgs 2028 print(summary) 2029 for tsk in kern.terminated_tasks: 2030 (summary, table_size, nbusy, nmsgs) = GetTaskBusyIPCSummary(tsk) 2031 ipc_table_size += table_size 2032 ipc_busy_ports += nbusy 2033 ipc_msgs += nmsgs 2034 print(summary) 2035 print( 2036 "Total Table Size: {:d}, Busy Ports: {:d}, Messages in-flight: {:d}".format( 2037 ipc_table_size, ipc_busy_ports, ipc_msgs 2038 ) 2039 ) 2040 return 2041 2042 2043# EndMacro: showbusyportsummary 2044# Macro: showport / showpset 2045 2046 2047def ShowPortOrPset(obj, space=0, O=None): 2048 """Routine that lists details about a given IPC port or pset 2049 Syntax: (lldb) showport 0xaddr 2050 """ 2051 if not obj or obj == xnudefines.IPC_OBJECT_DEAD: 2052 print("IPC_OBJECT_DEAD") 2053 return 2054 2055 if IsPortSetType(obj.io_type): 2056 with O.table(PrintPortSetSummary.header): 2057 PrintPortSetSummary(cast(obj, "ipc_pset_t"), space, O=O) 2058 else: 2059 with O.table(PrintPortSummary.header): 2060 PrintPortSummary(cast(obj, "ipc_port_t"), show_sets=True, O=O) 2061 2062 2063@lldb_command("showport", "K", fancy=True) 2064def ShowPort(cmd_args=None, cmd_options={}, O=None): 2065 """Routine that lists details about a given IPC port 2066 2067 usage: showport <address> 2068 """ 2069 # -K is default and kept for backward compat, it used to mean "show kmsg queue" 2070 if cmd_args is None or len(cmd_args) == 0: 2071 raise ArgumentError("Missing port argument") 2072 2073 obj = kern.GetValueFromAddress(cmd_args[0], "struct ipc_object *") 2074 ShowPortOrPset(obj, O=O) 2075 2076 2077@lldb_command("showpset", "S:", fancy=True) 2078def ShowPSet(cmd_args=None, cmd_options={}, O=None): 2079 """Routine that prints details for a given ipc_pset * 2080 2081 usage: showpset [-S <space>] <address> 2082 """ 2083 if cmd_args is None or len(cmd_args) == 0: 2084 raise ArgumentError("Missing port argument") 2085 2086 space = 0 2087 if "-S" in cmd_options: 2088 space = kern.GetValueFromAddress(cmd_options["-S"], "struct ipc_space *") 2089 obj = kern.GetValueFromAddress(cmd_args[0], "struct ipc_object *") 2090 ShowPortOrPset(obj, space=space, O=O) 2091 2092 2093# EndMacro: showport / showpset 2094# Macro: showkmsg: 2095 2096 2097@lldb_command("showkmsg") 2098def ShowKMSG(cmd_args=[]): 2099 """Show detail information about a <ipc_kmsg_t> structure 2100 Usage: (lldb) showkmsg <ipc_kmsg_t> 2101 """ 2102 if cmd_args is None or len(cmd_args) == 0: 2103 raise ArgumentError("Invalid arguments") 2104 2105 kmsg = kern.GetValueFromAddress(cmd_args[0], "ipc_kmsg_t") 2106 print(GetKMsgSummary.header) 2107 print(GetKMsgSummary(kmsg)) 2108 2109 2110# EndMacro: showkmsg 2111# IPC importance inheritance related macros. 2112 2113 2114@lldb_command("showalliits") 2115def ShowAllIITs(cmd_args=[], cmd_options={}): 2116 """Development only macro. Show list of all iits allocated in the system.""" 2117 try: 2118 iit_queue = kern.globals.global_iit_alloc_queue 2119 except ValueError: 2120 print("This debug macro is only available in development or debug kernels") 2121 return 2122 2123 print(GetIPCImportantTaskSummary.header) 2124 for iit in IterateQueue( 2125 iit_queue, "struct ipc_importance_task *", "iit_allocation" 2126 ): 2127 print(GetIPCImportantTaskSummary(iit)) 2128 return 2129 2130 2131@header( 2132 "{: <18s} {: <3s} {: <18s} {: <32s} {: <18s} {: <8s}".format( 2133 "ipc_imp_inherit", "don", "to_task", "proc_name", "from_elem", "depth" 2134 ) 2135) 2136@lldb_type_summary(["ipc_importance_inherit *", "ipc_importance_inherit_t"]) 2137def GetIPCImportanceInheritSummary(iii): 2138 """describes iii object of type ipc_importance_inherit_t *""" 2139 out_str = "" 2140 fmt = "{o: <#18x} {don: <3s} {o.iii_to_task.iit_task: <#18x} {task_name: <20s} {o.iii_from_elem: <#18x} {o.iii_depth: <#8x}" 2141 donating_str = "" 2142 if unsigned(iii.iii_donating): 2143 donating_str = "DON" 2144 taskname = GetProcNameForTask(iii.iii_to_task.iit_task) 2145 if hasattr(iii.iii_to_task, "iit_bsd_pid"): 2146 taskname = "({:d}) {:s}".format( 2147 iii.iii_to_task.iit_bsd_pid, iii.iii_to_task.iit_procname 2148 ) 2149 out_str += fmt.format(o=iii, task_name=taskname, don=donating_str) 2150 return out_str 2151 2152 2153@static_var("recursion_count", 0) 2154@header( 2155 "{: <18s} {: <4s} {: <8s} {: <8s} {: <18s} {: <18s}".format( 2156 "iie", "type", "refs", "made", "#kmsgs", "#inherits" 2157 ) 2158) 2159@lldb_type_summary(["ipc_importance_elem *"]) 2160def GetIPCImportanceElemSummary(iie): 2161 """describes an ipc_importance_elem * object""" 2162 2163 if GetIPCImportanceElemSummary.recursion_count > 500: 2164 GetIPCImportanceElemSummary.recursion_count = 0 2165 return "Recursion of 500 reached" 2166 2167 out_str = "" 2168 fmt = "{: <#18x} {: <4s} {: <8d} {: <8d} {: <#18x} {: <#18x}" 2169 if unsigned(iie.iie_bits) & xnudefines.IIE_TYPE_MASK: 2170 type_str = "INH" 2171 inherit_count = 0 2172 else: 2173 type_str = "TASK" 2174 iit = Cast(iie, "struct ipc_importance_task *") 2175 inherit_count = sum( 2176 1 2177 for i in IterateQueue( 2178 iit.iit_inherits, "struct ipc_importance_inherit *", "iii_inheritance" 2179 ) 2180 ) 2181 2182 refs = unsigned(iie.iie_bits) >> xnudefines.IIE_TYPE_BITS 2183 made_refs = unsigned(iie.iie_made) 2184 kmsg_count = sum( 2185 1 for i in IterateQueue(iie.iie_kmsgs, "struct ipc_kmsg *", "ikm_inheritance") 2186 ) 2187 out_str += fmt.format(iie, type_str, refs, made_refs, kmsg_count, inherit_count) 2188 if config["verbosity"] > vHUMAN: 2189 if kmsg_count > 0: 2190 out_str += "\n\t" + GetKMsgSummary.header 2191 for k in IterateQueue( 2192 iie.iie_kmsgs, "struct ipc_kmsg *", "ikm_inheritance" 2193 ): 2194 out_str += ( 2195 "\t" 2196 + "{: <#18x}".format(GetKmsgHeader(k).msgh_remote_port) 2197 + " " 2198 + GetKMsgSummary(k, "\t").lstrip() 2199 ) 2200 out_str += "\n" 2201 if inherit_count > 0: 2202 out_str += "\n\t" + GetIPCImportanceInheritSummary.header + "\n" 2203 for i in IterateQueue( 2204 iit.iit_inherits, "struct ipc_importance_inherit *", "iii_inheritance" 2205 ): 2206 out_str += "\t" + GetIPCImportanceInheritSummary(i) + "\n" 2207 out_str += "\n" 2208 if type_str == "INH": 2209 iii = Cast(iie, "struct ipc_importance_inherit *") 2210 out_str += "Inherit from: " + GetIPCImportanceElemSummary(iii.iii_from_elem) 2211 2212 return out_str 2213 2214 2215@header("{: <18s} {: <18s} {: <32}".format("iit", "task", "name")) 2216@lldb_type_summary(["ipc_importance_task *"]) 2217def GetIPCImportantTaskSummary(iit): 2218 """iit is a ipc_importance_task value object.""" 2219 fmt = "{: <#18x} {: <#18x} {: <32}" 2220 out_str = "" 2221 pname = GetProcNameForTask(iit.iit_task) 2222 if hasattr(iit, "iit_bsd_pid"): 2223 pname = "({:d}) {:s}".format(iit.iit_bsd_pid, iit.iit_procname) 2224 out_str += fmt.format(iit, iit.iit_task, pname) 2225 return out_str 2226 2227 2228@lldb_command("showallimportancetasks") 2229def ShowIPCImportanceTasks(cmd_args=[], cmd_options={}): 2230 """display a list of all tasks with ipc importance information. 2231 Usage: (lldb) showallimportancetasks 2232 Tip: add "-v" to see detailed information on each kmsg or inherit elems 2233 """ 2234 print( 2235 " " 2236 + GetIPCImportantTaskSummary.header 2237 + " " 2238 + GetIPCImportanceElemSummary.header 2239 ) 2240 for t in kern.tasks: 2241 s = "" 2242 if unsigned(t.task_imp_base): 2243 s += " " + GetIPCImportantTaskSummary(t.task_imp_base) 2244 s += " " + GetIPCImportanceElemSummary(addressof(t.task_imp_base.iit_elem)) 2245 print(s) 2246 2247 2248@lldb_command("showipcimportance", "") 2249def ShowIPCImportance(cmd_args=[], cmd_options={}): 2250 """Describe an importance from <ipc_importance_elem_t> argument. 2251 Usage: (lldb) showimportance <ipc_importance_elem_t> 2252 """ 2253 if cmd_args is None or len(cmd_args) == 0: 2254 raise ArgumentError("Please provide valid argument") 2255 2256 elem = kern.GetValueFromAddress(cmd_args[0], "ipc_importance_elem_t") 2257 print(GetIPCImportanceElemSummary.header) 2258 print(GetIPCImportanceElemSummary(elem)) 2259 2260 2261@header( 2262 "{: <18s} {: <18s} {: <8s} {: <5s} {: <5s} {: <8s}".format( 2263 "ivac", "tbl", "tblsize", "index", "Grow", "freelist" 2264 ) 2265) 2266@lldb_type_summary(["ipc_voucher_attr_control *", "ipc_voucher_attr_control_t"]) 2267def GetIPCVoucherAttrControlSummary(ivac): 2268 """describes a voucher attribute control settings""" 2269 out_str = "" 2270 fmt = "{c: <#18x} {c.ivac_table: <#18x} {c.ivac_table_size: <8d} {c.ivac_key_index: <5d} {growing: <5s} {c.ivac_freelist: <8d}" 2271 growing_str = "" 2272 2273 if ivac == 0: 2274 return "{: <#18x}".format(ivac) 2275 2276 growing_str = "Y" if unsigned(ivac.ivac_is_growing) else "N" 2277 out_str += fmt.format(c=ivac, growing=growing_str) 2278 return out_str 2279 2280 2281@lldb_command("showivac", "") 2282def ShowIPCVoucherAttributeControl(cmd_args=[], cmd_options={}): 2283 """Show summary of voucher attribute contols. 2284 Usage: (lldb) showivac <ipc_voucher_attr_control_t> 2285 """ 2286 if cmd_args is None or len(cmd_args) == 0: 2287 raise ArgumentError("Please provide correct arguments.") 2288 ivac = kern.GetValueFromAddress(cmd_args[0], "ipc_voucher_attr_control_t") 2289 print(GetIPCVoucherAttrControlSummary.header) 2290 print(GetIPCVoucherAttrControlSummary(ivac)) 2291 if config["verbosity"] > vHUMAN: 2292 cur_entry_index = 0 2293 last_entry_index = unsigned(ivac.ivac_table_size) 2294 print("index " + GetIPCVoucherAttributeEntrySummary.header) 2295 while cur_entry_index < last_entry_index: 2296 print( 2297 "{: <5d} ".format(cur_entry_index) 2298 + GetIPCVoucherAttributeEntrySummary( 2299 addressof(ivac.ivac_table[cur_entry_index]) 2300 ) 2301 ) 2302 cur_entry_index += 1 2303 2304 2305@header( 2306 "{: <18s} {: <30s} {: <30s} {: <30s} {: <30s}".format( 2307 "ivam", "get_value_fn", "extract_fn", "release_value_fn", "command_fn" 2308 ) 2309) 2310@lldb_type_summary(["ipc_voucher_attr_manager *", "ipc_voucher_attr_manager_t"]) 2311def GetIPCVoucherAttrManagerSummary(ivam): 2312 """describes a voucher attribute manager settings""" 2313 out_str = "" 2314 fmt = "{: <#18x} {: <30s} {: <30s} {: <30s} {: <30s}" 2315 2316 if unsigned(ivam) == 0: 2317 return "{: <#18x}".format(ivam) 2318 2319 get_value_fn = kern.Symbolicate(unsigned(ivam.ivam_get_value)) 2320 extract_fn = kern.Symbolicate(unsigned(ivam.ivam_extract_content)) 2321 release_value_fn = kern.Symbolicate(unsigned(ivam.ivam_release_value)) 2322 command_fn = kern.Symbolicate(unsigned(ivam.ivam_command)) 2323 out_str += fmt.format(ivam, get_value_fn, extract_fn, release_value_fn, command_fn) 2324 return out_str 2325 2326 2327def iv_key_to_index(key): 2328 """ref: osfmk/ipc/ipc_voucher.c: iv_key_to_index""" 2329 if (key == xnudefines.MACH_VOUCHER_ATTR_KEY_ALL) or ( 2330 key > xnudefines.MACH_VOUCHER_ATTR_KEY_NUM 2331 ): 2332 return xnudefines.IV_UNUSED_KEYINDEX 2333 return key - 1 2334 2335 2336def iv_index_to_key(index): 2337 """ref: osfmk/ipc/ipc_voucher.c: iv_index_to_key""" 2338 if index < xnudefines.MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN: 2339 return index + 1 2340 return xnudefines.MACH_VOUCHER_ATTR_KEY_NONE 2341 2342 2343@header( 2344 "{: <3s} {: <3s} {:s} {:s}".format( 2345 "idx", 2346 "key", 2347 GetIPCVoucherAttrControlSummary.header.strip(), 2348 GetIPCVoucherAttrManagerSummary.header.strip(), 2349 ) 2350) 2351@lldb_type_summary( 2352 ["ipc_voucher_global_table_element *", "ipc_voucher_global_table_element_t"] 2353) 2354def GetIPCVoucherGlobalTableElementSummary(idx, ivac, ivam): 2355 """describes a ipc_voucher_global_table_element object""" 2356 out_str = "" 2357 fmt = "{idx: <3d} {key: <3d} {ctrl_s:s} {mgr_s:s}" 2358 out_str += fmt.format( 2359 idx=idx, 2360 key=iv_index_to_key(idx), 2361 ctrl_s=GetIPCVoucherAttrControlSummary(addressof(ivac)), 2362 mgr_s=GetIPCVoucherAttrManagerSummary(ivam), 2363 ) 2364 return out_str 2365 2366 2367@lldb_command("showglobalvouchertable", "") 2368def ShowGlobalVoucherTable(cmd_args=[], cmd_options={}): 2369 """show detailed information of all voucher attribute managers registered with vouchers system 2370 Usage: (lldb) showglobalvouchertable 2371 """ 2372 entry_size = sizeof(kern.globals.ivac_global_table[0]) 2373 elems = sizeof(kern.globals.ivac_global_table) // entry_size 2374 print(GetIPCVoucherGlobalTableElementSummary.header) 2375 for i in range(elems): 2376 ivac = kern.globals.ivac_global_table[i] 2377 ivam = kern.globals.ivam_global_table[i] 2378 if unsigned(ivam) == 0: 2379 continue 2380 print(GetIPCVoucherGlobalTableElementSummary(i, ivac, ivam)) 2381 2382 2383# Type summaries for Bag of Bits. 2384 2385 2386@lldb_type_summary(["user_data_value_element", "user_data_element_t"]) 2387@header( 2388 "{0: <20s} {1: <16s} {2: <20s} {3: <20s} {4: <16s} {5: <20s}".format( 2389 "user_data_ve", "maderefs", "checksum", "hash value", "size", "data" 2390 ) 2391) 2392def GetBagofBitsElementSummary(data_element): 2393 """Summarizes the Bag of Bits element 2394 params: data_element = value of the object of type user_data_value_element_t 2395 returns: String with summary of the type. 2396 """ 2397 format_str = "{0: <#20x} {1: <16d} {2: <#20x} {3: <#20x} {4: <16d}" 2398 out_string = format_str.format( 2399 data_element, 2400 unsigned(data_element.e_made), 2401 data_element.e_sum, 2402 data_element.e_hash, 2403 unsigned(data_element.e_size), 2404 ) 2405 out_string += " 0x" 2406 2407 for i in range(0, (unsigned(data_element.e_size) - 1)): 2408 out_string += "{:02x}".format(int(data_element.e_data[i])) 2409 return out_string 2410 2411 2412def GetIPCHandleSummary(handle_ptr): 2413 """converts a handle value inside a voucher attribute table to ipc element and returns appropriate summary. 2414 params: handle_ptr - uint64 number stored in handle of voucher. 2415 returns: str - string summary of the element held in internal structure 2416 """ 2417 elem = kern.GetValueFromAddress(handle_ptr, "ipc_importance_elem_t") 2418 if elem.iie_bits & xnudefines.IIE_TYPE_MASK: 2419 iie = Cast(elem, "struct ipc_importance_inherit *") 2420 return GetIPCImportanceInheritSummary(iie) 2421 else: 2422 iit = Cast(elem, "struct ipc_importance_task *") 2423 return GetIPCImportantTaskSummary(iit) 2424 2425 2426def GetATMHandleSummary(handle_ptr): 2427 """Convert a handle value to atm value and returns corresponding summary of its fields. 2428 params: handle_ptr - uint64 number stored in handle of voucher 2429 returns: str - summary of atm value 2430 """ 2431 return "???" 2432 2433 2434def GetBankHandleSummary(handle_ptr): 2435 """converts a handle value inside a voucher attribute table to bank element and returns appropriate summary. 2436 params: handle_ptr - uint64 number stored in handle of voucher. 2437 returns: str - summary of bank element 2438 """ 2439 if handle_ptr == 1: 2440 return "Bank task of Current task" 2441 elem = kern.GetValueFromAddress(handle_ptr, "bank_element_t") 2442 if elem.be_type & 1: 2443 ba = Cast(elem, "struct bank_account *") 2444 return GetBankAccountSummary(ba) 2445 else: 2446 bt = Cast(elem, "struct bank_task *") 2447 return GetBankTaskSummary(bt) 2448 2449 2450def GetBagofBitsHandleSummary(handle_ptr): 2451 """Convert a handle value to bag of bits value and returns corresponding summary of its fields. 2452 params: handle_ptr - uint64 number stored in handle of voucher 2453 returns: str - summary of bag of bits element 2454 """ 2455 elem = kern.GetValueFromAddress(handle_ptr, "user_data_element_t") 2456 return GetBagofBitsElementSummary(elem) 2457 2458 2459@static_var( 2460 "attr_managers", 2461 { 2462 1: GetATMHandleSummary, 2463 2: GetIPCHandleSummary, 2464 3: GetBankHandleSummary, 2465 7: GetBagofBitsHandleSummary, 2466 }, 2467) 2468def GetHandleSummaryForKey(handle_ptr, key_num): 2469 """Get a summary of handle pointer from the voucher attribute manager. 2470 For example key 2 -> ipc and it puts either ipc_importance_inherit_t or ipc_important_task_t. 2471 key 3 -> Bank and it puts either bank_task_t or bank_account_t. 2472 key 7 -> Bag of Bits and it puts user_data_element_t in handle. So summary of it would be Bag of Bits content and refs etc. 2473 """ 2474 key_num = int(key_num) 2475 if key_num not in GetHandleSummaryForKey.attr_managers: 2476 return "Unknown key %d" % key_num 2477 return GetHandleSummaryForKey.attr_managers[key_num](handle_ptr) 2478 2479 2480@header( 2481 "{: <18s} {: <18s} {: <10s} {: <4s} {: <18s} {: <18s}".format( 2482 "ivace", "value_handle", "#refs", "rel?", "maderefs", "next_layer" 2483 ) 2484) 2485@lldb_type_summary(["ivac_entry *", "ivac_entry_t"]) 2486def GetIPCVoucherAttributeEntrySummary(ivace, manager_key_num=0): 2487 """Get summary for voucher attribute entry.""" 2488 out_str = "" 2489 fmt = "{e: <#18x} {e.ivace_value: <#18x} {e.ivace_refs: <10d} {release: <4s} {made_refs: <18s} {next_layer: <18s}" 2490 release_str = "" 2491 free_str = "" 2492 made_refs = "" 2493 next_layer = "" 2494 2495 if unsigned(ivace.ivace_releasing): 2496 release_str = "Y" 2497 if unsigned(ivace.ivace_free): 2498 free_str = "F" 2499 if unsigned(ivace.ivace_layered): 2500 next_layer = "{: <#18x}".format(ivace.ivace_u.ivaceu_layer) 2501 else: 2502 made_refs = "{: <18d}".format(ivace.ivace_u.ivaceu_made) 2503 2504 out_str += fmt.format( 2505 e=ivace, release=release_str, made_refs=made_refs, next_layer=next_layer 2506 ) 2507 if config["verbosity"] > vHUMAN and manager_key_num > 0: 2508 out_str += " " + GetHandleSummaryForKey( 2509 unsigned(ivace.ivace_value), manager_key_num 2510 ) 2511 if config["verbosity"] > vHUMAN: 2512 out_str += " {: <2s} {: <4d} {: <4d}".format( 2513 free_str, ivace.ivace_next, ivace.ivace_index 2514 ) 2515 return out_str 2516 2517 2518@lldb_command("showivacfreelist", "") 2519def ShowIVACFreeList(cmd_args=[], cmd_options={}): 2520 """Walk the free list and print every entry in the list. 2521 usage: (lldb) showivacfreelist <ipc_voucher_attr_control_t> 2522 """ 2523 if cmd_args is None or len(cmd_args) == 0: 2524 raise ArgumentError("Please provide <ipc_voucher_attr_control_t>") 2525 2526 ivac = kern.GetValueFromAddress(cmd_args[0], "ipc_voucher_attr_control_t") 2527 print(GetIPCVoucherAttrControlSummary.header) 2528 print(GetIPCVoucherAttrControlSummary(ivac)) 2529 if unsigned(ivac.ivac_freelist) == 0: 2530 print("ivac table is full") 2531 return 2532 print("index " + GetIPCVoucherAttributeEntrySummary.header) 2533 next_free = unsigned(ivac.ivac_freelist) 2534 while next_free != 0: 2535 print( 2536 "{: <5d} ".format(next_free) 2537 + GetIPCVoucherAttributeEntrySummary(addressof(ivac.ivac_table[next_free])) 2538 ) 2539 next_free = unsigned(ivac.ivac_table[next_free].ivace_next) 2540 2541 2542@header( 2543 "{: <18s} {: <8s} {: <18s} {: <18s}".format( 2544 "ipc_voucher", "refs", "table", "voucher_port" 2545 ) 2546) 2547@lldb_type_summary(["ipc_voucher *", "ipc_voucher_t"]) 2548def GetIPCVoucherSummary(voucher, show_entries=False): 2549 """describe a voucher from its ipc_voucher * object""" 2550 out_str = "" 2551 fmt = "{v: <#18x} {v.iv_refs: <8d} {table_addr: <#18x} {v.iv_port: <#18x}" 2552 out_str += fmt.format(v=voucher, table_addr=addressof(voucher.iv_table)) 2553 entries_str = "" 2554 if show_entries or config["verbosity"] > vHUMAN: 2555 elems = sizeof(voucher.iv_table) // sizeof(voucher.iv_table[0]) 2556 entries_header_str = ( 2557 "\n\t" 2558 + "{: <5s} {: <3s} {: <16s} {: <30s}".format( 2559 "index", "key", "value_index", "manager" 2560 ) 2561 + " " 2562 + GetIPCVoucherAttributeEntrySummary.header 2563 ) 2564 fmt = "{: <5d} {: <3d} {: <16d} {: <30s}" 2565 for i in range(elems): 2566 voucher_entry_index = unsigned(voucher.iv_table[i]) 2567 if voucher_entry_index: 2568 s = fmt.format( 2569 i, 2570 GetVoucherManagerKeyForIndex(i), 2571 voucher_entry_index, 2572 GetVoucherAttributeManagerNameForIndex(i), 2573 ) 2574 e = GetVoucherValueHandleFromVoucherForIndex(voucher, i) 2575 if e is not None: 2576 s += " " + GetIPCVoucherAttributeEntrySummary( 2577 addressof(e), GetVoucherManagerKeyForIndex(i) 2578 ) 2579 if entries_header_str: 2580 entries_str = entries_header_str 2581 entries_header_str = "" 2582 entries_str += "\n\t" + s 2583 if not entries_header_str: 2584 entries_str += "\n\t" 2585 out_str += entries_str 2586 return out_str 2587 2588 2589def GetVoucherManagerKeyForIndex(idx): 2590 """Returns key number for index based on global table. Will raise index error if value is incorrect""" 2591 ret = iv_index_to_key(idx) 2592 if ret == xnudefines.MACH_VOUCHER_ATTR_KEY_NONE: 2593 raise IndexError("invalid voucher key") 2594 return ret 2595 2596 2597def GetVoucherAttributeManagerForKey(k): 2598 """Return the attribute manager name for a given key 2599 params: k - int key number of the manager 2600 return: cvalue - the attribute manager object. 2601 None - if not found 2602 """ 2603 idx = iv_key_to_index(k) 2604 if idx == xnudefines.IV_UNUSED_KEYINDEX: 2605 return None 2606 return kern.globals.ivam_global_table[idx] 2607 2608 2609def GetVoucherAttributeControllerForKey(k): 2610 """Return the attribute controller for a given key 2611 params: k - int key number of the controller 2612 return: cvalue - the attribute controller object. 2613 None - if not found 2614 """ 2615 idx = iv_key_to_index(k) 2616 if idx == xnudefines.IV_UNUSED_KEYINDEX: 2617 return None 2618 return kern.globals.ivac_global_table[idx] 2619 2620 2621def GetVoucherAttributeManagerName(ivam): 2622 """find the name of the ivam object 2623 param: ivam - cvalue object of type ipc_voucher_attr_manager_t 2624 returns: str - name of the manager 2625 """ 2626 return kern.Symbolicate(unsigned(ivam)) 2627 2628 2629def GetVoucherAttributeManagerNameForIndex(idx): 2630 """get voucher attribute manager name for index 2631 return: str - name of the attribute manager object 2632 """ 2633 return GetVoucherAttributeManagerName( 2634 GetVoucherAttributeManagerForKey(GetVoucherManagerKeyForIndex(idx)) 2635 ) 2636 2637 2638def GetVoucherValueHandleFromVoucherForIndex(voucher, idx): 2639 """traverse the voucher attrs and get value_handle in the voucher attr controls table 2640 params: 2641 voucher - cvalue object of type ipc_voucher_t 2642 idx - int index in the entries for which you wish to get actual handle for 2643 returns: cvalue object of type ivac_entry_t 2644 None if no handle found. 2645 """ 2646 manager_key = GetVoucherManagerKeyForIndex(idx) 2647 voucher_num_elems = sizeof(voucher.iv_table) // sizeof(voucher.iv_table[0]) 2648 if idx >= voucher_num_elems: 2649 debuglog("idx %d is out of range max: %d" % (idx, voucher_num_elems)) 2650 return None 2651 voucher_entry_value = unsigned(voucher.iv_table[idx]) 2652 debuglog("manager_key %d" % manager_key) 2653 ivac = GetVoucherAttributeControllerForKey(manager_key) 2654 if ivac is None or addressof(ivac) == 0: 2655 debuglog("No voucher attribute controller for idx %d" % idx) 2656 return None 2657 2658 ivace_table = ivac.ivac_table 2659 if voucher_entry_value >= unsigned(ivac.ivac_table_size): 2660 print( 2661 "Failed to get ivace for value %d in table of size %d" 2662 % (voucher_entry_value, unsigned(ivac.ivac_table_size)) 2663 ) 2664 return None 2665 return ivace_table[voucher_entry_value] 2666 2667 2668@lldb_command("showallvouchers") 2669def ShowAllVouchers(cmd_args=[], cmd_options={}): 2670 """Display a list of all vouchers in the global voucher hash table 2671 Usage: (lldb) showallvouchers 2672 """ 2673 print(GetIPCVoucherSummary.header) 2674 voucher_ty = gettype("struct ipc_voucher") 2675 for v in kmemory.Zone("ipc vouchers").iter_allocated(voucher_ty): 2676 print(GetIPCVoucherSummary(value(v.AddressOf()))) 2677 2678 2679@lldb_command("showvoucher", "") 2680def ShowVoucher(cmd_args=[], cmd_options={}): 2681 """Describe a voucher from <ipc_voucher_t> argument. 2682 Usage: (lldb) showvoucher <ipc_voucher_t> 2683 """ 2684 if cmd_args is None or len(cmd_args) == 0: 2685 raise ArgumentError("Please provide valid argument") 2686 2687 voucher = kern.GetValueFromAddress(cmd_args[0], "ipc_voucher_t") 2688 print(GetIPCVoucherSummary.header) 2689 print(GetIPCVoucherSummary(voucher, show_entries=True)) 2690 2691 2692@lldb_command("showportsendrights") 2693def ShowPortSendRights(cmd_args=[], cmd_options={}): 2694 """Display a list of send rights across all tasks for a given port. 2695 Usage: (lldb) showportsendrights <ipc_port_t> 2696 """ 2697 if cmd_args is None or len(cmd_args) == 0: 2698 raise ArgumentError("no port address provided") 2699 2700 port = kern.GetValueFromAddress(cmd_args[0], "struct ipc_port *") 2701 if not port or port == xnudefines.MACH_PORT_DEAD: 2702 return 2703 2704 return FindPortRights(cmd_args=[unsigned(port)], cmd_options={"-R": "S"}) 2705 2706 2707@lldb_command("showtasksuspenders") 2708def ShowTaskSuspenders(cmd_args=[], cmd_options={}): 2709 """Display the tasks and send rights that are holding a target task suspended. 2710 Usage: (lldb) showtasksuspenders <task_t> 2711 """ 2712 if cmd_args is None or len(cmd_args) == 0: 2713 raise ArgumentError("no task address provided") 2714 2715 task = kern.GetValueFromAddress(cmd_args[0], "task_t") 2716 2717 if task.suspend_count == 0: 2718 print( 2719 "task {:#x} ({:s}) is not suspended".format( 2720 unsigned(task), GetProcNameForTask(task) 2721 ) 2722 ) 2723 return 2724 2725 # If the task has been suspended by the kernel (potentially by 2726 # kperf, using task_suspend_internal) or a client of task_suspend2 2727 # that does not convert its task suspension token to a port using 2728 # convert_task_suspension_token_to_port, then it's impossible to determine 2729 # which task did the suspension. 2730 port = task.itk_resume 2731 if task.pidsuspended: 2732 print( 2733 'task {:#x} ({:s}) has been `pid_suspend`ed. (Probably runningboardd\'s fault. Go look at the syslog for "Suspending task.")'.format( 2734 unsigned(task), GetProcNameForTask(task) 2735 ) 2736 ) 2737 return 2738 elif not port: 2739 print( 2740 "task {:#x} ({:s}) is suspended but no resume port exists".format( 2741 unsigned(task), GetProcNameForTask(task) 2742 ) 2743 ) 2744 return 2745 2746 return FindPortRights(cmd_args=[unsigned(port)], cmd_options={"-R": "S"}) 2747 2748 2749# Macro: showmqueue: 2750@lldb_command("showmqueue", fancy=True) 2751def ShowMQueue(cmd_args=None, cmd_options={}, O=None): 2752 """Routine that lists details about a given mqueue. 2753 An mqueue is directly tied to a mach port, so it just shows the details of that port. 2754 Syntax: (lldb) showmqueue <address> 2755 """ 2756 if cmd_args is None or len(cmd_args) == 0: 2757 raise ArgumentError("Missing mqueue argument") 2758 2759 kern.GetValueFromAddress(cmd_args[0], "struct ipc_mqueue *") 2760 portoff = getfieldoffset("struct ipc_port", "ip_messages") 2761 port = unsigned(ArgumentStringToInt(cmd_args[0])) - unsigned(portoff) 2762 obj = kern.GetValueFromAddress(port, "struct ipc_object *") 2763 ShowPortOrPset(obj, O=O) 2764 2765 2766# EndMacro: showmqueue 2767