selftests/bpf: Fix csum_partial() dropping trailing byte on odd length

csum_partial() computes num_u16 = len >> 1 and only sums that many
16-bit words, so the last byte of an odd-length buffer never gets
added to the checksum. RFC 1071 says it should be padded with a zero
byte and summed as one more word, not dropped.

This backs build_ip_csum(), build_udp_v4_csum() and
build_udp_v6_csum(), used by flow_dissector_classification.c and
xdp_metadata.c to hand-build packets. No current caller builds an
odd-length payload, so nothing fails today, but a future one would
get a silently wrong checksum.

Also bump flow_dissector_classification's TEST_PACKET_LEN from 100 to
99 so this actually gets exercised instead of staying latent.

f4504af685 wrote the len >> 1 division, but it only ever passed
sizeof(iphdr), always even, so it couldn't hit the bug. Tagging
bcc00987bc instead, since it added the first caller,
build_udp_v4_csum()/build_udp_v6_csum(), that can pass an odd length.

Verified with test_progs under vmtest.sh: without the fix, an odd
TEST_PACKET_LEN makes the kernel drop the packet over a bad checksum
and flow_dissector_classification fails; with the fix, both
flow_dissector_classification and xdp_metadata pass.

Signed-off-by: Madhav Khosla <madhav.khoslaa@gmail.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20260920085549.867099-1-madhav.khoslaa@gmail.com
This commit is contained in:
Madhav Khosla 2026-09-20 14:25:49 +05:30 committed by Alexei Starovoitov
parent 94809ecf9d
commit ddaa827dce
No known key found for this signature in database
2 changed files with 14 additions and 3 deletions

View File

@ -129,12 +129,23 @@ static __u16 csum_fold(__u32 csum)
static __wsum csum_partial(const void *buf, int len, __wsum sum)
{
__u16 *p = (__u16 *)buf;
const __u8 *p = buf;
int num_u16 = len >> 1;
int i;
for (i = 0; i < num_u16; i++)
sum += p[i];
sum += ((const __u16 *)p)[i];
/*
* RFC 1071: an odd-length buffer's trailing byte is paired with
* a zero pad byte to form the final 16-bit word.
*/
if (len & 1) {
__u16 tail = 0;
__builtin_memcpy(&tail, p + len - 1, 1);
sum += tail;
}
return sum;
}

View File

@ -27,7 +27,7 @@
#define TEST_NAME_MAX_LEN (32 + SUBTEST_NAME_MAX_LEN)
#define MAX_SOURCE_PORTS 3
#define TEST_PACKETS_COUNT 10
#define TEST_PACKET_LEN 100
#define TEST_PACKET_LEN 99
#define TEST_PACKET_PATTERN 'a'
#define TEST_IPV4 "192.168.0.1/32"
#define TEST_IPV6 "100::a/128"