* [PATCH] scripts/gdb: Fix radix tree lookup pointer handling
@ 2026-10-07 9:31 Ting-Han Hou
2026-10-09 18:10 ` Ting-Han Hou
2026-10-10 21:17 ` Andrew Morton
0 siblings, 2 replies; 7+ messages in thread
From: Ting-Han Hou @ 2026-10-07 9:31 UTC (permalink / raw)
To: Jan Kiszka, Kieran Bingham; +Cc: linux-kernel
From: Ting-Han Hou <ue081723@gmail.com>
lookup() casts each slot to a pointer to a node pointer and then
dereferences it. Since the slot already contains an entry pointer,
this reads the entry's contents as another pointer instead of
returning the entry itself.
Internal entries also need their tag bits removed before descending
to the next node.
Return non-internal entries directly and use entry_to_node() when
descending through internal entries. Remove the explicit shift
counter, as traversal now follows the entry type.
Fixes: b7235d6bb516 ("scripts/gdb: add a Radix Tree Parser")
Assisted-by: LLM
Signed-off-by: Ting-Han Hou <ue081723@gmail.com>
---
Tested with GDB 15.1 against standalone GCC-built C fixtures using the
xarray/xa_node field layout, with XA_CHUNK_SHIFT=4 and 6. These are
synthetic debugger fixtures, not a booted kernel or a kernel core dump.
Before the fix, a one-level lookup returned the payload 0x12345678
instead of its address; a two-level lookup missed a populated slot.
After the fix, all 33 checks passed for each slot configuration (66
total), including empty/direct roots, one- to three-level trees,
zero-filled payloads, value entries, absent/out-of-range indices,
struct/pointer roots, and the GDB convenience function.
This does not test multi-index entries or concurrent updates/retry
entries. The change is limited to the debugger lookup helper.
scripts/gdb/linux/radixtree.py | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/scripts/gdb/linux/radixtree.py b/scripts/gdb/linux/radixtree.py
--- a/scripts/gdb/linux/radixtree.py
+++ b/scripts/gdb/linux/radixtree.py
@@ -55,24 +55,15 @@
if (index > maxindex):
return None
- shift = node['shift'] + constants.LX_RADIX_TREE_MAP_SHIFT
-
while True:
offset = (index >> node['shift']) & constants.LX_RADIX_TREE_MAP_MASK
slot = node['slots'][offset]
if slot == 0:
return None
-
- node = slot.cast(node.type.pointer()).dereference()
- if node == 0:
- return None
-
- shift -= constants.LX_RADIX_TREE_MAP_SHIFT
- if (shift <= 0):
- break
-
- return node
+ if not is_internal_node(slot):
+ return slot
+ node = entry_to_node(slot)
def descend(parent, index):
offset = (index >> int(parent["shift"])) & constants.LX_RADIX_TREE_MAP_MASK
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] scripts/gdb: Fix radix tree lookup pointer handling
2026-10-07 9:31 [PATCH] scripts/gdb: Fix radix tree lookup pointer handling Ting-Han Hou
@ 2026-10-09 18:10 ` Ting-Han Hou
2026-10-10 21:17 ` Andrew Morton
1 sibling, 0 replies; 7+ messages in thread
From: Ting-Han Hou @ 2026-10-09 18:10 UTC (permalink / raw)
To: Andrew Morton; +Cc: Jan Kiszka, Kieran Bingham, linux-kernel
Adding Andrew to Cc, as scripts/gdb patches usually go through his
tree. Sorry for leaving you off the original posting, Andrew.
The patch is here:
https://lore.kernel.org/r/CA+NtrhTyE4J-C2=hSUXYc+NJBsQeYvbwJb+-ROHE1QQP-_gYzg@mail.gmail.com/
Thanks,
Ting-Han
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] scripts/gdb: Fix radix tree lookup pointer handling
2026-10-07 9:31 [PATCH] scripts/gdb: Fix radix tree lookup pointer handling Ting-Han Hou
2026-10-09 18:10 ` Ting-Han Hou
@ 2026-10-10 21:17 ` Andrew Morton
2026-10-11 2:30 ` [PATCH v2 0/2] scripts/gdb: fix radix tree lookup and stack depot diagnostic Ting-Han Hou
1 sibling, 1 reply; 7+ messages in thread
From: Andrew Morton @ 2026-10-10 21:17 UTC (permalink / raw)
To: Ting-Han Hou; +Cc: Jan Kiszka, Kieran Bingham, linux-kernel
On Wed, 7 Oct 2026 02:31:24 -0700 Ting-Han Hou <ue081723@gmail.com> wrote:
> lookup() casts each slot to a pointer to a node pointer and then
> dereferences it. Since the slot already contains an entry pointer,
> this reads the entry's contents as another pointer instead of
> returning the entry itself.
>
> Internal entries also need their tag bits removed before descending
> to the next node.
>
> Return non-internal entries directly and use entry_to_node() when
> descending through internal entries. Remove the explicit shift
> counter, as traversal now follows the entry type.
>
> Fixes: b7235d6bb516 ("scripts/gdb: add a Radix Tree Parser")
> Assisted-by: LLM
> Signed-off-by: Ting-Han Hou <ue081723@gmail.com>
> ---
> Tested with GDB 15.1 against standalone GCC-built C fixtures using the
> xarray/xa_node field layout, with XA_CHUNK_SHIFT=4 and 6. These are
> synthetic debugger fixtures, not a booted kernel or a kernel core dump.
>
> Before the fix, a one-level lookup returned the payload 0x12345678
> instead of its address; a two-level lookup missed a populated slot.
> After the fix, all 33 checks passed for each slot configuration (66
> total), including empty/direct roots, one- to three-level trees,
> zero-filled payloads, value entries, absent/out-of-range indices,
> struct/pointer roots, and the GDB convenience function.
>
> This does not test multi-index entries or concurrent updates/retry
> entries. The change is limited to the debugger lookup helper.
Thanks. Sashiko expressed a concern:
https://sashiko.dev/#/patchset/CA+NtrhTyE4J-C2=hSUXYc+NJBsQeYvbwJb+-ROHE1QQP-_gYzg@mail.gmail.com
your other patch doesn't apply due to word-wrapping.
Please turn these into a 2-patch series.
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 0/2] scripts/gdb: fix radix tree lookup and stack depot diagnostic
2026-10-10 21:17 ` Andrew Morton
@ 2026-10-11 2:30 ` Ting-Han Hou
2026-10-11 2:30 ` [PATCH v2 1/2] scripts/gdb: Fix radix tree lookup pointer handling Ting-Han Hou
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Ting-Han Hou @ 2026-10-11 2:30 UTC (permalink / raw)
To: Andrew Morton; +Cc: Jan Kiszka, Kieran Bingham, Kuan-Ying Lee, linux-kernel
Andrew, here are the two scripts/gdb fixes as a series, as requested.
Patch 1 fixes $lx_radix_tree_lookup(), which dereferenced slot contents
as if they were node pointers. v2 also handles the XArray sibling,
retry and zero entries that Sashiko pointed out: they carry the
internal tag but are not node pointers, and the v1 patch raised a GDB
MemoryError on them. Large folios in the page cache make sibling
entries common, so this was worth fixing rather than documenting.
Patch 2 is unchanged from v1 apart from being resent with git
send-email, since the earlier copy was word-wrapped by the mail
client. Sorry about that.
Both patches are tested against standalone GCC-built fixtures that use
the kernel struct layouts (details below the --- line of each patch),
not against a running kernel.
v1:
https://lore.kernel.org/r/CA+NtrhTyE4J-C2=hSUXYc+NJBsQeYvbwJb+-ROHE1QQP-_gYzg@mail.gmail.com/
https://lore.kernel.org/r/CA+NtrhTQXdQRWe+pRJHyKxPPgsF7Ob5R41Xm3dAJ54OfT2_z5A@mail.gmail.com/
Ting-Han Hou (2):
scripts/gdb: Fix radix tree lookup pointer handling
scripts/gdb: Fix stack depot out-of-bounds diagnostic
scripts/gdb/linux/radixtree.py | 66 ++++++++++++++++++++-------------
scripts/gdb/linux/stackdepot.py | 2 +-
2 files changed, 41 insertions(+), 27 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/2] scripts/gdb: Fix radix tree lookup pointer handling
2026-10-11 2:30 ` [PATCH v2 0/2] scripts/gdb: fix radix tree lookup and stack depot diagnostic Ting-Han Hou
@ 2026-10-11 2:30 ` Ting-Han Hou
2026-10-11 2:30 ` [PATCH v2 2/2] scripts/gdb: Fix stack depot out-of-bounds diagnostic Ting-Han Hou
2026-10-11 3:58 ` [PATCH v2 0/2] scripts/gdb: fix radix tree lookup and stack depot diagnostic Andrew Morton
2 siblings, 0 replies; 7+ messages in thread
From: Ting-Han Hou @ 2026-10-11 2:30 UTC (permalink / raw)
To: Andrew Morton; +Cc: Jan Kiszka, Kieran Bingham, Kuan-Ying Lee, linux-kernel
lookup() casts each slot to a pointer to a node pointer and then
dereferences it. Since the slot already contains an entry pointer,
this reads the entry's contents as another pointer instead of
returning the entry itself. Internal entries also keep their tag
bits when descending, so multi-level trees are read from the wrong
address and $lx_radix_tree_lookup() fails with a memory error.
Sibling, retry and zero entries carry the same internal tag but are
small integers rather than node pointers, so the tag alone must not
decide whether to descend. A page cache populated with large folios
has a sibling entry in every slot but the first of each folio.
Walk the tree the way xas_load() does: descend only through entries
that xa_is_node() would accept, follow sibling entries to the
canonical slot of a multi-index entry, and treat retry and zero
entries like empty slots since they hold no data. Remove the explicit
shift counter, as traversal now follows the entry type.
Fixes: b7235d6bb516 ("scripts/gdb: add a Radix Tree Parser")
Assisted-by: LLM
Signed-off-by: Ting-Han Hou <ue081723@gmail.com>
---
Changes in v2:
- Handle XArray sibling, retry and zero entries, which carry the
internal tag but are not node pointers. v1 passed them to
entry_to_node() and the next node['shift'] read raised a GDB
MemoryError (reported by Sashiko). Added is_node(), is_sibling()
and sibling_offset() helpers modelled on xa_is_node(),
xa_is_sibling() and xa_to_sibling().
- Sent as patch 1/2 of a series with git send-email.
Tested with GDB 17.1 against standalone GCC-built C fixtures using the
xarray/xa_node field layout, with XA_CHUNK_SHIFT=4 and 6. These are
synthetic debugger fixtures, not a booted kernel or a kernel core dump.
The fixtures cover empty/direct roots, one- to three-level trees,
zero-filled payloads, value entries, absent/out-of-range indices,
struct/pointer roots, the GDB convenience function, and multi-index
entries with sibling entries both in a leaf node and at the root level
of a two-level tree, plus retry and zero entries. Before this patch 26
of the 38 checks fail for each chunk size (wrong value, missed slot or
MemoryError); the v1 patch fails the 7 sibling/retry/zero checks with
a MemoryError; with this patch all 38 pass for both chunk sizes.
The lx-radix-tree iterator in the same file has not been changed.
scripts/gdb/linux/radixtree.py | 66 ++++++++++++++++++++--------------
1 file changed, 40 insertions(+), 26 deletions(-)
diff --git a/scripts/gdb/linux/radixtree.py b/scripts/gdb/linux/radixtree.py
index bc2954e45c32..8ce9936dfa50 100644
--- a/scripts/gdb/linux/radixtree.py
+++ b/scripts/gdb/linux/radixtree.py
@@ -27,6 +27,24 @@ def entry_to_node(node):
indirect_ptr = node.cast(long_type) & ~constants.LX_RADIX_TREE_INTERNAL_NODE
return indirect_ptr.cast(radix_tree_node_type.get_type().pointer())
+def is_node(entry):
+ # Like xa_is_node(): internal entries below 4096 are sibling, retry
+ # or zero entries rather than pointers to a struct xa_node.
+ ulong_type = utils.get_ulong_type()
+ return is_internal_node(entry) and entry.cast(ulong_type) > 4096
+
+def is_sibling(entry):
+ # Like xa_is_sibling(): a multi-index entry occupies several slots,
+ # and all but the first hold a sibling entry that encodes the offset
+ # of the first one.
+ ulong_type = utils.get_ulong_type()
+ return is_internal_node(entry) and entry.cast(ulong_type) < \
+ xa_mk_internal(constants.LX_RADIX_TREE_MAP_SIZE - 1)
+
+def sibling_offset(entry):
+ ulong_type = utils.get_ulong_type()
+ return int(entry.cast(ulong_type)) >> 2
+
def node_maxindex(node):
return (constants.LX_RADIX_TREE_MAP_SIZE << node['shift']) - 1
@@ -40,39 +58,35 @@ def resolve_root(root):
def lookup(root, index):
root = resolve_root(root)
- node = root['xa_head']
- if node == 0:
- return None
+ entry = root['xa_head']
- if not (is_internal_node(node)):
- if (index > 0):
+ if is_node(entry):
+ node = entry_to_node(entry)
+ if index > node_maxindex(node):
return None
- return node
- node = entry_to_node(node)
- maxindex = node_maxindex(node)
-
- if (index > maxindex):
- return None
-
- shift = node['shift'] + constants.LX_RADIX_TREE_MAP_SHIFT
-
- while True:
- offset = (index >> node['shift']) & constants.LX_RADIX_TREE_MAP_MASK
- slot = node['slots'][offset]
+ # Walk down the tree like xas_load() does.
+ while True:
+ offset = (index >> node['shift']) & constants.LX_RADIX_TREE_MAP_MASK
+ entry = node['slots'][offset]
- if slot == 0:
- return None
+ while is_sibling(entry):
+ entry = node['slots'][sibling_offset(entry)]
+ if node['shift'] and is_node(entry):
+ # xas_descend() turns this into a retry entry.
+ return None
- node = slot.cast(node.type.pointer()).dereference()
- if node == 0:
- return None
+ if not is_node(entry) or node['shift'] == 0:
+ break
+ node = entry_to_node(entry)
+ elif index > 0:
+ return None
- shift -= constants.LX_RADIX_TREE_MAP_SHIFT
- if (shift <= 0):
- break
+ # Empty slots and retry or zero entries hold no data.
+ if entry == 0 or is_internal_node(entry):
+ return None
- return node
+ return entry
def descend(parent, index):
offset = (index >> int(parent["shift"])) & constants.LX_RADIX_TREE_MAP_MASK
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 2/2] scripts/gdb: Fix stack depot out-of-bounds diagnostic
2026-10-11 2:30 ` [PATCH v2 0/2] scripts/gdb: fix radix tree lookup and stack depot diagnostic Ting-Han Hou
2026-10-11 2:30 ` [PATCH v2 1/2] scripts/gdb: Fix radix tree lookup pointer handling Ting-Han Hou
@ 2026-10-11 2:30 ` Ting-Han Hou
2026-10-11 3:58 ` [PATCH v2 0/2] scripts/gdb: fix radix tree lookup and stack depot diagnostic Andrew Morton
2 siblings, 0 replies; 7+ messages in thread
From: Ting-Han Hou @ 2026-10-11 2:30 UTC (permalink / raw)
To: Andrew Morton; +Cc: Jan Kiszka, Kieran Bingham, Kuan-Ying Lee, linux-kernel
The pool_index field was renamed to pool_index_plus_1, but the
out-of-bounds diagnostic in stack_depot_fetch() still accesses the old
field. Looking up a handle whose pool index is outside stack_pools
therefore raises "There is no member named pool_index" instead of
printing the diagnostic and returning an empty result.
Use the already decoded pool_index variable in the diagnostic.
Fixes: 9d938f40b228 ("scripts/gdb: rename pool_index to pool_index_plus_1")
Assisted-by: LLM
Signed-off-by: Ting-Han Hou <ue081723@gmail.com>
---
Changes in v2:
- No code change. Resent with git send-email as patch 2/2 of the
series; the v1 posting was word-wrapped by the mail client and did
not apply.
Tested the lx-stack_depot_lookup command with GDB 17.1 and a GCC-built
ELF fixture using the handle bitfield layout for 4 KiB pages. The
fixture contains one pool and a valid stack record. This is a synthetic
debugger fixture, not a booted kernel or a kernel core dump.
Handles 2 and 0xffff fail with the missing-member exception before the
fix and print the correct out-of-bounds diagnostic after it. Handle 1
still prints the expected instructions, and handle 0 retains its
existing rejection.
scripts/gdb/linux/stackdepot.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/gdb/linux/stackdepot.py b/scripts/gdb/linux/stackdepot.py
index 37313a5a51a0..aaa18af25cd1 100644
--- a/scripts/gdb/linux/stackdepot.py
+++ b/scripts/gdb/linux/stackdepot.py
@@ -39,7 +39,7 @@ def stack_depot_fetch(handle):
pool_index = parts['pool_index_plus_1'] - 1
if pool_index >= pools_num:
- gdb.write("pool index %d out of bounds (%d) for stack id 0x%08x\n" % (parts['pool_index'], pools_num, handle))
+ gdb.write("pool index %d out of bounds (%d) for stack id 0x%08x\n" % (pool_index, pools_num, handle))
return gdb.Value(0), 0
stack_pools = gdb.parse_and_eval('stack_pools')
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 0/2] scripts/gdb: fix radix tree lookup and stack depot diagnostic
2026-10-11 2:30 ` [PATCH v2 0/2] scripts/gdb: fix radix tree lookup and stack depot diagnostic Ting-Han Hou
2026-10-11 2:30 ` [PATCH v2 1/2] scripts/gdb: Fix radix tree lookup pointer handling Ting-Han Hou
2026-10-11 2:30 ` [PATCH v2 2/2] scripts/gdb: Fix stack depot out-of-bounds diagnostic Ting-Han Hou
@ 2026-10-11 3:58 ` Andrew Morton
2 siblings, 0 replies; 7+ messages in thread
From: Andrew Morton @ 2026-10-11 3:58 UTC (permalink / raw)
To: Ting-Han Hou; +Cc: Jan Kiszka, Kieran Bingham, Kuan-Ying Lee, linux-kernel
On Sun, 11 Oct 2026 10:30:42 +0800 Ting-Han Hou <ue081723@gmail.com> wrote:
> Andrew, here are the two scripts/gdb fixes as a series, as requested.
>
> Patch 1 fixes $lx_radix_tree_lookup(), which dereferenced slot contents
> as if they were node pointers. v2 also handles the XArray sibling,
> retry and zero entries that Sashiko pointed out: they carry the
> internal tag but are not node pointers, and the v1 patch raised a GDB
> MemoryError on them. Large folios in the page cache make sibling
> entries common, so this was worth fixing rather than documenting.
>
> Patch 2 is unchanged from v1 apart from being resent with git
> send-email, since the earlier copy was word-wrapped by the mail
> client. Sorry about that.
>
> Both patches are tested against standalone GCC-built fixtures that use
> the kernel struct layouts (details below the --- line of each patch),
> not against a running kernel.
Thanks, I'll queue these for testing while awaiting maintainer review.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-11 3:59 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-07 9:31 [PATCH] scripts/gdb: Fix radix tree lookup pointer handling Ting-Han Hou
2026-10-09 18:10 ` Ting-Han Hou
2026-10-10 21:17 ` Andrew Morton
2026-10-11 2:30 ` [PATCH v2 0/2] scripts/gdb: fix radix tree lookup and stack depot diagnostic Ting-Han Hou
2026-10-11 2:30 ` [PATCH v2 1/2] scripts/gdb: Fix radix tree lookup pointer handling Ting-Han Hou
2026-10-11 2:30 ` [PATCH v2 2/2] scripts/gdb: Fix stack depot out-of-bounds diagnostic Ting-Han Hou
2026-10-11 3:58 ` [PATCH v2 0/2] scripts/gdb: fix radix tree lookup and stack depot diagnostic Andrew Morton
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®