Skip to content

Commit 899d8ba

Browse files
dkanalievKernel Patches Daemon
authored andcommitted
bpf: Introduce tnum_scast as a tnum native sign extension helper
This patch introduces a new helper function - tnum_scast(), which sign-extends a tnum from a smaller integer size to the full 64-bit bpf register range. This is achieved by utilizing the native sign-extension behavior of signed 64-bit integers. By casting the value and mask to s64, shifting left to align the target sign bit with the 64-bit MSB, and then performing an arithmetic right shift, the sign bit is automatically propagated to the upper bits. For the mask, this works because if the sign bit is unknown (1), the arithmetic shift propagates 1s (making upper bits unknonw). If known (0), it propagates 0s (making upper bits known). a) When the sign bit is known: Assume a tnum with value = 0xFF, mask = 0x00, size = 1, which corresponds to an 8-bit subregister of value 0xFF (-1 in 8 bits). s = 64 - 8 = 56 value = ((s64)0xFF << 56) >> 56; // 0xFF...FF (-1) mask = ((s64)0x00 << 56) >> 56; // 0x00...00 Because the sign bit is known to be 1, we sign-extend with 1s. The resulting tnum is (0xFFFFFFFFFFFFFFFF, 0x0000000000000000). b) When the sign bit is unknown: Assume a tnum with value = 0x7F, mask = 0x80, size = 1. s = 56 value = ((s64)0x7F << 56) >> 56; // 0x00...7F mask = ((s64)0x80 << 56) >> 56; // 0xFF...80 The lower 8 bits can be 0x7F or 0xFF. The mask sign bit was 1 (unknown), so the arithmetic shift propagated 1s, making all higher 56 bits unknown. In 64-bit form, this tnum correctly represents the range from 0x000000000000007F (+127) to 0xFFFFFFFFFFFFFFFF (-1). Signed-off-by: Dimitar Kanaliev <[email protected]>
1 parent ae9b520 commit 899d8ba

File tree

2 files changed

+16
-0
lines changed

2 files changed

+16
-0
lines changed

include/linux/tnum.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ struct tnum tnum_union(struct tnum t1, struct tnum t2);
6363
/* Return @a with all but the lowest @size bytes cleared */
6464
struct tnum tnum_cast(struct tnum a, u8 size);
6565

66+
/* Return @a sign-extended from @size bytes */
67+
struct tnum tnum_scast(struct tnum a, u8 size);
68+
6669
/* Returns true if @a is a known constant */
6770
static inline bool tnum_is_const(struct tnum a)
6871
{

kernel/bpf/tnum.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,19 @@ struct tnum tnum_cast(struct tnum a, u8 size)
199199
return a;
200200
}
201201

202+
struct tnum tnum_scast(struct tnum a, u8 size)
203+
{
204+
u8 s = 64 - size * 8;
205+
u64 value, mask;
206+
207+
if (size >= 8)
208+
return a;
209+
210+
value = ((s64)a.value << s) >> s;
211+
mask = ((s64)a.mask << s) >> s;
212+
return TNUM(value, mask);
213+
}
214+
202215
bool tnum_is_aligned(struct tnum a, u64 size)
203216
{
204217
if (!size)

0 commit comments

Comments
 (0)