Add a DM test that feeds __net_defragment() a single crafted fragment whose trailing hole descriptor lands just past pkt_buff. Without the preceding fix the 8-byte hole write goes out of bounds; with it the fragment is dropped and no datagram is delivered. Signed-off-by: Shahriyar Jalayeri <shahriyar@byteray.co.uk> Acked-by: Jerome Forissier <jerome.forissier@arm.com>
119 lines
3.7 KiB
C
119 lines
3.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Regression test for IP fragment reassembly.
|
|
*
|
|
* The test drives the real RX path via net_process_received_packet(). Final IP
|
|
* fragment (MF=0) is duplicated, crafted payload triggers redelivery of the datagram,
|
|
* which fails the test for the unfixed code.
|
|
*/
|
|
|
|
#include <net.h>
|
|
#include <string.h>
|
|
#include <test/ut.h>
|
|
#include <dm/test.h>
|
|
|
|
#define FRAG_LEN (8)
|
|
#define PAYLOAD_OFFSET (ETHER_HDR_SIZE + IP_HDR_SIZE)
|
|
#define FRAME_LEN (PAYLOAD_OFFSET + FRAG_LEN)
|
|
|
|
static int udp_rx_count;
|
|
|
|
static void defrag_udp_handler(uchar *pkt, unsigned int dport,
|
|
struct in_addr sip, unsigned int sport,
|
|
unsigned int len)
|
|
{
|
|
udp_rx_count++;
|
|
}
|
|
|
|
static int build_frag(uchar *buf, u16 off_flags, const u16 *payload)
|
|
{
|
|
struct ethernet_hdr *et = (struct ethernet_hdr *)buf;
|
|
struct ip_udp_hdr *ip = (struct ip_udp_hdr *)(buf + ETHER_HDR_SIZE);
|
|
|
|
memset(buf, 0, FRAME_LEN);
|
|
et->et_protlen = htons(PROT_IP);
|
|
|
|
ip->ip_hl_v = 0x45;
|
|
ip->ip_len = htons(IP_HDR_SIZE + FRAG_LEN);
|
|
ip->ip_id = htons(0x4321);
|
|
ip->ip_off = htons(off_flags);
|
|
ip->ip_ttl = 64;
|
|
ip->ip_p = IPPROTO_UDP;
|
|
/* Broadcast destination is accepted regardless of net_ip. */
|
|
ip->ip_dst.s_addr = 0xffffffff;
|
|
ip->ip_sum = compute_ip_checksum(ip, IP_HDR_SIZE);
|
|
|
|
memcpy(buf + PAYLOAD_OFFSET, payload, FRAG_LEN);
|
|
|
|
return FRAME_LEN;
|
|
}
|
|
|
|
static int dm_test_net_ip_defrag_dup_last(struct unit_test_state *uts)
|
|
{
|
|
rxhand_f *saved_handler = net_get_udp_handler();
|
|
uchar frame[FRAME_LEN];
|
|
/* UDP header, carried by first fragment. */
|
|
u16 udp_hdr[4] = { htons(5000), htons(5001),
|
|
htons(UDP_HDR_SIZE + FRAG_LEN), 0 };
|
|
/*
|
|
* Second fragment's payload doubles as a fake hole
|
|
* {last_byte >= FRAG_LEN, next_hole = 0, prev_hole = 0}, so that the
|
|
* buggy code re-reading it on a duplicate re-delivers the datagram.
|
|
*/
|
|
u16 frag_b[4] = { 2 * FRAG_LEN, 0, 0, 0 };
|
|
|
|
udp_rx_count = 0;
|
|
net_set_udp_handler(defrag_udp_handler);
|
|
|
|
/* UDP header, offset 0, MF=1; then data, offset 1, MF=0 */
|
|
net_process_received_packet(frame, build_frag(frame, IP_FLAGS_MFRAG, udp_hdr));
|
|
net_process_received_packet(frame, build_frag(frame, 1, frag_b));
|
|
ut_asserteq(1, udp_rx_count);
|
|
|
|
/* Duplicate the final fragment: UDP datagram must not be delivered again. */
|
|
net_process_received_packet(frame, build_frag(frame, 1, frag_b));
|
|
ut_asserteq(1, udp_rx_count);
|
|
|
|
net_set_udp_handler(saved_handler);
|
|
|
|
return 0;
|
|
}
|
|
|
|
DM_TEST(dm_test_net_ip_defrag_dup_last, 0);
|
|
|
|
/*
|
|
* A fragment placed at the very top of the reassembly buffer takes the
|
|
* split-hole branch, which writes an 8-byte "struct hole" at
|
|
* pkt_buff + IP_HDR_SIZE + (offset8 + len / 8) * 8. With start + len equal to
|
|
* IP_MAXUDP that write reaches the end of pkt_buff and spills past it. pkt_buff
|
|
* is a static array, so this is flagged under AddressSanitizer; the fix rejects
|
|
* such a fragment instead. The datagram is incomplete, so nothing is delivered
|
|
* either way.
|
|
*/
|
|
static int dm_test_net_ip_defrag_oob(struct unit_test_state *uts)
|
|
{
|
|
rxhand_f *saved_handler = net_get_udp_handler();
|
|
uchar frame[FRAME_LEN];
|
|
struct ip_udp_hdr *ip = (struct ip_udp_hdr *)(frame + ETHER_HDR_SIZE);
|
|
u16 payload[4] = { 0, 0, 0, 0 };
|
|
/* Offset (8-byte units) so that start + FRAG_LEN == IP_MAXUDP. */
|
|
u16 off8 = (CONFIG_NET_MAXDEFRAG - IP_HDR_SIZE - FRAG_LEN) / 8;
|
|
|
|
udp_rx_count = 0;
|
|
net_set_udp_handler(defrag_udp_handler);
|
|
|
|
build_frag(frame, IP_FLAGS_MFRAG | off8, payload);
|
|
/* A distinct id forces a fresh reassembly independent of earlier tests. */
|
|
ip->ip_id = htons(0x7abc);
|
|
ip->ip_sum = 0;
|
|
ip->ip_sum = compute_ip_checksum(ip, IP_HDR_SIZE);
|
|
net_process_received_packet(frame, FRAME_LEN);
|
|
|
|
ut_asserteq(0, udp_rx_count);
|
|
|
|
net_set_udp_handler(saved_handler);
|
|
|
|
return 0;
|
|
}
|
|
DM_TEST(dm_test_net_ip_defrag_oob, 0);
|