Skip to content

Commit 2c68bc7

Browse files
committed
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
Pull networking fixes from David Miller: 1) Netlink socket dumping had several missing verifications and checks. In particular, address comparisons in the request byte code interpreter could access past the end of the address in the inet_request_sock. Also, address family and address prefix lengths were not validated properly at all. This means arbitrary applications can read past the end of certain kernel data structures. Fixes from Neal Cardwell. 2) ip_check_defrag() operates in contexts where we're in the process of, or about to, input the packet into the real protocols (specifically macvlan and AF_PACKET snooping). Unfortunately, it does a pskb_may_pull() which can modify the backing packet data which is not legal if the SKB is shared. It very much can be shared in this context. Deal with the possibility that the SKB is segmented by using skb_copy_bits(). Fix from Johannes Berg based upon a report by Eric Leblond. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net: ipv4: ip_check_defrag must not modify skb before unsharing inet_diag: validate port comparison byte code to prevent unsafe reads inet_diag: avoid unsafe and nonsensical prefix matches in inet_diag_bc_run() inet_diag: validate byte code to prevent oops in inet_diag_bc_run() inet_diag: fix oops for IPv4 AF_INET6 TCP SYN-RECV state
2 parents caf4919 + 1bf3751 commit 2c68bc7

File tree

2 files changed

+131
-42
lines changed

2 files changed

+131
-42
lines changed

net/ipv4/inet_diag.c

Lines changed: 122 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ struct inet_diag_entry {
4444
u16 dport;
4545
u16 family;
4646
u16 userlocks;
47+
#if IS_ENABLED(CONFIG_IPV6)
48+
struct in6_addr saddr_storage; /* for IPv4-mapped-IPv6 addresses */
49+
struct in6_addr daddr_storage; /* for IPv4-mapped-IPv6 addresses */
50+
#endif
4751
};
4852

4953
static DEFINE_MUTEX(inet_diag_table_mutex);
@@ -428,25 +432,31 @@ static int inet_diag_bc_run(const struct nlattr *_bc,
428432
break;
429433
}
430434

431-
if (cond->prefix_len == 0)
432-
break;
433-
434435
if (op->code == INET_DIAG_BC_S_COND)
435436
addr = entry->saddr;
436437
else
437438
addr = entry->daddr;
438439

440+
if (cond->family != AF_UNSPEC &&
441+
cond->family != entry->family) {
442+
if (entry->family == AF_INET6 &&
443+
cond->family == AF_INET) {
444+
if (addr[0] == 0 && addr[1] == 0 &&
445+
addr[2] == htonl(0xffff) &&
446+
bitstring_match(addr + 3,
447+
cond->addr,
448+
cond->prefix_len))
449+
break;
450+
}
451+
yes = 0;
452+
break;
453+
}
454+
455+
if (cond->prefix_len == 0)
456+
break;
439457
if (bitstring_match(addr, cond->addr,
440458
cond->prefix_len))
441459
break;
442-
if (entry->family == AF_INET6 &&
443-
cond->family == AF_INET) {
444-
if (addr[0] == 0 && addr[1] == 0 &&
445-
addr[2] == htonl(0xffff) &&
446-
bitstring_match(addr + 3, cond->addr,
447-
cond->prefix_len))
448-
break;
449-
}
450460
yes = 0;
451461
break;
452462
}
@@ -509,36 +519,95 @@ static int valid_cc(const void *bc, int len, int cc)
509519
return 0;
510520
}
511521

522+
/* Validate an inet_diag_hostcond. */
523+
static bool valid_hostcond(const struct inet_diag_bc_op *op, int len,
524+
int *min_len)
525+
{
526+
int addr_len;
527+
struct inet_diag_hostcond *cond;
528+
529+
/* Check hostcond space. */
530+
*min_len += sizeof(struct inet_diag_hostcond);
531+
if (len < *min_len)
532+
return false;
533+
cond = (struct inet_diag_hostcond *)(op + 1);
534+
535+
/* Check address family and address length. */
536+
switch (cond->family) {
537+
case AF_UNSPEC:
538+
addr_len = 0;
539+
break;
540+
case AF_INET:
541+
addr_len = sizeof(struct in_addr);
542+
break;
543+
case AF_INET6:
544+
addr_len = sizeof(struct in6_addr);
545+
break;
546+
default:
547+
return false;
548+
}
549+
*min_len += addr_len;
550+
if (len < *min_len)
551+
return false;
552+
553+
/* Check prefix length (in bits) vs address length (in bytes). */
554+
if (cond->prefix_len > 8 * addr_len)
555+
return false;
556+
557+
return true;
558+
}
559+
560+
/* Validate a port comparison operator. */
561+
static inline bool valid_port_comparison(const struct inet_diag_bc_op *op,
562+
int len, int *min_len)
563+
{
564+
/* Port comparisons put the port in a follow-on inet_diag_bc_op. */
565+
*min_len += sizeof(struct inet_diag_bc_op);
566+
if (len < *min_len)
567+
return false;
568+
return true;
569+
}
570+
512571
static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
513572
{
514573
const void *bc = bytecode;
515574
int len = bytecode_len;
516575

517576
while (len > 0) {
518577
const struct inet_diag_bc_op *op = bc;
578+
int min_len = sizeof(struct inet_diag_bc_op);
519579

520580
//printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len);
521581
switch (op->code) {
522-
case INET_DIAG_BC_AUTO:
523582
case INET_DIAG_BC_S_COND:
524583
case INET_DIAG_BC_D_COND:
584+
if (!valid_hostcond(bc, len, &min_len))
585+
return -EINVAL;
586+
break;
525587
case INET_DIAG_BC_S_GE:
526588
case INET_DIAG_BC_S_LE:
527589
case INET_DIAG_BC_D_GE:
528590
case INET_DIAG_BC_D_LE:
529-
case INET_DIAG_BC_JMP:
530-
if (op->no < 4 || op->no > len + 4 || op->no & 3)
531-
return -EINVAL;
532-
if (op->no < len &&
533-
!valid_cc(bytecode, bytecode_len, len - op->no))
591+
if (!valid_port_comparison(bc, len, &min_len))
534592
return -EINVAL;
535593
break;
594+
case INET_DIAG_BC_AUTO:
595+
case INET_DIAG_BC_JMP:
536596
case INET_DIAG_BC_NOP:
537597
break;
538598
default:
539599
return -EINVAL;
540600
}
541-
if (op->yes < 4 || op->yes > len + 4 || op->yes & 3)
601+
602+
if (op->code != INET_DIAG_BC_NOP) {
603+
if (op->no < min_len || op->no > len + 4 || op->no & 3)
604+
return -EINVAL;
605+
if (op->no < len &&
606+
!valid_cc(bytecode, bytecode_len, len - op->no))
607+
return -EINVAL;
608+
}
609+
610+
if (op->yes < min_len || op->yes > len + 4 || op->yes & 3)
542611
return -EINVAL;
543612
bc += op->yes;
544613
len -= op->yes;
@@ -596,6 +665,36 @@ static int inet_twsk_diag_dump(struct inet_timewait_sock *tw,
596665
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
597666
}
598667

668+
/* Get the IPv4, IPv6, or IPv4-mapped-IPv6 local and remote addresses
669+
* from a request_sock. For IPv4-mapped-IPv6 we must map IPv4 to IPv6.
670+
*/
671+
static inline void inet_diag_req_addrs(const struct sock *sk,
672+
const struct request_sock *req,
673+
struct inet_diag_entry *entry)
674+
{
675+
struct inet_request_sock *ireq = inet_rsk(req);
676+
677+
#if IS_ENABLED(CONFIG_IPV6)
678+
if (sk->sk_family == AF_INET6) {
679+
if (req->rsk_ops->family == AF_INET6) {
680+
entry->saddr = inet6_rsk(req)->loc_addr.s6_addr32;
681+
entry->daddr = inet6_rsk(req)->rmt_addr.s6_addr32;
682+
} else if (req->rsk_ops->family == AF_INET) {
683+
ipv6_addr_set_v4mapped(ireq->loc_addr,
684+
&entry->saddr_storage);
685+
ipv6_addr_set_v4mapped(ireq->rmt_addr,
686+
&entry->daddr_storage);
687+
entry->saddr = entry->saddr_storage.s6_addr32;
688+
entry->daddr = entry->daddr_storage.s6_addr32;
689+
}
690+
} else
691+
#endif
692+
{
693+
entry->saddr = &ireq->loc_addr;
694+
entry->daddr = &ireq->rmt_addr;
695+
}
696+
}
697+
599698
static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
600699
struct request_sock *req,
601700
struct user_namespace *user_ns,
@@ -637,8 +736,10 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
637736
r->idiag_inode = 0;
638737
#if IS_ENABLED(CONFIG_IPV6)
639738
if (r->idiag_family == AF_INET6) {
640-
*(struct in6_addr *)r->id.idiag_src = inet6_rsk(req)->loc_addr;
641-
*(struct in6_addr *)r->id.idiag_dst = inet6_rsk(req)->rmt_addr;
739+
struct inet_diag_entry entry;
740+
inet_diag_req_addrs(sk, req, &entry);
741+
memcpy(r->id.idiag_src, entry.saddr, sizeof(struct in6_addr));
742+
memcpy(r->id.idiag_dst, entry.daddr, sizeof(struct in6_addr));
642743
}
643744
#endif
644745

@@ -691,18 +792,7 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
691792
continue;
692793

693794
if (bc) {
694-
entry.saddr =
695-
#if IS_ENABLED(CONFIG_IPV6)
696-
(entry.family == AF_INET6) ?
697-
inet6_rsk(req)->loc_addr.s6_addr32 :
698-
#endif
699-
&ireq->loc_addr;
700-
entry.daddr =
701-
#if IS_ENABLED(CONFIG_IPV6)
702-
(entry.family == AF_INET6) ?
703-
inet6_rsk(req)->rmt_addr.s6_addr32 :
704-
#endif
705-
&ireq->rmt_addr;
795+
inet_diag_req_addrs(sk, req, &entry);
706796
entry.dport = ntohs(ireq->rmt_port);
707797

708798
if (!inet_diag_bc_run(bc, &entry))

net/ipv4/ip_fragment.c

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -707,28 +707,27 @@ EXPORT_SYMBOL(ip_defrag);
707707

708708
struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user)
709709
{
710-
const struct iphdr *iph;
710+
struct iphdr iph;
711711
u32 len;
712712

713713
if (skb->protocol != htons(ETH_P_IP))
714714
return skb;
715715

716-
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
716+
if (!skb_copy_bits(skb, 0, &iph, sizeof(iph)))
717717
return skb;
718718

719-
iph = ip_hdr(skb);
720-
if (iph->ihl < 5 || iph->version != 4)
719+
if (iph.ihl < 5 || iph.version != 4)
721720
return skb;
722-
if (!pskb_may_pull(skb, iph->ihl*4))
723-
return skb;
724-
iph = ip_hdr(skb);
725-
len = ntohs(iph->tot_len);
726-
if (skb->len < len || len < (iph->ihl * 4))
721+
722+
len = ntohs(iph.tot_len);
723+
if (skb->len < len || len < (iph.ihl * 4))
727724
return skb;
728725

729-
if (ip_is_fragment(ip_hdr(skb))) {
726+
if (ip_is_fragment(&iph)) {
730727
skb = skb_share_check(skb, GFP_ATOMIC);
731728
if (skb) {
729+
if (!pskb_may_pull(skb, iph.ihl*4))
730+
return skb;
732731
if (pskb_trim_rcsum(skb, len))
733732
return skb;
734733
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));

0 commit comments

Comments
 (0)