Skip to content

Improve freelist walking #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions libslub/frontend/commands/gdb/sbobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,11 @@ def parse_many(

count_objects = len(objects_list)

addrs_list = [o.address for o in objects_list]
dups = {x for x in addrs_list if addrs_list.count(x) > 1}
if len(dups) > 0:
pu.print_error("Freelist broken: contains duplicates")

o = objects_list[index]
first_address = o.address
dump_offset = 0
Expand Down Expand Up @@ -623,6 +628,8 @@ def parse_many(
suffix += " (region end)"
if is_freelist:
suffix += f" [{index+1:#d}]"
if o.address in dups:
suffix += pu.red(" (dup)")

# Only print the chunk type for non verbose
printed = False
Expand Down
3 changes: 3 additions & 0 deletions libslub/slub/obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ def to_string(self, name="", verbose=0, use_cache=False, indent=0, colorize_func
else:
t = "F"
address = "{:#x}".format(self.address)
if self.address < 0xffff800000000000:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't currently restrict use of this library to x64 anywhere, so I would say this should be abstracted and per-architecture, otherwise this would break it on anything but x64.

colorize_func = pu.red
address += " (corrupted?)"
txt += "{:s}{:s} {:s}".format(" "*indent, colorize_func(address), t)

return txt
Expand Down
30 changes: 23 additions & 7 deletions libslub/slub/sb.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,21 +349,37 @@ def walk_linear_memory_region(slab_cache, slab, region_start):
def walk_freelist(slab_cache, freelist):
"""Iterator return each object address in the slab's free list

@param slab_cache: slab cache associated with a given slab. The reason we pas it is because it contains
@param slab_cache: slab cache associated with a given slab. The reason we pass it is because it contains
offsets and random values used to find the next element in the freelist
@param freelist: address of the head of the freelist
@return: iterator returns each object address in the slab's free list
"""

count = 0

# https://elixir.bootlin.com/linux/v5.15/source/mm/slub.c#L412
# and https://elixir.bootlin.com/linux/v5.15/source/mm/slub.c#L252
max_objs_per_slab = slab_cache["oo"]["x"] & ((1 << 16) - 1)

void = gdb.lookup_type("void").pointer().pointer()
offset = int(slab_cache["offset"])

# Stop when we encounter a NULL pointer
while freelist:
address = int(freelist) & sb.UNSIGNED_LONG
yield address
freelist = gdb.Value(address + offset).cast(void).dereference()
freelist = sb.freelist_ptr(slab_cache, freelist, address + offset)
try:
# Stop when we either encounter a NULL pointer or max_objs_per_slab is reached
while freelist and count < max_objs_per_slab:
address = int(freelist) & sb.UNSIGNED_LONG
yield address
if address < 0xffff800000000000:
pu.print_error(f"WARNING: invalid freelist in {slab_cache["name"].string()}: {address=:#x} (non-kernel)")

count += 1
freelist = gdb.Value(address + offset).cast(void).dereference()
freelist = sb.freelist_ptr(slab_cache, freelist, address + offset)

except gdb.MemoryError as e:
freelist = None
pu.print_error(f"WARNING: invalid freelist in {slab_cache["name"].string()}: {address=:#x} ({e})")
return

# XXX - move to class: kmem_cache_cpu
def get_current_slab_cache_cpu(self, slab_cache):
Expand Down