mallocng: Add ng-metaarea and ng-ctx commands (#3164)

* add missing type annotations

* ng-metaarea to dump meta_area objects

* ng-ctx: command to dump the __malloc_context object

* autogen docs

* properly bail if we cant find the __malloc_context

* take the p var out of `from_start`
pull/3177/head
k4lizen 5 months ago committed by GitHub
parent 500bb9edbc
commit df03112578
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -260,6 +260,8 @@
- [mallocng-explain](musl/mallocng-explain.md) - Gives a quick explanation of musl's mallocng allocator. - [mallocng-explain](musl/mallocng-explain.md) - Gives a quick explanation of musl's mallocng allocator.
- [mallocng-find](musl/mallocng-find.md) - Find slot which contains the given address. - [mallocng-find](musl/mallocng-find.md) - Find slot which contains the given address.
- [mallocng-group](musl/mallocng-group.md) - Print out information about a mallocng group at the given address. - [mallocng-group](musl/mallocng-group.md) - Print out information about a mallocng group at the given address.
- [mallocng-malloc-context](musl/mallocng-malloc-context.md) - Print out the mallocng __malloc_context (ctx) object.
- [mallocng-meta-area](musl/mallocng-meta-area.md) - Print out a mallocng meta_area object at the given address.
- [mallocng-meta](musl/mallocng-meta.md) - Print out information about a mallocng group given the address of its meta. - [mallocng-meta](musl/mallocng-meta.md) - Print out information about a mallocng group given the address of its meta.
- [mallocng-slot-start](musl/mallocng-slot-start.md) - Dump information about a mallocng slot, given its start address. - [mallocng-slot-start](musl/mallocng-slot-start.md) - Dump information about a mallocng slot, given its start address.
- [mallocng-slot-user](musl/mallocng-slot-user.md) - Dump information about a mallocng slot, given its user address. - [mallocng-slot-user](musl/mallocng-slot-user.md) - Dump information about a mallocng slot, given its user address.

@ -0,0 +1,25 @@
<!-- THIS PART OF THIS FILE IS AUTOGENERATED. DO NOT MODIFY IT. See scripts/generate-docs.sh -->
# mallocng-malloc-context
```text
usage: mallocng-malloc-context [-h] [address]
```
Print out the mallocng __malloc_context (ctx) object.
**Alias:** ng-ctx
### Positional arguments
|Positional Argument|Help|
| :--- | :--- |
|address|Use the provided address instead of the one Pwndbg found.|
### Optional arguments
|Short|Long|Help|
| :--- | :--- | :--- |
|-h|--help|show this help message and exit|
<!-- END OF AUTOGENERATED PART. Do not modify this line or the line below, they mark the end of the auto-generated part of the file. If you want to extend the documentation in a way which cannot easily be done by adding to the command help description, write below the following line. -->
<!-- ------------\>8---- ----\>8---- ----\>8------------ -->

@ -0,0 +1,25 @@
<!-- THIS PART OF THIS FILE IS AUTOGENERATED. DO NOT MODIFY IT. See scripts/generate-docs.sh -->
# mallocng-meta-area
```text
usage: mallocng-meta-area [-h] address
```
Print out a mallocng meta_area object at the given address.
**Alias:** ng-metaarea
### Positional arguments
|Positional Argument|Help|
| :--- | :--- |
|address|The address of the meta_area object.|
### Optional arguments
|Short|Long|Help|
| :--- | :--- | :--- |
|-h|--help|show this help message and exit|
<!-- END OF AUTOGENERATED PART. Do not modify this line or the line below, they mark the end of the auto-generated part of the file. If you want to extend the documentation in a way which cannot easily be done by adding to the command help description, write below the following line. -->
<!-- ------------\>8---- ----\>8---- ----\>8------------ -->

@ -22,9 +22,9 @@ import pwndbg.color.message as message
# https://elixir.bootlin.com/musl/v1.2.5/source/src/malloc/mallocng/meta.h#L14 # https://elixir.bootlin.com/musl/v1.2.5/source/src/malloc/mallocng/meta.h#L14
# Slot granularity. # Slot granularity.
UNIT = 16 UNIT: int = 16
# Size of in-band metadata. # Size of in-band metadata.
IB = 4 IB: int = 4
# https://elixir.bootlin.com/musl/v1.2.5/source/src/malloc/mallocng/malloc.c#L12 # https://elixir.bootlin.com/musl/v1.2.5/source/src/malloc/mallocng/malloc.c#L12
# Describes the possible sizes a slot can be. These are `/ UNIT`. # Describes the possible sizes a slot can be. These are `/ UNIT`.
@ -47,7 +47,7 @@ class SlotState(Enum):
# Shorthand # Shorthand
def int_size(): def int_size() -> int:
return pwndbg.aglib.typeinfo.sint.sizeof return pwndbg.aglib.typeinfo.sint.sizeof
@ -548,13 +548,11 @@ class Slot:
sn3 = memory.u8(start - 3) sn3 = memory.u8(start - 3)
if sn3 == 224: if sn3 == 224:
off = memory.u16(start - 2) off = memory.u16(start - 2)
p = start + off * UNIT obj = cls(start + off * UNIT)
obj = cls(p)
obj._sn3 = sn3 obj._sn3 = sn3
else: else:
# freed / avail slots will also go into this branch. # freed / avail slots will also go into this branch.
p = start obj = cls(start)
obj = cls(p)
obj._sn3 = obj._pn3 = sn3 obj._sn3 = obj._pn3 = sn3
obj._start = start obj._start = start
@ -784,7 +782,7 @@ class Meta:
# Semi-custom methods.. # Semi-custom methods..
@property @property
def stride(self): def stride(self) -> int:
""" """
Returns -1 if sizeclass >= len(size_classes). Returns -1 if sizeclass >= len(size_classes).
""" """
@ -804,7 +802,7 @@ class Meta:
# Custom methods.. # Custom methods..
@property @property
def cnt(self): def cnt(self) -> int:
""" """
Number of slots in the group. Number of slots in the group.
""" """
@ -851,7 +849,7 @@ class Meta:
return SlotState.ALLOCATED return SlotState.ALLOCATED
@staticmethod @staticmethod
def sizeof(): def sizeof() -> int:
return 2 * int_size() + 4 * pwndbg.aglib.arch.ptrsize return 2 * int_size() + 4 * pwndbg.aglib.arch.ptrsize
@ -878,7 +876,7 @@ class MetaArea:
self.load() self.load()
def load(self): def load(self) -> None:
ptrsize = pwndbg.aglib.arch.ptrsize ptrsize = pwndbg.aglib.arch.ptrsize
uint64size = pwndbg.aglib.typeinfo.uint64.sizeof uint64size = pwndbg.aglib.typeinfo.uint64.sizeof
endian = pwndbg.aglib.arch.endian endian = pwndbg.aglib.arch.endian
@ -909,6 +907,14 @@ class MetaArea:
""" """
return self.slots + idx * Meta.sizeof() return self.slots + idx * Meta.sizeof()
@property
def area_size(self) -> int:
"""
Returns not the size of `struct meta_area` but rather
the size of the memory this object represents.
"""
return (self.slots - self.addr) + self.nslots * Meta.sizeof()
class MallocContext: class MallocContext:
""" """
@ -965,7 +971,7 @@ class MallocContext:
# evaluation. # evaluation.
self.load() self.load()
def load(self): def load(self) -> None:
ptrsize = pwndbg.aglib.arch.ptrsize ptrsize = pwndbg.aglib.arch.ptrsize
size_tsize = pwndbg.aglib.typeinfo.size_t.sizeof size_tsize = pwndbg.aglib.typeinfo.size_t.sizeof
unsignedsize = pwndbg.aglib.typeinfo.uint.sizeof unsignedsize = pwndbg.aglib.typeinfo.uint.sizeof
@ -1059,7 +1065,7 @@ class Mallocng(pwndbg.aglib.heap.heap.MemoryAllocator):
before you used the object. before you used the object.
""" """
def __init__(self): def __init__(self) -> None:
self.finished_init: bool = False self.finished_init: bool = False
self.ctx_addr: int = 0 self.ctx_addr: int = 0
@ -1068,16 +1074,21 @@ class Mallocng(pwndbg.aglib.heap.heap.MemoryAllocator):
self.secret: bytearray = b"" self.secret: bytearray = b""
self.hope: bool = True self.hope: bool = True
def init_if_needed(self): def init_if_needed(self) -> bool:
""" """
We want this class to be a singleton, but also we can't We want this class to be a singleton, but also we can't
initialize it as soon as pwndbg is loaded. initialize it as soon as pwndbg is loaded.
Users of the object are responsible for calling this to Users of the object are responsible for calling this to
make sure the object is initialized. make sure the object is initialized.
Returns:
True if this object is successfully initialized (whether
now or before). False otherswise. If this returns False
you may not use this object for heap operations.
""" """
if self.finished_init: if self.finished_init:
return return self.hope
self.ctx_addr = 0 self.ctx_addr = 0
self.ctx = None self.ctx = None
@ -1090,9 +1101,13 @@ class Mallocng(pwndbg.aglib.heap.heap.MemoryAllocator):
if self.ctx_addr and self.hope: if self.ctx_addr and self.hope:
self.ctx = MallocContext(self.ctx_addr) self.ctx = MallocContext(self.ctx_addr)
self.finished_init = True # We will try to reinitialize again if we failed now.
if self.hope:
self.finished_init = True
return self.hope
def set_ctx_addr(self): def set_ctx_addr(self) -> None:
""" """
Find where the __malloc_context global symbol is. Try using debug information, Find where the __malloc_context global symbol is. Try using debug information,
but if it isn't available try using a heuristic. but if it isn't available try using a heuristic.
@ -1152,7 +1167,7 @@ class Mallocng(pwndbg.aglib.heap.heap.MemoryAllocator):
return return
for addr, mapname in possible: for addr, mapname in possible:
if mapname.contains("libc"): if "libc" in mapname:
self.ctx_addr = addr self.ctx_addr = addr
return return

@ -554,8 +554,12 @@ def smart_dump_slot(
# If it wasn't provided to us, let's try to search for it now. # If it wasn't provided to us, let's try to search for it now.
output += "Could not load valid meta from local information, searching the heap.. " output += "Could not load valid meta from local information, searching the heap.. "
ng.init_if_needed()
gslot, fslot = ng.find_slot(slot.p, False, False) if not ng.init_if_needed():
output += message.error("\nCouldn't find the allocator, aborting the search. ")
gslot, fslot = None, None
else:
gslot, fslot = ng.find_slot(slot.p, False, False)
if gslot is None: if gslot is None:
output += "Not found.\n\n" output += "Not found.\n\n"
@ -581,6 +585,84 @@ def smart_dump_slot(
return output return output
def dump_meta_area(meta_area: mallocng.MetaArea) -> str:
area_range = (
"@ "
+ C.memory.get(meta_area.addr)
+ " - "
+ C.memory.get(meta_area.addr + meta_area.area_size)
)
pp = PropertyPrinter()
pp.start_section("meta_area", area_range)
pp.add(
[
Property(name="check", value=meta_area.check),
Property(name="next", value=meta_area.next, is_addr=True),
Property(name="nslots", value=meta_area.nslots),
Property(name="slots", value=meta_area.slots, is_addr=True),
]
)
return pp.dump()
def dump_malloc_context(ctx: mallocng.MallocContext) -> str:
ctx_addr = "@ " + C.memory.get(ctx.addr)
pp = PropertyPrinter(22)
pp.start_section("ctx", ctx_addr)
props = [
Property(name="secret", value=ctx.secret),
]
if ctx.has_pagesize_field:
props.append(
Property(name="pagesize", value=ctx.pagesize),
)
props.extend(
[
Property(name="init_done", value=ctx.init_done),
Property(name="mmap_counter", value=ctx.mmap_counter),
Property(name="free_meta_head", value=ctx.free_meta_head, is_addr=True),
Property(name="avail_meta", value=ctx.avail_meta, is_addr=True),
Property(name="avail_meta_count", value=ctx.avail_meta_count),
Property(name="avail_meta_area_count", value=ctx.avail_meta_area_count),
Property(name="meta_alloc_shift", value=ctx.meta_alloc_shift),
Property(name="meta_area_head", value=ctx.meta_area_head, is_addr=True),
Property(name="meta_area_tail", value=ctx.meta_area_tail, is_addr=True),
Property(name="avail_meta_areas", value=ctx.avail_meta_areas, is_addr=True),
]
)
for i in range(len(ctx.active)):
if ctx.active[i] != 0:
props.append(Property(name=f"active[{i}]", value=ctx.active[i], is_addr=True))
for i in range(len(ctx.usage_by_class)):
if ctx.usage_by_class[i] != 0:
props.append(Property(name=f"usage_by_class[{i}]", value=ctx.usage_by_class[i]))
for i in range(len(ctx.unmap_seq)):
if ctx.unmap_seq[i] != 0:
props.append(Property(name=f"unmap_seq[{i}]", value=ctx.unmap_seq[i]))
for i in range(len(ctx.bounces)):
if ctx.bounces[i] != 0:
props.append(Property(name=f"bounces[{i}]", value=ctx.bounces[i]))
props.extend(
[
Property(name="seq", value=ctx.seq),
Property(name="brk", value=ctx.brk, is_addr=True),
]
)
pp.add(props)
return pp.dump()
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description=""" description="""
Dump information about a mallocng slot, given its user address. Dump information about a mallocng slot, given its user address.
@ -730,6 +812,77 @@ def mallocng_group(address: int) -> None:
return return
parser = argparse.ArgumentParser(
description="""
Print out a mallocng meta_area object at the given address.
""",
)
parser.add_argument(
"address",
type=int,
help="The address of the meta_area object.",
)
@pwndbg.commands.Command(
parser,
category=CommandCategory.MUSL,
aliases=["ng-metaarea"],
)
@pwndbg.commands.OnlyWhenRunning
def mallocng_meta_area(address: int) -> None:
if not memory.is_readable_address(address):
print(message.error(f"Address {address:#x} not readable."))
return
try:
meta_area = mallocng.MetaArea(address)
print(dump_meta_area(meta_area), end="")
except pwndbg.dbg_mod.Error as e:
print(message.error(str(e)))
return
parser = argparse.ArgumentParser(
description="""
Print out the mallocng __malloc_context (ctx) object.
""",
)
parser.add_argument(
"address",
nargs="?",
type=int,
help="Use the provided address instead of the one Pwndbg found.",
)
@pwndbg.commands.Command(
parser,
category=CommandCategory.MUSL,
aliases=["ng-ctx"],
)
@pwndbg.commands.OnlyWhenRunning
def mallocng_malloc_context(address: Optional[int] = None) -> None:
if address is None:
if not ng.init_if_needed():
print(message.error("Couldn't find the allocator, aborting the command."))
return
ctx = ng.ctx
else:
if not memory.is_readable_address(address):
print(message.error(f"Address {address:#x} not readable."))
return
try:
ctx = mallocng.MallocContext(address)
except pwndbg.dbg_mod.Error as e:
print(message.error(str(e)))
return
print(dump_malloc_context(ctx), end="")
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description=""" description="""
Find slot which contains the given address. Find slot which contains the given address.
@ -779,7 +932,9 @@ def mallocng_find(
print(message.error(f"Address {hex(address)} not readable.")) print(message.error(f"Address {hex(address)} not readable."))
return return
ng.init_if_needed() if not ng.init_if_needed():
print(message.error("Couldn't find the allocator, aborting the command."))
return
grouped_slot, slot = ng.find_slot(address, metadata, shallow) grouped_slot, slot = ng.find_slot(address, metadata, shallow)

Loading…
Cancel
Save