Skip to content

Commit 9959862

Browse files
r41k0uclaude
andcommitted
Core: Widen any sub-64-bit context field instead of special-casing xdp_md
Reading a context field narrower than a register was hardcoded to struct_xdp_md's i32 fields in three places that had to agree with each other: the destination alloca in allocation_pass, the zext in load_ctx_field, and the store in assign_pass. struct pt_regs' cs and ss are 2 bytes, so a second special case would have been needed. Replace all three with the general rule: a context field is loaded at its natural width and zero-extended to i64, so its destination is i64. allocation_pass gains the same context discriminator load_ctx_field uses (a context argument has no alloca of its own), so non-context field reads, which go through load_struct_field and bpf_probe_read_kernel, keep their natural width and are untouched. struct_xdp_md's generated IR is byte-identical, as is every other vmlinux/xdp test program's; the only diff in the whole corpus remains the four new globals from the previous commit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 4ff3ce2 commit 9959862

3 files changed

Lines changed: 28 additions & 22 deletions

File tree

pythonbpf/allocation_pass.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,9 @@ def _allocate_for_attribute(
343343
if VmlinuxHandlerRegistry.is_vmlinux_struct(struct_type.__name__):
344344
# Handle vmlinux struct field access
345345
vmlinux_struct_name = struct_type.__name__
346+
# Same discriminator handle_vmlinux_struct_field uses: a context
347+
# argument has no alloca of its own.
348+
is_context_field = local_sym_tab[struct_var].var is None
346349
if not VmlinuxHandlerRegistry.has_field(vmlinux_struct_name, field_name):
347350
logger.error(
348351
f"Field '{field_name}' not found in vmlinux struct '{vmlinux_struct_name}'"
@@ -364,16 +367,15 @@ def _allocate_for_attribute(
364367
field_size_bits = field_size_bytes * 8
365368

366369
if field_size_bits in [8, 16, 32, 64]:
367-
# Special case: struct_xdp_md i32 fields should allocate as i64
368-
# because load_ctx_field will zero-extend them to i64
369-
if (
370-
vmlinux_struct_name == "struct_xdp_md"
371-
and field_size_bits == 32
372-
):
370+
# Sub-register-width context fields allocate as i64,
371+
# because load_ctx_field zero-extends them to i64.
372+
# Non-context fields go through load_struct_field, which
373+
# keeps them at their natural width.
374+
if is_context_field and field_size_bits < 64:
373375
actual_ir_type = ir.IntType(64)
374376
logger.info(
375-
f"Allocating {var_name} as i64 for i32 field from struct_xdp_md.{field_name} "
376-
"(will be zero-extended during load)"
377+
f"Allocating {var_name} as i64 for i{field_size_bits} field from "
378+
f"{vmlinux_struct_name}.{field_name} (will be zero-extended during load)"
377379
)
378380
else:
379381
actual_ir_type = ir.IntType(field_size_bits)

pythonbpf/assign_pass.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,22 +185,24 @@ def handle_variable_assignment(
185185
return False
186186
if isinstance(val_type, Field):
187187
logger.info("Handling assignment to struct field")
188-
# Special handling for struct_xdp_md i32 fields that are zero-extended to i64
189-
# The load_ctx_field already extended them, so val is i64 but val_type.type shows c_uint
188+
field_ir_type = ctypes_to_ir(val_type.type.__name__)
189+
# Sub-register-width context fields are zero-extended to i64 by
190+
# load_ctx_field, so val is already i64 even though the field type
191+
# says otherwise (c_uint for xdp_md, c_ushort for pt_regs.cs/ss).
190192
if (
191-
hasattr(val_type, "type")
192-
and val_type.type.__name__ == "c_uint"
193+
isinstance(field_ir_type, ir.IntType)
194+
and field_ir_type.width < 64
193195
and isinstance(var_type, ir.IntType)
194196
and var_type.width == 64
195197
):
196-
# This is the struct_xdp_md case - value is already i64
197198
builder.store(val, var_ptr)
198199
logger.info(
199-
f"Assigned zero-extended struct_xdp_md i32 field to {var_name} (i64)"
200+
f"Assigned zero-extended i{field_ir_type.width} context field "
201+
f"to {var_name} (i64)"
200202
)
201203
return True
202204
# TODO: handling only ctype struct fields for now. Handle other stuff too later.
203-
elif var_type == ctypes_to_ir(val_type.type.__name__):
205+
elif var_type == field_ir_type:
204206
builder.store(val, var_ptr)
205207
logger.info(f"Assigned ctype struct field to {var_name}")
206208
return True

pythonbpf/vmlinux_parser/vmlinux_exports_handler.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ def load_ctx_field(builder, ctx_arg, offset_global, field_data, struct_name=None
315315

316316
# Determine the appropriate IR type based on field information
317317
int_width = 64 # Default to 64-bit
318-
needs_zext = False # Track if we need zero-extension for xdp_md
318+
needs_zext = False # Track if we need zero-extension to a full register
319319

320320
if field_data is not None:
321321
# Try to determine the size from field metadata
@@ -328,12 +328,14 @@ def load_ctx_field(builder, ctx_arg, offset_global, field_data, struct_name=None
328328
int_width = field_size_bits
329329
logger.info(f"Determined field size: {int_width} bits")
330330

331-
# Special handling for struct_xdp_md i32 fields
332-
# Load as i32 but extend to i64 before storing
333-
if struct_name == "struct_xdp_md" and int_width == 32:
331+
# Context fields are loaded at their natural width and
332+
# widened to a full 64-bit register, so that everything
333+
# downstream sees one uniform integer type.
334+
if int_width < 64:
334335
needs_zext = True
335336
logger.info(
336-
"struct_xdp_md i32 field detected, will zero-extend to i64"
337+
f"i{int_width} field {struct_name} detected, "
338+
"will zero-extend to i64"
337339
)
338340
else:
339341
logger.warning(
@@ -363,10 +365,10 @@ def load_ctx_field(builder, ctx_arg, offset_global, field_data, struct_name=None
363365
# Load and return the value
364366
value = builder.load(typed_ptr)
365367

366-
# Zero-extend i32 to i64 for struct_xdp_md fields
368+
# Widen sub-register-width context fields to i64
367369
if needs_zext:
368370
value = builder.zext(value, ir.IntType(64))
369-
logger.info("Zero-extended i32 value to i64 for struct_xdp_md field")
371+
logger.info(f"Zero-extended i{int_width} context field value to i64")
370372

371373
return value
372374

0 commit comments

Comments
 (0)