Skip to content

Commit 7f5773e

Browse files
r41k0uclaude
andcommitted
Core: Make XDP return names respect local and global shadowing
handle_return consulted the hardcoded XDP action table before any symbol table, so with a local XDP_PASS = 55 in scope, "return XDP_PASS" silently returned 2. clang treats the equivalent C (a local shadowing an enum constant) as the local winning, and every other resolution site in the compiler already resolves local, then global, then vmlinux -- the return fast path was the one place with the order inverted. It now applies only when the name is bound in neither table. Also warn when a @bpfglobal shadows a vmlinux enum constant. C rejects that outright as a redefinition; PythonBPF follows the rebinding semantics the Python file itself has (the global wins, consistently), but the collision is worth a compile-time warning rather than silence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L1PX8EuP9C3o3veWGA84RF
1 parent 20b9bb9 commit 7f5773e

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

pythonbpf/functions/functions_pass.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,19 @@ def handle_return(builder, stmt, local_sym_tab, ret_type, compilation_context=No
265265
logger.info(f"Handling return statement: {ast.dump(stmt)}")
266266
if stmt.value is None:
267267
return handle_none_return(builder)
268-
elif isinstance(stmt.value, ast.Name) and is_xdp_name(stmt.value.id):
268+
elif (
269+
isinstance(stmt.value, ast.Name)
270+
and is_xdp_name(stmt.value.id)
271+
and stmt.value.id not in local_sym_tab
272+
and (
273+
compilation_context is None
274+
or stmt.value.id not in compilation_context.bpf_globals
275+
)
276+
):
277+
# The XDP fast path resolves names like XDP_PASS from the helper
278+
# constant table, but only as a fallback: a local or @bpfglobal of the
279+
# same name shadows it, mirroring C (a local shadows an enum constant)
280+
# and the resolution order everywhere else in the compiler.
269281
return handle_xdp_return(stmt, builder, ret_type)
270282
else:
271283
# Fallback for now if ctx not passed, but caller should pass it

pythonbpf/globals_pass.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import logging
77
from .type_deducer import ctypes_to_ir
88
from .debuginfo import DebugInfoGenerator
9+
from .expr import VmlinuxHandlerRegistry
910
from .debuginfo import dwarf_constants as dc
1011

1112
logger: Logger = logging.getLogger(__name__)
@@ -169,6 +170,17 @@ def globals_processing(tree, compilation_context):
169170
)
170171
):
171172
gvar = _emit_global(compilation_context.module, node, name)
173+
if VmlinuxHandlerRegistry.handle_name(name) is not None:
174+
# C rejects this outright ("redefinition as different
175+
# kind of symbol"); Python's rebinding semantics let
176+
# the global win, and resolution order (local, then
177+
# global, then vmlinux) applies it consistently. Warn
178+
# so the shadowing is at least never silent.
179+
logger.warning(
180+
f"@bpfglobal '{name}' shadows a vmlinux enum "
181+
f"constant of the same name; reads of '{name}' "
182+
f"will use the global"
183+
)
172184
if isinstance(gvar.value_type, ir.IntType):
173185
compilation_context.bpf_globals[name] = BpfGlobalSymbol(
174186
var=gvar,

0 commit comments

Comments
 (0)