Skip to content

Commit e133938

Browse files
Hou TaoAlexei Starovoitov
authored andcommitted
bpf: Use __u64 to save the bits in bits iterator
On 32-bit hosts (e.g., arm32), when a bpf program passes a u64 to bpf_iter_bits_new(), bpf_iter_bits_new() will use bits_copy to store the content of the u64. However, bits_copy is only 4 bytes, leading to stack corruption. The straightforward solution would be to replace u64 with unsigned long in bpf_iter_bits_new(). However, this introduces confusion and problems for 32-bit hosts because the size of ulong in bpf program is 8 bytes, but it is treated as 4-bytes after passed to bpf_iter_bits_new(). Fix it by changing the type of both bits and bit_count from unsigned long to u64. However, the change is not enough. The main reason is that bpf_iter_bits_next() uses find_next_bit() to find the next bit and the pointer passed to find_next_bit() is an unsigned long pointer instead of a u64 pointer. For 32-bit little-endian host, it is fine but it is not the case for 32-bit big-endian host. Because under 32-bit big-endian host, the first iterated unsigned long will be the bits 32-63 of the u64 instead of the expected bits 0-31. Therefore, in addition to changing the type, swap the two unsigned longs within the u64 for 32-bit big-endian host. Signed-off-by: Hou Tao <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 393397f commit e133938

File tree

1 file changed

+30
-3
lines changed

1 file changed

+30
-3
lines changed

kernel/bpf/helpers.c

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2855,13 +2855,36 @@ struct bpf_iter_bits {
28552855

28562856
struct bpf_iter_bits_kern {
28572857
union {
2858-
unsigned long *bits;
2859-
unsigned long bits_copy;
2858+
__u64 *bits;
2859+
__u64 bits_copy;
28602860
};
28612861
int nr_bits;
28622862
int bit;
28632863
} __aligned(8);
28642864

2865+
/* On 64-bit hosts, unsigned long and u64 have the same size, so passing
2866+
* a u64 pointer and an unsigned long pointer to find_next_bit() will
2867+
* return the same result, as both point to the same 8-byte area.
2868+
*
2869+
* For 32-bit little-endian hosts, using a u64 pointer or unsigned long
2870+
* pointer also makes no difference. This is because the first iterated
2871+
* unsigned long is composed of bits 0-31 of the u64 and the second unsigned
2872+
* long is composed of bits 32-63 of the u64.
2873+
*
2874+
* However, for 32-bit big-endian hosts, this is not the case. The first
2875+
* iterated unsigned long will be bits 32-63 of the u64, so swap these two
2876+
* ulong values within the u64.
2877+
*/
2878+
static void swap_ulong_in_u64(u64 *bits, unsigned int nr)
2879+
{
2880+
#if (BITS_PER_LONG == 32) && defined(__BIG_ENDIAN)
2881+
unsigned int i;
2882+
2883+
for (i = 0; i < nr; i++)
2884+
bits[i] = (bits[i] >> 32) | ((u64)(u32)bits[i] << 32);
2885+
#endif
2886+
}
2887+
28652888
/**
28662889
* bpf_iter_bits_new() - Initialize a new bits iterator for a given memory area
28672890
* @it: The new bpf_iter_bits to be created
@@ -2904,6 +2927,8 @@ bpf_iter_bits_new(struct bpf_iter_bits *it, const u64 *unsafe_ptr__ign, u32 nr_w
29042927
if (err)
29052928
return -EFAULT;
29062929

2930+
swap_ulong_in_u64(&kit->bits_copy, nr_words);
2931+
29072932
kit->nr_bits = nr_bits;
29082933
return 0;
29092934
}
@@ -2922,6 +2947,8 @@ bpf_iter_bits_new(struct bpf_iter_bits *it, const u64 *unsafe_ptr__ign, u32 nr_w
29222947
return err;
29232948
}
29242949

2950+
swap_ulong_in_u64(kit->bits, nr_words);
2951+
29252952
kit->nr_bits = nr_bits;
29262953
return 0;
29272954
}
@@ -2939,7 +2966,7 @@ __bpf_kfunc int *bpf_iter_bits_next(struct bpf_iter_bits *it)
29392966
{
29402967
struct bpf_iter_bits_kern *kit = (void *)it;
29412968
int bit = kit->bit, nr_bits = kit->nr_bits;
2942-
const unsigned long *bits;
2969+
const void *bits;
29432970

29442971
if (!nr_bits || bit >= nr_bits)
29452972
return NULL;

0 commit comments

Comments
 (0)