Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+132 -43
+122 -32
net/ipv4/inet_diag.c
··· 44 44 u16 dport; 45 45 u16 family; 46 46 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 47 51 }; 48 52 49 53 static DEFINE_MUTEX(inet_diag_table_mutex); ··· 432 428 break; 433 429 } 434 430 435 - if (cond->prefix_len == 0) 436 - break; 437 - 438 431 if (op->code == INET_DIAG_BC_S_COND) 439 432 addr = entry->saddr; 440 433 else 441 434 addr = entry->daddr; 442 435 436 + if (cond->family != AF_UNSPEC && 437 + cond->family != entry->family) { 438 + if (entry->family == AF_INET6 && 439 + cond->family == AF_INET) { 440 + if (addr[0] == 0 && addr[1] == 0 && 441 + addr[2] == htonl(0xffff) && 442 + bitstring_match(addr + 3, 443 + cond->addr, 444 + cond->prefix_len)) 445 + break; 446 + } 447 + yes = 0; 448 + break; 449 + } 450 + 451 + if (cond->prefix_len == 0) 452 + break; 443 453 if (bitstring_match(addr, cond->addr, 444 454 cond->prefix_len)) 445 455 break; 446 - if (entry->family == AF_INET6 && 447 - cond->family == AF_INET) { 448 - if (addr[0] == 0 && addr[1] == 0 && 449 - addr[2] == htonl(0xffff) && 450 - bitstring_match(addr + 3, cond->addr, 451 - cond->prefix_len)) 452 - break; 453 - } 454 456 yes = 0; 455 457 break; 456 458 } ··· 519 509 return 0; 520 510 } 521 511 512 + /* Validate an inet_diag_hostcond. */ 513 + static bool valid_hostcond(const struct inet_diag_bc_op *op, int len, 514 + int *min_len) 515 + { 516 + int addr_len; 517 + struct inet_diag_hostcond *cond; 518 + 519 + /* Check hostcond space. */ 520 + *min_len += sizeof(struct inet_diag_hostcond); 521 + if (len < *min_len) 522 + return false; 523 + cond = (struct inet_diag_hostcond *)(op + 1); 524 + 525 + /* Check address family and address length. */ 526 + switch (cond->family) { 527 + case AF_UNSPEC: 528 + addr_len = 0; 529 + break; 530 + case AF_INET: 531 + addr_len = sizeof(struct in_addr); 532 + break; 533 + case AF_INET6: 534 + addr_len = sizeof(struct in6_addr); 535 + break; 536 + default: 537 + return false; 538 + } 539 + *min_len += addr_len; 540 + if (len < *min_len) 541 + return false; 542 + 543 + /* Check prefix length (in bits) vs address length (in bytes). */ 544 + if (cond->prefix_len > 8 * addr_len) 545 + return false; 546 + 547 + return true; 548 + } 549 + 550 + /* Validate a port comparison operator. */ 551 + static inline bool valid_port_comparison(const struct inet_diag_bc_op *op, 552 + int len, int *min_len) 553 + { 554 + /* Port comparisons put the port in a follow-on inet_diag_bc_op. */ 555 + *min_len += sizeof(struct inet_diag_bc_op); 556 + if (len < *min_len) 557 + return false; 558 + return true; 559 + } 560 + 522 561 static int inet_diag_bc_audit(const void *bytecode, int bytecode_len) 523 562 { 524 563 const void *bc = bytecode; ··· 575 516 576 517 while (len > 0) { 577 518 const struct inet_diag_bc_op *op = bc; 519 + int min_len = sizeof(struct inet_diag_bc_op); 578 520 579 521 //printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len); 580 522 switch (op->code) { 581 - case INET_DIAG_BC_AUTO: 582 523 case INET_DIAG_BC_S_COND: 583 524 case INET_DIAG_BC_D_COND: 525 + if (!valid_hostcond(bc, len, &min_len)) 526 + return -EINVAL; 527 + break; 584 528 case INET_DIAG_BC_S_GE: 585 529 case INET_DIAG_BC_S_LE: 586 530 case INET_DIAG_BC_D_GE: 587 531 case INET_DIAG_BC_D_LE: 588 - case INET_DIAG_BC_JMP: 589 - if (op->no < 4 || op->no > len + 4 || op->no & 3) 590 - return -EINVAL; 591 - if (op->no < len && 592 - !valid_cc(bytecode, bytecode_len, len - op->no)) 532 + if (!valid_port_comparison(bc, len, &min_len)) 593 533 return -EINVAL; 594 534 break; 535 + case INET_DIAG_BC_AUTO: 536 + case INET_DIAG_BC_JMP: 595 537 case INET_DIAG_BC_NOP: 596 538 break; 597 539 default: 598 540 return -EINVAL; 599 541 } 600 - if (op->yes < 4 || op->yes > len + 4 || op->yes & 3) 542 + 543 + if (op->code != INET_DIAG_BC_NOP) { 544 + if (op->no < min_len || op->no > len + 4 || op->no & 3) 545 + return -EINVAL; 546 + if (op->no < len && 547 + !valid_cc(bytecode, bytecode_len, len - op->no)) 548 + return -EINVAL; 549 + } 550 + 551 + if (op->yes < min_len || op->yes > len + 4 || op->yes & 3) 601 552 return -EINVAL; 602 553 bc += op->yes; 603 554 len -= op->yes; ··· 665 596 cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); 666 597 } 667 598 599 + /* Get the IPv4, IPv6, or IPv4-mapped-IPv6 local and remote addresses 600 + * from a request_sock. For IPv4-mapped-IPv6 we must map IPv4 to IPv6. 601 + */ 602 + static inline void inet_diag_req_addrs(const struct sock *sk, 603 + const struct request_sock *req, 604 + struct inet_diag_entry *entry) 605 + { 606 + struct inet_request_sock *ireq = inet_rsk(req); 607 + 608 + #if IS_ENABLED(CONFIG_IPV6) 609 + if (sk->sk_family == AF_INET6) { 610 + if (req->rsk_ops->family == AF_INET6) { 611 + entry->saddr = inet6_rsk(req)->loc_addr.s6_addr32; 612 + entry->daddr = inet6_rsk(req)->rmt_addr.s6_addr32; 613 + } else if (req->rsk_ops->family == AF_INET) { 614 + ipv6_addr_set_v4mapped(ireq->loc_addr, 615 + &entry->saddr_storage); 616 + ipv6_addr_set_v4mapped(ireq->rmt_addr, 617 + &entry->daddr_storage); 618 + entry->saddr = entry->saddr_storage.s6_addr32; 619 + entry->daddr = entry->daddr_storage.s6_addr32; 620 + } 621 + } else 622 + #endif 623 + { 624 + entry->saddr = &ireq->loc_addr; 625 + entry->daddr = &ireq->rmt_addr; 626 + } 627 + } 628 + 668 629 static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, 669 630 struct request_sock *req, 670 631 struct user_namespace *user_ns, ··· 736 637 r->idiag_inode = 0; 737 638 #if IS_ENABLED(CONFIG_IPV6) 738 639 if (r->idiag_family == AF_INET6) { 739 - *(struct in6_addr *)r->id.idiag_src = inet6_rsk(req)->loc_addr; 740 - *(struct in6_addr *)r->id.idiag_dst = inet6_rsk(req)->rmt_addr; 640 + struct inet_diag_entry entry; 641 + inet_diag_req_addrs(sk, req, &entry); 642 + memcpy(r->id.idiag_src, entry.saddr, sizeof(struct in6_addr)); 643 + memcpy(r->id.idiag_dst, entry.daddr, sizeof(struct in6_addr)); 741 644 } 742 645 #endif 743 646 ··· 792 691 continue; 793 692 794 693 if (bc) { 795 - entry.saddr = 796 - #if IS_ENABLED(CONFIG_IPV6) 797 - (entry.family == AF_INET6) ? 798 - inet6_rsk(req)->loc_addr.s6_addr32 : 799 - #endif 800 - &ireq->loc_addr; 801 - entry.daddr = 802 - #if IS_ENABLED(CONFIG_IPV6) 803 - (entry.family == AF_INET6) ? 804 - inet6_rsk(req)->rmt_addr.s6_addr32 : 805 - #endif 806 - &ireq->rmt_addr; 694 + inet_diag_req_addrs(sk, req, &entry); 807 695 entry.dport = ntohs(ireq->rmt_port); 808 696 809 697 if (!inet_diag_bc_run(bc, &entry))
+10 -11
net/ipv4/ip_fragment.c
··· 707 707 708 708 struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) 709 709 { 710 - const struct iphdr *iph; 710 + struct iphdr iph; 711 711 u32 len; 712 712 713 713 if (skb->protocol != htons(ETH_P_IP)) 714 714 return skb; 715 715 716 - if (!pskb_may_pull(skb, sizeof(struct iphdr))) 716 + if (!skb_copy_bits(skb, 0, &iph, sizeof(iph))) 717 717 return skb; 718 718 719 - iph = ip_hdr(skb); 720 - if (iph->ihl < 5 || iph->version != 4) 721 - 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)) 719 + if (iph.ihl < 5 || iph.version != 4) 727 720 return skb; 728 721 729 - if (ip_is_fragment(ip_hdr(skb))) { 722 + len = ntohs(iph.tot_len); 723 + if (skb->len < len || len < (iph.ihl * 4)) 724 + return skb; 725 + 726 + if (ip_is_fragment(&iph)) { 730 727 skb = skb_share_check(skb, GFP_ATOMIC); 731 728 if (skb) { 729 + if (!pskb_may_pull(skb, iph.ihl*4)) 730 + return skb; 732 731 if (pskb_trim_rcsum(skb, len)) 733 732 return skb; 734 733 memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));