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-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-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-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.

@ -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
# Slot granularity.
UNIT = 16
UNIT: int = 16
# 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
# Describes the possible sizes a slot can be. These are `/ UNIT`.
@ -47,7 +47,7 @@ class SlotState(Enum):
# Shorthand
def int_size():
def int_size() -> int:
return pwndbg.aglib.typeinfo.sint.sizeof
@ -548,13 +548,11 @@ class Slot:
sn3 = memory.u8(start - 3)
if sn3 == 224:
off = memory.u16(start - 2)
p = start + off * UNIT
obj = cls(p)
obj = cls(start + off * UNIT)
obj._sn3 = sn3
else:
# freed / avail slots will also go into this branch.
p = start
obj = cls(p)
obj = cls(start)
obj._sn3 = obj._pn3 = sn3
obj._start = start
@ -784,7 +782,7 @@ class Meta:
# Semi-custom methods..
@property
def stride(self):
def stride(self) -> int:
"""
Returns -1 if sizeclass >= len(size_classes).
"""
@ -804,7 +802,7 @@ class Meta:
# Custom methods..
@property
def cnt(self):
def cnt(self) -> int:
"""
Number of slots in the group.
"""
@ -851,7 +849,7 @@ class Meta:
return SlotState.ALLOCATED
@staticmethod
def sizeof():
def sizeof() -> int:
return 2 * int_size() + 4 * pwndbg.aglib.arch.ptrsize
@ -878,7 +876,7 @@ class MetaArea:
self.load()
def load(self):
def load(self) -> None:
ptrsize = pwndbg.aglib.arch.ptrsize
uint64size = pwndbg.aglib.typeinfo.uint64.sizeof
endian = pwndbg.aglib.arch.endian
@ -909,6 +907,14 @@ class MetaArea:
"""
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:
"""
@ -965,7 +971,7 @@ class MallocContext:
# evaluation.
self.load()
def load(self):
def load(self) -> None:
ptrsize = pwndbg.aglib.arch.ptrsize
size_tsize = pwndbg.aglib.typeinfo.size_t.sizeof
unsignedsize = pwndbg.aglib.typeinfo.uint.sizeof
@ -1059,7 +1065,7 @@ class Mallocng(pwndbg.aglib.heap.heap.MemoryAllocator):
before you used the object.
"""
def __init__(self):
def __init__(self) -> None:
self.finished_init: bool = False
self.ctx_addr: int = 0
@ -1068,16 +1074,21 @@ class Mallocng(pwndbg.aglib.heap.heap.MemoryAllocator):
self.secret: bytearray = b""
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
initialize it as soon as pwndbg is loaded.
Users of the object are responsible for calling this to
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:
return
return self.hope
self.ctx_addr = 0
self.ctx = None
@ -1090,9 +1101,13 @@ class Mallocng(pwndbg.aglib.heap.heap.MemoryAllocator):
if self.ctx_addr and self.hope:
self.ctx = MallocContext(self.ctx_addr)
# We will try to reinitialize again if we failed now.
if self.hope:
self.finished_init = True
def set_ctx_addr(self):
return self.hope
def set_ctx_addr(self) -> None:
"""
Find where the __malloc_context global symbol is. Try using debug information,
but if it isn't available try using a heuristic.
@ -1152,7 +1167,7 @@ class Mallocng(pwndbg.aglib.heap.heap.MemoryAllocator):
return
for addr, mapname in possible:
if mapname.contains("libc"):
if "libc" in mapname:
self.ctx_addr = addr
return

@ -554,7 +554,11 @@ def smart_dump_slot(
# 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.. "
ng.init_if_needed()
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:
@ -581,6 +585,84 @@ def smart_dump_slot(
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(
description="""
Dump information about a mallocng slot, given its user address.
@ -730,6 +812,77 @@ def mallocng_group(address: int) -> None:
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(
description="""
Find slot which contains the given address.
@ -779,7 +932,9 @@ def mallocng_find(
print(message.error(f"Address {hex(address)} not readable."))
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)

Loading…
Cancel
Save