diff options
| -rw-r--r-- | include/linux/filter.h | 15 | ||||
| -rw-r--r-- | net/core/filter.c | 86 | ||||
| -rw-r--r-- | net/core/sock_diag.c | 23 |
3 files changed, 93 insertions, 31 deletions
diff --git a/include/linux/filter.h b/include/linux/filter.h index e65e23087367..93a9792e27bc 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h | |||
| @@ -19,14 +19,19 @@ struct compat_sock_fprog { | |||
| 19 | }; | 19 | }; |
| 20 | #endif | 20 | #endif |
| 21 | 21 | ||
| 22 | struct sock_fprog_kern { | ||
| 23 | u16 len; | ||
| 24 | struct sock_filter *filter; | ||
| 25 | }; | ||
| 26 | |||
| 22 | struct sk_buff; | 27 | struct sk_buff; |
| 23 | struct sock; | 28 | struct sock; |
| 24 | 29 | ||
| 25 | struct sk_filter | 30 | struct sk_filter { |
| 26 | { | ||
| 27 | atomic_t refcnt; | 31 | atomic_t refcnt; |
| 28 | u32 jited:1, /* Is our filter JIT'ed? */ | 32 | u32 jited:1, /* Is our filter JIT'ed? */ |
| 29 | len:31; /* Number of filter blocks */ | 33 | len:31; /* Number of filter blocks */ |
| 34 | struct sock_fprog_kern *orig_prog; /* Original BPF program */ | ||
| 30 | struct rcu_head rcu; | 35 | struct rcu_head rcu; |
| 31 | unsigned int (*bpf_func)(const struct sk_buff *skb, | 36 | unsigned int (*bpf_func)(const struct sk_buff *skb, |
| 32 | const struct sock_filter *filter); | 37 | const struct sock_filter *filter); |
| @@ -42,14 +47,20 @@ static inline unsigned int sk_filter_size(unsigned int proglen) | |||
| 42 | offsetof(struct sk_filter, insns[proglen])); | 47 | offsetof(struct sk_filter, insns[proglen])); |
| 43 | } | 48 | } |
| 44 | 49 | ||
| 50 | #define sk_filter_proglen(fprog) \ | ||
| 51 | (fprog->len * sizeof(fprog->filter[0])) | ||
| 52 | |||
| 45 | extern int sk_filter(struct sock *sk, struct sk_buff *skb); | 53 | extern int sk_filter(struct sock *sk, struct sk_buff *skb); |
| 46 | extern unsigned int sk_run_filter(const struct sk_buff *skb, | 54 | extern unsigned int sk_run_filter(const struct sk_buff *skb, |
| 47 | const struct sock_filter *filter); | 55 | const struct sock_filter *filter); |
| 56 | |||
| 48 | extern int sk_unattached_filter_create(struct sk_filter **pfp, | 57 | extern int sk_unattached_filter_create(struct sk_filter **pfp, |
| 49 | struct sock_fprog *fprog); | 58 | struct sock_fprog *fprog); |
| 50 | extern void sk_unattached_filter_destroy(struct sk_filter *fp); | 59 | extern void sk_unattached_filter_destroy(struct sk_filter *fp); |
| 60 | |||
| 51 | extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); | 61 | extern int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); |
| 52 | extern int sk_detach_filter(struct sock *sk); | 62 | extern int sk_detach_filter(struct sock *sk); |
| 63 | |||
| 53 | extern int sk_chk_filter(struct sock_filter *filter, unsigned int flen); | 64 | extern int sk_chk_filter(struct sock_filter *filter, unsigned int flen); |
| 54 | extern int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, unsigned len); | 65 | extern int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, unsigned len); |
| 55 | extern void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to); | 66 | extern void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to); |
diff --git a/net/core/filter.c b/net/core/filter.c index bb3c76458ca9..9730e7fe4770 100644 --- a/net/core/filter.c +++ b/net/core/filter.c | |||
| @@ -629,6 +629,37 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) | |||
| 629 | } | 629 | } |
| 630 | EXPORT_SYMBOL(sk_chk_filter); | 630 | EXPORT_SYMBOL(sk_chk_filter); |
| 631 | 631 | ||
| 632 | static int sk_store_orig_filter(struct sk_filter *fp, | ||
| 633 | const struct sock_fprog *fprog) | ||
| 634 | { | ||
| 635 | unsigned int fsize = sk_filter_proglen(fprog); | ||
| 636 | struct sock_fprog_kern *fkprog; | ||
| 637 | |||
| 638 | fp->orig_prog = kmalloc(sizeof(*fkprog), GFP_KERNEL); | ||
| 639 | if (!fp->orig_prog) | ||
| 640 | return -ENOMEM; | ||
| 641 | |||
| 642 | fkprog = fp->orig_prog; | ||
| 643 | fkprog->len = fprog->len; | ||
| 644 | fkprog->filter = kmemdup(fp->insns, fsize, GFP_KERNEL); | ||
| 645 | if (!fkprog->filter) { | ||
| 646 | kfree(fp->orig_prog); | ||
| 647 | return -ENOMEM; | ||
| 648 | } | ||
| 649 | |||
| 650 | return 0; | ||
| 651 | } | ||
| 652 | |||
| 653 | static void sk_release_orig_filter(struct sk_filter *fp) | ||
| 654 | { | ||
| 655 | struct sock_fprog_kern *fprog = fp->orig_prog; | ||
| 656 | |||
| 657 | if (fprog) { | ||
| 658 | kfree(fprog->filter); | ||
| 659 | kfree(fprog); | ||
| 660 | } | ||
| 661 | } | ||
| 662 | |||
| 632 | /** | 663 | /** |
| 633 | * sk_filter_release_rcu - Release a socket filter by rcu_head | 664 | * sk_filter_release_rcu - Release a socket filter by rcu_head |
| 634 | * @rcu: rcu_head that contains the sk_filter to free | 665 | * @rcu: rcu_head that contains the sk_filter to free |
| @@ -637,6 +668,7 @@ void sk_filter_release_rcu(struct rcu_head *rcu) | |||
| 637 | { | 668 | { |
| 638 | struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu); | 669 | struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu); |
| 639 | 670 | ||
| 671 | sk_release_orig_filter(fp); | ||
| 640 | bpf_jit_free(fp); | 672 | bpf_jit_free(fp); |
| 641 | } | 673 | } |
| 642 | EXPORT_SYMBOL(sk_filter_release_rcu); | 674 | EXPORT_SYMBOL(sk_filter_release_rcu); |
| @@ -669,8 +701,8 @@ static int __sk_prepare_filter(struct sk_filter *fp) | |||
| 669 | int sk_unattached_filter_create(struct sk_filter **pfp, | 701 | int sk_unattached_filter_create(struct sk_filter **pfp, |
| 670 | struct sock_fprog *fprog) | 702 | struct sock_fprog *fprog) |
| 671 | { | 703 | { |
| 704 | unsigned int fsize = sk_filter_proglen(fprog); | ||
| 672 | struct sk_filter *fp; | 705 | struct sk_filter *fp; |
| 673 | unsigned int fsize = sizeof(struct sock_filter) * fprog->len; | ||
| 674 | int err; | 706 | int err; |
| 675 | 707 | ||
| 676 | /* Make sure new filter is there and in the right amounts. */ | 708 | /* Make sure new filter is there and in the right amounts. */ |
| @@ -680,10 +712,16 @@ int sk_unattached_filter_create(struct sk_filter **pfp, | |||
| 680 | fp = kmalloc(sk_filter_size(fprog->len), GFP_KERNEL); | 712 | fp = kmalloc(sk_filter_size(fprog->len), GFP_KERNEL); |
| 681 | if (!fp) | 713 | if (!fp) |
| 682 | return -ENOMEM; | 714 | return -ENOMEM; |
| 715 | |||
| 683 | memcpy(fp->insns, fprog->filter, fsize); | 716 | memcpy(fp->insns, fprog->filter, fsize); |
| 684 | 717 | ||
| 685 | atomic_set(&fp->refcnt, 1); | 718 | atomic_set(&fp->refcnt, 1); |
| 686 | fp->len = fprog->len; | 719 | fp->len = fprog->len; |
| 720 | /* Since unattached filters are not copied back to user | ||
| 721 | * space through sk_get_filter(), we do not need to hold | ||
| 722 | * a copy here, and can spare us the work. | ||
| 723 | */ | ||
| 724 | fp->orig_prog = NULL; | ||
| 687 | 725 | ||
| 688 | err = __sk_prepare_filter(fp); | 726 | err = __sk_prepare_filter(fp); |
| 689 | if (err) | 727 | if (err) |
| @@ -716,7 +754,7 @@ EXPORT_SYMBOL_GPL(sk_unattached_filter_destroy); | |||
| 716 | int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) | 754 | int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) |
| 717 | { | 755 | { |
| 718 | struct sk_filter *fp, *old_fp; | 756 | struct sk_filter *fp, *old_fp; |
| 719 | unsigned int fsize = sizeof(struct sock_filter) * fprog->len; | 757 | unsigned int fsize = sk_filter_proglen(fprog); |
| 720 | unsigned int sk_fsize = sk_filter_size(fprog->len); | 758 | unsigned int sk_fsize = sk_filter_size(fprog->len); |
| 721 | int err; | 759 | int err; |
| 722 | 760 | ||
| @@ -730,6 +768,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) | |||
| 730 | fp = sock_kmalloc(sk, sk_fsize, GFP_KERNEL); | 768 | fp = sock_kmalloc(sk, sk_fsize, GFP_KERNEL); |
| 731 | if (!fp) | 769 | if (!fp) |
| 732 | return -ENOMEM; | 770 | return -ENOMEM; |
| 771 | |||
| 733 | if (copy_from_user(fp->insns, fprog->filter, fsize)) { | 772 | if (copy_from_user(fp->insns, fprog->filter, fsize)) { |
| 734 | sock_kfree_s(sk, fp, sk_fsize); | 773 | sock_kfree_s(sk, fp, sk_fsize); |
| 735 | return -EFAULT; | 774 | return -EFAULT; |
| @@ -738,6 +777,12 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) | |||
| 738 | atomic_set(&fp->refcnt, 1); | 777 | atomic_set(&fp->refcnt, 1); |
| 739 | fp->len = fprog->len; | 778 | fp->len = fprog->len; |
| 740 | 779 | ||
| 780 | err = sk_store_orig_filter(fp, fprog); | ||
| 781 | if (err) { | ||
| 782 | sk_filter_uncharge(sk, fp); | ||
| 783 | return -ENOMEM; | ||
| 784 | } | ||
| 785 | |||
| 741 | err = __sk_prepare_filter(fp); | 786 | err = __sk_prepare_filter(fp); |
| 742 | if (err) { | 787 | if (err) { |
| 743 | sk_filter_uncharge(sk, fp); | 788 | sk_filter_uncharge(sk, fp); |
| @@ -750,6 +795,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) | |||
| 750 | 795 | ||
| 751 | if (old_fp) | 796 | if (old_fp) |
| 752 | sk_filter_uncharge(sk, old_fp); | 797 | sk_filter_uncharge(sk, old_fp); |
| 798 | |||
| 753 | return 0; | 799 | return 0; |
| 754 | } | 800 | } |
| 755 | EXPORT_SYMBOL_GPL(sk_attach_filter); | 801 | EXPORT_SYMBOL_GPL(sk_attach_filter); |
| @@ -769,6 +815,7 @@ int sk_detach_filter(struct sock *sk) | |||
| 769 | sk_filter_uncharge(sk, filter); | 815 | sk_filter_uncharge(sk, filter); |
| 770 | ret = 0; | 816 | ret = 0; |
| 771 | } | 817 | } |
| 818 | |||
| 772 | return ret; | 819 | return ret; |
| 773 | } | 820 | } |
| 774 | EXPORT_SYMBOL_GPL(sk_detach_filter); | 821 | EXPORT_SYMBOL_GPL(sk_detach_filter); |
| @@ -851,34 +898,41 @@ void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to) | |||
| 851 | to-> | ||
