Handle functions passed as an argument - #79
Merged
rikvanriel merged 7 commits intoAug 28, 2026
Merged
Conversation
extract_functions_with_calls returns three vectors as a bare tuple, so every caller repeats their order and a reader has to count positions to tell which is which. Two callers destructure it, one of them discarding two elements. Return a struct instead. No behavioural change. Assisted-by: claw:claude-opus-5 Signed-off-by: Rik van Riel <[email protected]>
A driver's interrupt handler is reachable only because its name was passed to
request_irq, and nothing records that. `callers nic_intr` answers that nothing
calls it, which is how a handler looks right before someone concludes it is
dead code. The same holds for INIT_WORK, timer_setup, kthread_run and every
other registrar that takes a function by name: 53,918 such arguments go to a
callee that is itself a function, 146,295 to a macro.
Record one row per call argument that names an identifier the file does not
bind to a value. Whether that identifier belongs to a function cannot be
decided while indexing, because the answer usually lives in another file and
files are indexed in parallel; the reader decides it against the functions
table.
Excluded at extraction, all decidable from the file alone: a name the file
declares as a local, a parameter or a file-scope variable, since `min_t(u32,
len, size)` names no function even where some tree defines one called `len`;
a call naming itself, which is recursion; an export or `container_of`, which
name a function without handing it anywhere; and `module_init`, whose
initcall is recorded already.
Indexing the kernel at 0595459f:
$ semcode-index --database /tmp/idx-a1/.semcode.db
$ du -sm /tmp/idx-a1/.semcode.db/argument_functions.lance
41 /tmp/idx-a1/.semcode.db/argument_functions.lance
1,921,278 rows over 37,285 files, of which 242,279 name a function. The
remaining rows name enum constants and macros, and cost 41 MB against an
index of 903 MB.
Assisted-by: claw:claude-opus-5
Signed-off-by: Rik van Riel <[email protected]>
`registrations e1000_intr` said the handler is installed nowhere, because
nothing joined the argument it is passed as. A reader then has to grep for the
name to find out that the driver hands it to request_irq.
Report those calls, keeping them apart from struct-member installations: the
two are different claims, and only the second names a slot.
(semcode) registrations e1000_intr
Finding where e1000_intr is installed
=== Handed to ===
1 calls are handed it as an argument:
1. request_irq() argument 1 at drivers/net/ethernet/intel/e1000e/netdev.c:2190 in e1000_request_irq
which is where the driver installs it:
err = request_irq(adapter->pdev->irq, e1000_intr, IRQF_SHARED,
netdev->name, netdev);
An argument only counts where its name belongs to a function at the revision
asked about, so an enum constant sitting in the same position reports nothing.
Over the kernel at 0595459f that leaves 242,279 arguments naming 57,541
distinct functions.
Being handed to a call is not being called by it: `callers e1000_intr` still
reports only e1000_netpoll, since whether request_irq invokes what it is given
is a fact about request_irq.
Assisted-by: claw:claude-opus-5
Signed-off-by: Rik van Riel <[email protected]>
A registrar is not a name on a list. request_threaded_irq stores the handler it is given in irqaction::handler; request_irq is a registrar only because it hands its own parameter to that one; every subsystem has its own, and any enumeration of them would miss those. Read a body and say what it does with one named parameter: stores it in a member, hands it to another call, or invokes it. Intraprocedural, one body at a time, no value tracking beyond the parameter's own name. The wrapper case falls out of following the handover. Nothing consumes this yet. Assisted-by: claw:claude-opus-5 Signed-off-by: Rik van Riel <[email protected]>
Being handed to request_irq is only useful if it says where the handler ends
up, and request_irq stores nothing: it hands its parameter to
request_threaded_irq, which writes it into irqaction::handler. Reporting the
first hop alone leaves the reader to walk the wrappers by hand.
Follow the parameter until a body stores it in a member or calls it, and print
the route:
(semcode) registrations e1000_intr
=== Handed to ===
1 calls are handed it as an argument:
1. request_irq() argument 1 at drivers/net/ethernet/intel/e1000e/netdev.c:2190 in e1000_request_irq
installs it in irqaction::handler through request_irq(handler) -> request_threaded_irq(handler)
The search is breadth first because a body usually hands the same parameter to
more than one call, an error path among them, and depth first down the first
of those reaches nothing: devm_request_irq hands its handler to
devm_request_result before it hands it to devm_request_threaded_irq, and only
the second leads to irqaction::handler, four hops away.
A position only qualifies if it takes a function pointer, following one
typedef, since otherwise an integer written into an integer member reports
nla_put_u32 installing something in nlattr::nla_type, which it does not.
Over the 6,000 busiest positions in the kernel at 0595459f, 79 take a function
and 64 of those reach a member or a call, covering 10,924 call sites:
$ handover_probe /tmp/idx-a1/.semcode.db ~/linux 0595459f 6000
of the 6000 busiest positions (1445058 call sites):
take a function at that position: 79
reach a member: 64 positions, 10924 sites
Both hops stay in the output. A two-hop claim printed as a one-hop fact is
harder to check than no answer at all.
Assisted-by: claw:claude-opus-5
Signed-off-by: Rik van Riel <[email protected]>
INIT_WORK, timer_setup and call_rcu do not call what they are given; kref_put
does. Both read as one edge, and the difference is the whole question for
anyone reasoning about a race: two chains that differ only in when the callee
runs are the same graph to a reader.
Storing a function in a member defers it by construction, since whoever
dispatches through that member chooses the time, and invoking it happens
before the call returns. The distinction is already in what the body does, so
it is derived rather than stored, and the answer states it:
installs it in irqaction::handler, called later through
request_irq(handler) -> request_threaded_irq(handler)
Assisted-by: claw:claude-opus-5
Signed-off-by: Rik van Riel <[email protected]>
`callers neigh_hash_free_rcu` reported that nothing calls it. The callback runs
from RCU, and the only thing that says which objects it frees is the head it
was queued on:
call_rcu(&old_nht->rcu, neigh_hash_free_rcu);
Following the callback argument reaches rcu_head::func, which is where every
RCU callback lands and therefore says nothing about this one. The type is in
the other argument.
Record it where the file declares the base's type, and report it beside the
slot:
(semcode) registrations neigh_hash_free_rcu
2. call_rcu() argument 1 at net/core/neighbour.c:618 in neigh_hash_grow
attached to neigh_hash_table::rcu
installs it in rcu_head::func, called later through call_rcu(func) -> __call_rcu_common(func)
Not specific to RCU: the same shape records which object a completion, a timer
or a work item belongs to. Over the kernel at 0595459f, 246,809 of 1,921,278
rows name a subject; the rest pass no object, or pass one the file does not
declare.
Assisted-by: claw:claude-opus-5
Signed-off-by: Rik van Riel <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Handle functions passed as an argument