mallocng ¤
Implements handling of musl's allocator mallocng. https://elixir.bootlin.com/musl/v1.2.5/source/src/malloc/mallocng
Classes:
-
SlotState– -
Group–A group is an array of slots.
-
Slot–The "unit of allocation" (analogous to glibc's "chunk").
-
GroupedSlot–This is not a mallocng concept, this is a pwndbg abstraction.
-
Meta–The metadata of a group.
-
MetaArea–Slabs that contain metas, linked in a singly-linked list.
-
MallocContext–The global object that holds all allocator state.
-
Mallocng–Tracks the allocator state.
Functions:
-
int_size–
Attributes:
-
UNIT(int) – -
IB(int) – -
size_classes(list[int]) – -
mallocng–
size_classes module-attribute ¤
size_classes: list[int] = [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
12,
15,
18,
20,
25,
31,
36,
42,
50,
63,
72,
84,
102,
127,
146,
170,
204,
255,
292,
340,
409,
511,
584,
682,
818,
1023,
1169,
1364,
1637,
2047,
2340,
2730,
3276,
4095,
4680,
5460,
6552,
8191,
]
SlotState ¤
Group ¤
A group is an array of slots.
https://elixir.bootlin.com/musl/v1.2.5/source/src/malloc/mallocng/meta.h#L17 struct group { struct meta *meta; unsigned char active_idx:5; char pad[UNIT - sizeof(struct meta *) - 1]; unsigned char storage[]; };
Methods:
-
preload–Read all the necessary process memory to populate the group's
-
set_meta–Sets the meta object for this group.
-
at_index–Get the address of the slot at index idx.
Attributes:
-
addr– -
meta(Meta) –Raises:
-
active_idx(int) –Raises:
-
storage(int) – -
group_size(int) –The size of this group, in bytes.
group_size property ¤
preload ¤
Read all the necessary process memory to populate the group's fields.
Do this if you know you will be using most of the fields of the group. It will be faster, since we can do one reads instead of two small ones. You may also catch inaccessible memory exceptions here and not worry about it later.
Raises:
-
Error–When reading memory fails.
Slot ¤
The "unit of allocation" (analogous to glibc's "chunk"). There is no struct in the source code that describes it.
Methods:
-
preload–Read all the necessary process memory to populate the slot's
-
preload_meta_dependants–Preloads all fields that depend on a sane meta.
-
is_cyclic–Returns whether mallocng reports that p != start.
-
contains_group–Does this slot nest a group?
-
set_group–If the slot is FREED or AVAIL, it is impossible for it to
-
from_p– -
from_start–
Attributes:
-
p(int) – -
offset(int) –Raises:
-
pn3(int) –Raises:
-
idx(int) –Raises:
-
reserved_in_header(int) – -
big_offset_check(int) –Raises:
-
start(int) –Raises:
-
cyclic_offset(int) –Returns zero if is_cyclic() is False.
-
startn3(int) –Raises:
-
reserved_in_footer(int) –Returns -1 if the value is invalid, i.e.
-
end(int) –Raises:
-
reserved(int) –Returns 0 if reserved_in_header() == 6.
-
nominal_size(int) –Raises:
-
user_size(int) –Raises:
-
slack(int) –Raises:
-
group(Group) – -
meta(Meta) –Raises:
-
slot_state(SlotState) –
cyclic_offset property ¤
reserved_in_footer property ¤
Returns -1 if the value is invalid, i.e. reserved_in_header() != 5.
Raises:
-
Error–When reading memory fails.
reserved property ¤
Returns 0 if reserved_in_header() == 6. Returns -1 if reserved_in_header() == 7.
Raises:
-
Error–When reading memory fails.
preload ¤
Read all the necessary process memory to populate the slot's p header fields.
Do this if you know you will be using most of the fields of the slot. It will be faster, since we can do a few big reads instead of many small ones. You may also catch inaccessible memory exceptions here and not worry about it later.
Fields dependant on the meta are not loaded - you will still need to worry about exceptions coming from them.
Raises:
-
Error–When reading memory fails.
preload_meta_dependants ¤
Preloads all fields that depend on a sane meta.
It generally only makes sense to run this after preload(). Calling this reduces the amount of process writes and centralizes field exceptions to this function.
If both preload() and preload_meta_dependants() return without exceptions, all the fields in this class are guaranteed to not cause any more memory reads nor raise any more exceptions.
Raises:
-
Error–When the meta is corrupt and/or reading memory fails.
GroupedSlot ¤
GroupedSlot(group: Group, idx: int)
This is not a mallocng concept, this is a pwndbg abstraction.
A Slot object uses its inband metadata to recover all its fields and uncover more information about itself by locating its group and meta. It works essentially the same way mallocng's free() works.
However, if a slot is freed or available, most of its in-band metadata will be invalid and it will not be able to recover group and meta. But, given the start of the slot, we can infer which group it belongs to and what its index is by walking allocator state i.e. ctx i.e. by using Mallocng.find_slot().
A GroupedSlot then describes all information we can glean about a slot which is described by a (group, idx) pair. Many of its fields can be completely different from a Slot at the same location. They are guaranteed to be the same only if the slot is ALLOCATED and hasn't been corrupted.
Not all fields that are available in Slot are available in GroupedSlot.
Make sure the group you are passing to the constructor points to a valid meta object.
Attributes:
Meta ¤
The metadata of a group.
https://elixir.bootlin.com/musl/v1.2.5/source/src/malloc/mallocng/meta.h#L24 struct meta { struct meta *prev, *next; struct group *mem; volatile int avail_mask, freed_mask; uintptr_t last_idx:5; uintptr_t freeable:1; uintptr_t sizeclass:6; uintptr_t maplen:8*sizeof(uintptr_t)-12; };
Methods:
-
preload–Read all the necessary process memory to populate the meta's
-
parent_group–If this group is nested, returns the address of the group which
-
root_group–Returns the topmost/biggest parent group. It will never be a nested
-
slotstate_at_index– -
sizeof–
Attributes:
-
addr(int) – -
prev(int) –Raises:
-
next(int) –Raises:
-
mem(int) –Raises:
-
avail_mask(int) –Raises:
-
freed_mask(int) –Raises:
-
last_idx(int) –Raises:
-
freeable(int) –Raises:
-
sizeclass(int) –Raises:
-
maplen(int) –Raises:
-
stride(int) –Returns -1 if sizeclass >= len(size_classes).
-
cnt(int) –Number of slots in the group.
-
is_donated(bool) –Returns whether the group object referred to by this meta has been
-
is_mmaped(bool) –Returns whether the group object referred to by this meta has been
-
is_nested(bool) –Returns whether the group object referred to by this meta has been
is_donated property ¤
Returns whether the group object referred to by this meta has been created by being donated by ld.
is_mmaped property ¤
Returns whether the group object referred to by this meta has been created by being mmaped.
is_nested property ¤
Returns whether the group object referred to by this meta has been created by being nested into a slot.
preload ¤
Read all the necessary process memory to populate the meta's fields.
Do this if you know you will be using most of the fields of the meta. It will be faster, since we can do a one big read instead of many small ones. You may also catch inaccessible memory exceptions here and not worry about it later.
Raises:
-
Error–When reading memory fails.
parent_group ¤
If this group is nested, returns the address of the group which contains the slot in which this group is in. Otherwise, returns -1.
MetaArea ¤
Slabs that contain metas, linked in a singly-linked list.
https://elixir.bootlin.com/musl/v1.2.5/source/src/malloc/mallocng/meta.h#L34 struct meta_area { uint64_t check; struct meta_area *next; int nslots; struct meta slots[]; };
Methods:
Attributes:
-
addr(int) – -
check(int) – -
meta_area(int) – -
nslots(int) – -
slots(int) – -
area_size(int) –Returns not the size of
struct meta_areabut rather
area_size property ¤
Returns not the size of struct meta_area but rather the size of the memory this object represents.
MallocContext ¤
The global object that holds all allocator state.
https://elixir.bootlin.com/musl/v1.2.5/source/src/malloc/mallocng/meta.h#L41 struct malloc_context { uint64_t secret;
ifndef PAGESIZE¤
size_t pagesize;
endif¤
int init_done; unsigned mmap_counter; struct meta *free_meta_head; struct meta *avail_meta; size_t avail_meta_count, avail_meta_area_count, meta_alloc_shift; struct meta_area *meta_area_head, *meta_area_tail; unsigned char *avail_meta_areas; struct meta *active[48]; size_t usage_by_class[48]; uint8_t unmap_seq[32], bounces[32]; uint8_t seq; uintptr_t brk; };
Methods:
-
load– -
looks_valid–Returns true if this object looks like a valid
struct malloc_contextobject
Attributes:
-
addr(int) – -
secret(int) – -
pagesize(int) – -
init_done(int) – -
mmap_counter(int) – -
free_meta_head(int) – -
avail_meta(int) – -
avail_meta_count(int) – -
avail_meta_area_count(int) – -
meta_alloc_shift(int) – -
meta_area_head(int) – -
meta_area_tail(int) – -
avail_meta_areas(int) – -
active(list[int]) – -
usage_by_class(list[int]) – -
unmap_seq(list[int]) – -
bounces(list[int]) – -
seq(int) – -
brk(int) – -
sizeof(int) – -
has_pagesize_field(bool) –
looks_valid ¤
Returns true if this object looks like a valid struct malloc_context object describing an initialized heap. False otherwise.
This is used by class Mallocng to find the correct ctx object.
We consider it invalid if the heap reads as uninitialized because: 1. Performing this check filters out invalid ctx objects very well. 2. When musl is dynmically linked, due to the ld donation logic, the heap will usually be initialized before the start of main().
Mallocng ¤
Bases: MemoryAllocator
Tracks the allocator state. By leveraging the __malloc_context symbol.
Import this singleton class like: from pwndbg.aglib.heap.mallocng import mallocng as ng
and make sure that you have run ng.init_if_needed() before you used the object.
Methods:
-
init_if_needed–We want this class to be a singleton, but also we can't
-
set_ctx_addr–Find where the __malloc_context global symbol is. Try using debug information,
-
libc_has_debug_syms– -
find_slot–Get the slot which contains this address.
-
containing–Same as find_slot() but returns only the
startaddress of the slot, or zero -
get_free_metas–Get all free metas by traversing the ctx.free_meta_head doubly
-
meta_is_avail–Checks whether a meta is available.
-
is_initialized–Returns whether the allocator is initialized or not.
Attributes:
-
finished_init(bool) – -
ctx_addr(int) – -
ctx(MallocContext | None) – -
has_debug_syms(bool) –
init_if_needed ¤
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. This also ensures our view of the heap is up-to-date.
Returns:
-
bool–True if this object is successfully initialized (whether
-
bool–now or before). False otherswise. If this returns False
-
bool–you may not use this object for heap operations.
set_ctx_addr ¤
Find where the __malloc_context global symbol is. Try using debug information, but if it isn't available try using a heuristic.
find_slot ¤
find_slot(
address: int, metadata: bool = False, shallow: bool = False
) -> tuple[GroupedSlot | None, Slot | None]
Get the slot which contains this address.
We say a slot "contains" an address, if the address is in [start, start + stride). Thus, this will match the previous slot if you provide the address of the header inband metadata of a slot.
If metadata is True, then we check [start - IB, end) for containment.
If shallow is True, return the biggest slot which contains this address. The group that owns this slot will not be a nested group.
Returns (None, None) if nothing is found.
containing ¤
Same as find_slot() but returns only the start address of the slot, or zero if no slot is found.
is_initialized ¤
Returns whether the allocator is initialized or not.
Returns:
-
bool–A boolean.