Skip to content

Commit ff0a3a7

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: conntrack: dccp: copy entire header to stack buffer, not just basic one
Eric Dumazet says: nf_conntrack_dccp_packet() has an unique: dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh); And nothing more is 'pulled' from the packet, depending on the content. dh->dccph_doff, and/or dh->dccph_x ...) So dccp_ack_seq() is happily reading stuff past the _dh buffer. BUG: KASAN: stack-out-of-bounds in nf_conntrack_dccp_packet+0x1134/0x11c0 Read of size 4 at addr ffff000128f66e0c by task syz-executor.2/29371 [..] Fix this by increasing the stack buffer to also include room for the extra sequence numbers and all the known dccp packet type headers, then pull again after the initial validation of the basic header. While at it, mark packets invalid that lack 48bit sequence bit but where RFC says the type MUST use them. Compile tested only. v2: first skb_header_pointer() now needs to adjust the size to only pull the generic header. (Eric) Heads-up: I intend to remove dccp conntrack support later this year. Fixes: 2bc7804 ("[NETFILTER]: nf_conntrack: add DCCP protocol support") Reported-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Florian Westphal <fw@strlen.de> Reviewed-by: Eric Dumazet <edumazet@google.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent 6f67fbf commit ff0a3a7

1 file changed

Lines changed: 49 additions & 3 deletions

File tree

net/netfilter/nf_conntrack_proto_dccp.c

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -432,9 +432,19 @@ static bool dccp_error(const struct dccp_hdr *dh,
432432
struct sk_buff *skb, unsigned int dataoff,
433433
const struct nf_hook_state *state)
434434
{
435+
static const unsigned long require_seq48 = 1 << DCCP_PKT_REQUEST |
436+
1 << DCCP_PKT_RESPONSE |
437+
1 << DCCP_PKT_CLOSEREQ |
438+
1 << DCCP_PKT_CLOSE |
439+
1 << DCCP_PKT_RESET |
440+
1 << DCCP_PKT_SYNC |
441+
1 << DCCP_PKT_SYNCACK;
435442
unsigned int dccp_len = skb->len - dataoff;
436443
unsigned int cscov;
437444
const char *msg;
445+
u8 type;
446+
447+
BUILD_BUG_ON(DCCP_PKT_INVALID >= BITS_PER_LONG);
438448

439449
if (dh->dccph_doff * 4 < sizeof(struct dccp_hdr) ||
440450
dh->dccph_doff * 4 > dccp_len) {
@@ -459,34 +469,70 @@ static bool dccp_error(const struct dccp_hdr *dh,
459469
goto out_invalid;
460470
}
461471

462-
if (dh->dccph_type >= DCCP_PKT_INVALID) {
472+
type = dh->dccph_type;
473+
if (type >= DCCP_PKT_INVALID) {
463474
msg = "nf_ct_dccp: reserved packet type ";
464475
goto out_invalid;
465476
}
477+
478+
if (test_bit(type, &require_seq48) && !dh->dccph_x) {
479+
msg = "nf_ct_dccp: type lacks 48bit sequence numbers";
480+
goto out_invalid;
481+
}
482+
466483
return false;
467484
out_invalid:
468485
nf_l4proto_log_invalid(skb, state, IPPROTO_DCCP, "%s", msg);
469486
return true;
470487
}
471488

489+
struct nf_conntrack_dccp_buf {
490+
struct dccp_hdr dh; /* generic header part */
491+
struct dccp_hdr_ext ext; /* optional depending dh->dccph_x */
492+
union { /* depends on header type */
493+
struct dccp_hdr_ack_bits ack;
494+
struct dccp_hdr_request req;
495+
struct dccp_hdr_response response;
496+
struct dccp_hdr_reset rst;
497+
} u;
498+
};
499+
500+
static struct dccp_hdr *
501+
dccp_header_pointer(const struct sk_buff *skb, int offset, const struct dccp_hdr *dh,
502+
struct nf_conntrack_dccp_buf *buf)
503+
{
504+
unsigned int hdrlen = __dccp_hdr_len(dh);
505+
506+
if (hdrlen > sizeof(*buf))
507+
return NULL;
508+
509+
return skb_header_pointer(skb, offset, hdrlen, buf);
510+
}
511+
472512
int nf_conntrack_dccp_packet(struct nf_conn *ct, struct sk_buff *skb,
473513
unsigned int dataoff,
474514
enum ip_conntrack_info ctinfo,
475515
const struct nf_hook_state *state)
476516
{
477517
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
478-
struct dccp_hdr _dh, *dh;
518+
struct nf_conntrack_dccp_buf _dh;
479519
u_int8_t type, old_state, new_state;
480520
enum ct_dccp_roles role;
481521
unsigned int *timeouts;
522+
struct dccp_hdr *dh;
482523

483-
dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh);
524+
dh = skb_header_pointer(skb, dataoff, sizeof(*dh), &_dh.dh);
484525
if (!dh)
485526
return NF_DROP;
486527

487528
if (dccp_error(dh, skb, dataoff, state))
488529
return -NF_ACCEPT;
489530

531+
/* pull again, including possible 48 bit sequences and subtype header */
532+
dh = dccp_header_pointer(skb, dataoff, dh, &_dh);
533+
if (!dh)
534+
return NF_DROP;
535+
490536
type = dh->dccph_type;
491537
if (!nf_ct_is_confirmed(ct) && !dccp_new(ct, skb, dh, state))
492538
return -NF_ACCEPT;

0 commit comments

Comments
 (0)