99 field_int_type ,
1010 is_ctypes ,
1111 IntTy ,
12+ PktPtrTy ,
1213 int_literal_type ,
1314 signedness ,
1415)
2425 get_base_type_and_depth ,
2526)
2627from .vmlinux_registry import VmlinuxHandlerRegistry
27- from .packet_pointer import MAX_PACKET_OFF , is_packet_pointer
28+ from .packet_pointer import MAX_PACKET_OFF , packet_type
2829from ..vmlinux_parser .dependency_node import Field
2930
3031logger : Logger = logging .getLogger (__name__ )
@@ -123,6 +124,11 @@ def _handle_attribute_expr(
123124 expr , local_sym_tab , None , builder
124125 )
125126 if vmlinux_result is not None :
127+ pkt_ty = packet_type (expr , local_sym_tab )
128+ if pkt_ty is not None :
129+ # A packet-pointer field: the value is the loaded
130+ # pointer, and the descriptor says so from here on.
131+ return vmlinux_result [0 ], pkt_ty
126132 return vmlinux_result
127133 else :
128134 raise RuntimeError ("Vmlinux struct did not process successfully" )
@@ -200,6 +206,8 @@ def _descriptor(val, ty):
200206 value unless the descriptor is itself an integer type, sign from the
201207 descriptor (an IntTy, a vmlinux Field, or plain -> signed). None when the
202208 value is not an integer at all."""
209+ if isinstance (ty , PktPtrTy ):
210+ return ty # a packet pointer keeps its kind, never re-ranked
203211 if isinstance (ty , ir .IntType ):
204212 return IntTy (ty .width , signedness (ty ))
205213 field = field_int_type (ty )
@@ -285,18 +293,16 @@ def _handle_binary_op_impl(func, compilation_context, rval, builder, local_sym_t
285293 narrowed to it -- so u32 * u32 wraps at 32 bits even though the arithmetic
286294 itself runs in an i64 register. Returns (value, IntTy)."""
287295 op = rval .op
288- left_pkt = is_packet_pointer (rval .left , local_sym_tab )
289- right_pkt = is_packet_pointer (rval .right , local_sym_tab )
290296 left , left_ty = get_typed_operand (
291297 func , compilation_context , rval .left , builder , local_sym_tab
292298 )
293299 right , right_ty = get_typed_operand (
294300 func , compilation_context , rval .right , builder , local_sym_tab
295301 )
296- if left_pkt or right_pkt :
297- return _packet_pointer_binop (
298- builder , rval , left , left_ty , left_pkt , right , right_ty , right_pkt
299- )
302+ if isinstance ( left_ty , PktPtrTy ) or isinstance ( right_ty , PktPtrTy ) :
303+ # Before the usual arithmetic conversions, which would re-rank the
304+ # pointer as an ordinary integer and drop its kind.
305+ return _packet_pointer_binop ( builder , rval , left , left_ty , right , right_ty )
300306 result_ty = usual_arithmetic_conversions (left_ty , right_ty )
301307 logger .info (
302308 f"binop { type (op ).__name__ } : { left_ty .describe ()} x { right_ty .describe ()} "
@@ -308,21 +314,20 @@ def _handle_binary_op_impl(func, compilation_context, rval, builder, local_sym_t
308314 return canonicalise (builder , result , result_ty ), result_ty
309315
310316
311- def _packet_pointer_binop (
312- builder , rval , left , left_ty , left_pkt , right , right_ty , right_pkt
313- ):
317+ def _packet_pointer_binop (builder , rval , left , left_ty , right , right_ty ):
314318 """A binary operation with a packet-pointer operand (see packet_pointer.py):
315- 64-bit, never ranked as the field's declared u32, with the offset taken as
316- an unsigned 16-bit value so the verifier can track the packet range."""
319+ 64-bit, never ranked as an ordinary integer, with the offset taken as an
320+ unsigned 16-bit value so the verifier can track the packet range. The
321+ result of pointer +/- offset is a pointer of the same kind; pointer -
322+ pointer is a plain 64-bit length."""
317323 op = rval .op
318324 where = f"line { rval .lineno } : { ast .unparse (rval )} "
319- ptr_ty = IntTy (64 , False )
325+ left_pkt = isinstance (left_ty , PktPtrTy )
326+ right_pkt = isinstance (right_ty , PktPtrTy )
320327 if left_pkt and right_pkt :
321328 if isinstance (op , ast .Sub ):
322329 # data_end - data: a length, an ordinary 64-bit scalar.
323- left = convert (builder , left , left_ty , ptr_ty )
324- right = convert (builder , right , right_ty , ptr_ty )
325- return builder .sub (left , right ), ptr_ty
330+ return builder .sub (left , right ), IntTy (64 , False )
326331 raise SyntaxError (
327332 f"only subtraction is defined between packet pointers ({ where } )"
328333 )
@@ -333,24 +338,24 @@ def _packet_pointer_binop(
333338 if right_pkt and isinstance (op , ast .Sub ):
334339 raise SyntaxError (f"cannot subtract a packet pointer from an offset ({ where } )" )
335340
336- ptr , ptr_src_ty , off , off_ty = (
337- (left , left_ty , right , right_ty )
338- if left_pkt
339- else (right , right_ty , left , left_ty )
340- )
341+ ptr , ptr_ty , off = (left , left_ty , right ) if left_pkt else (right , right_ty , left )
342+ if ptr_ty .kind == "pkt_end" :
343+ # The verifier only compares against the end of the packet.
344+ raise SyntaxError (
345+ f"a packet-end pointer can only be compared, not offset ({ where } )"
346+ )
341347 if isinstance (off , ir .Constant ) and isinstance (off .constant , int ):
342348 if not 0 <= off .constant <= MAX_PACKET_OFF :
343349 raise SyntaxError (
344350 f"packet offset { off .constant } is outside 0..{ MAX_PACKET_OFF } "
345351 f"({ where } ); subtract instead of adding a negative offset"
346352 )
347- ptr = convert (builder , ptr , ptr_src_ty , ptr_ty )
348353 # The offset as u16, widened with zero-extension: a value the verifier can
349354 # bound to [0, 0xffff].
350355 off = builder .trunc (off , ir .IntType (16 )) if off .type .width > 16 else off
351356 off = builder .zext (off , ir .IntType (64 )) if off .type .width < 64 else off
352357 result = builder .add (ptr , off ) if isinstance (op , ast .Add ) else builder .sub (ptr , off )
353- return result , ptr_ty
358+ return result , PktPtrTy ( ptr_ty . kind )
354359
355360
356361def _handle_binary_op (
@@ -462,6 +467,13 @@ def _handle_compare(func, compilation_context, builder, cond, local_sym_tab):
462467 lhs , lhs_ty = lhs
463468 rhs , rhs_ty = rhs
464469 lhs_desc , rhs_desc = _descriptor (lhs , lhs_ty ), _descriptor (rhs , rhs_ty )
470+ if isinstance (lhs_desc , PktPtrTy ) or isinstance (rhs_desc , PktPtrTy ):
471+ # A packet bounds check (data + n > data_end): the verifier accepts
472+ # pointer comparisons only at 64 bits, and they are unsigned.
473+ u64 = IntTy (64 , False )
474+ lhs = convert (builder , lhs , lhs_desc , u64 )
475+ rhs = convert (builder , rhs , rhs_desc , u64 )
476+ return handle_comparator (func , builder , cond .ops [0 ], lhs , rhs , signed = False )
465477 if lhs_desc is not None and rhs_desc is not None :
466478 # Both integers: compare in the promoted type, which also picks the
467479 # signed or unsigned predicate (u64 > s64 is an unsigned compare in C).
0 commit comments