diff options
author | Arnd Bergmann <arnd@arndb.de> | 2009-11-10 22:39:40 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-11-11 22:22:22 -0500 |
commit | a2116ed223c88b6c424f42398e54d1607dc785ba (patch) | |
tree | 6a01a5d91706b4079a0db3a6d80b9ea606a954ae /net | |
parent | c029f4440fd3f0dcc6923f917536fd62d6ef5d1d (diff) |
net/compat: fix dev_ifsioc emulation corner cases
Handling for SIOCSHWTSTAMP is broken on architectures
with a split user/kernel address space like s390,
because it passes a real user pointer while using
set_fs(KERNEL_DS).
A similar problem might arise the next time somebody
adds code to dev_ifsioc.
Split up dev_ifsioc into three separate functions for
SIOCSHWTSTAMP, SIOC*IFMAP and all other numbers so
we can get rid of set_fs in all potentially affected
cases.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Patrick Ohly <patrick.ohly@intel.com>
Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/socket.c | 117 |
1 files changed, 72 insertions, 45 deletions
diff --git a/net/socket.c b/net/socket.c index befd9f5b1620..05c482848a67 100644 --- a/net/socket.c +++ b/net/socket.c | |||
@@ -2723,38 +2723,15 @@ static int siocdevprivate_ioctl(struct net *net, unsigned int cmd, | |||
2723 | static int dev_ifsioc(struct net *net, struct socket *sock, | 2723 | static int dev_ifsioc(struct net *net, struct socket *sock, |
2724 | unsigned int cmd, struct compat_ifreq __user *uifr32) | 2724 | unsigned int cmd, struct compat_ifreq __user *uifr32) |
2725 | { | 2725 | { |
2726 | struct ifreq ifr; | 2726 | struct ifreq __user *uifr; |
2727 | struct compat_ifmap __user *uifmap32; | ||
2728 | mm_segment_t old_fs; | ||
2729 | int err; | 2727 | int err; |
2730 | 2728 | ||
2731 | uifmap32 = &uifr32->ifr_ifru.ifru_map; | 2729 | uifr = compat_alloc_user_space(sizeof(*uifr)); |
2732 | switch (cmd) { | 2730 | if (copy_in_user(uifr, uifr32, sizeof(*uifr32))) |
2733 | case SIOCSIFMAP: | 2731 | return -EFAULT; |
2734 | err = copy_from_user(&ifr, uifr32, sizeof(ifr.ifr_name)); | 2732 | |
2735 | err |= __get_user(ifr.ifr_map.mem_start, &uifmap32->mem_start); | 2733 | err = sock_do_ioctl(net, sock, cmd, (unsigned long)uifr); |
2736 | err |= __get_user(ifr.ifr_map.mem_end, &uifmap32->mem_end); | 2734 | |
2737 | err |= __get_user(ifr.ifr_map.base_addr, &uifmap32->base_addr); | ||
2738 | err |= __get_user(ifr.ifr_map.irq, &uifmap32->irq); | ||
2739 | err |= __get_user(ifr.ifr_map.dma, &uifmap32->dma); | ||
2740 | err |= __get_user(ifr.ifr_map.port, &uifmap32->port); | ||
2741 | if (err) | ||
2742 | return -EFAULT; | ||
2743 | break; | ||
2744 | case SIOCSHWTSTAMP: | ||
2745 | if (copy_from_user(&ifr, uifr32, sizeof(*uifr32))) | ||
2746 | return -EFAULT; | ||
2747 | ifr.ifr_data = compat_ptr(uifr32->ifr_ifru.ifru_data); | ||
2748 | break; | ||
2749 | default: | ||
2750 | if (copy_from_user(&ifr, uifr32, sizeof(*uifr32))) | ||
2751 | return -EFAULT; | ||
2752 | break; | ||
2753 | } | ||
2754 | old_fs = get_fs(); | ||
2755 | set_fs (KERNEL_DS); | ||
2756 | err = sock_do_ioctl(net, sock, cmd, (unsigned long)&ifr); | ||
2757 | set_fs (old_fs); | ||
2758 | if (!err) { | 2735 | if (!err) { |
2759 | switch (cmd) { | 2736 | switch (cmd) { |
2760 | case SIOCGIFFLAGS: | 2737 | case SIOCGIFFLAGS: |
@@ -2771,18 +2748,7 @@ static int dev_ifsioc(struct net *net, struct socket *sock, | |||
2771 | case SIOCGIFTXQLEN: | 2748 | case SIOCGIFTXQLEN: |
2772 | case SIOCGMIIPHY: | 2749 | case SIOCGMIIPHY: |
2773 | case SIOCGMIIREG: | 2750 | case SIOCGMIIREG: |
2774 | if (copy_to_user(uifr32, &ifr, sizeof(*uifr32))) | 2751 | if (copy_in_user(uifr32, uifr, sizeof(*uifr32))) |
2775 | return -EFAULT; | ||
2776 | break; | ||
2777 | case SIOCGIFMAP: | ||
2778 | err = copy_to_user(uifr32, &ifr, sizeof(ifr.ifr_name)); | ||
2779 | err |= __put_user(ifr.ifr_map.mem_start, &uifmap32->mem_start); | ||
2780 | err |= __put_user(ifr.ifr_map.mem_end, &uifmap32->mem_end); | ||
2781 | err |= __put_user(ifr.ifr_map.base_addr, &uifmap32->base_addr); | ||
2782 | err |= __put_user(ifr.ifr_map.irq, &uifmap32->irq); | ||
2783 | err |= __put_user(ifr.ifr_map.dma, &uifmap32->dma); | ||
2784 | err |= __put_user(ifr.ifr_map.port, &uifmap32->port); | ||
2785 | if (err) | ||
2786 | err = -EFAULT; | 2752 | err = -EFAULT; |
2787 | break; | 2753 | break; |
2788 | } | 2754 | } |
@@ -2790,6 +2756,65 @@ static int dev_ifsioc(struct net *net, struct socket *sock, | |||
2790 | return err; | 2756 | return err; |
2791 | } | 2757 | } |
2792 | 2758 | ||
2759 | static int compat_sioc_ifmap(struct net *net, unsigned int cmd, | ||
2760 | struct compat_ifreq __user *uifr32) | ||
2761 | { | ||
2762 | struct ifreq ifr; | ||
2763 | struct compat_ifmap __user *uifmap32; | ||
2764 | mm_segment_t old_fs; | ||
2765 | int err; | ||
2766 | |||
2767 | uifmap32 = &uifr32->ifr_ifru.ifru_map; | ||
2768 | err = copy_from_user(&ifr, uifr32, sizeof(ifr.ifr_name)); | ||
2769 | err |= __get_user(ifr.ifr_map.mem_start, &uifmap32->mem_start); | ||
2770 | err |= __get_user(ifr.ifr_map.mem_end, &uifmap32->mem_end); | ||
2771 | err |= __get_user(ifr.ifr_map.base_addr, &uifmap32->base_addr); | ||
2772 | err |= __get_user(ifr.ifr_map.irq, &uifmap32->irq); | ||
2773 | err |= __get_user(ifr.ifr_map.dma, &uifmap32->dma); | ||
2774 | err |= __get_user(ifr.ifr_map.port, &uifmap32->port); | ||
2775 | if (err) | ||
2776 | return -EFAULT; | ||
2777 | |||
2778 | old_fs = get_fs(); | ||
2779 | set_fs (KERNEL_DS); | ||
2780 | err = dev_ioctl(net, cmd, (void __user *)&ifr); | ||
2781 | set_fs (old_fs); | ||
2782 | |||
2783 | if (cmd == SIOCGIFMAP && !err) { | ||
2784 | err = copy_to_user(uifr32, &ifr, sizeof(ifr.ifr_name)); | ||
2785 | err |= __put_user(ifr.ifr_map.mem_start, &uifmap32->mem_start); | ||
2786 | err |= __put_user(ifr.ifr_map.mem_end, &uifmap32->mem_end); | ||
2787 | err |= __put_user(ifr.ifr_map.base_addr, &uifmap32->base_addr); | ||
2788 | err |= __put_user(ifr.ifr_map.irq, &uifmap32->irq); | ||
2789 | err |= __put_user(ifr.ifr_map.dma, &uifmap32->dma); | ||
2790 | err |= __put_user(ifr.ifr_map.port, &uifmap32->port); | ||
2791 | if (err) | ||
2792 | err = -EFAULT; | ||
2793 | } | ||
2794 | return err; | ||
2795 | } | ||
2796 | |||
2797 | static int compat_siocshwtstamp(struct net *net, struct compat_ifreq __user *uifr32) | ||
2798 | { | ||
2799 | void __user *uptr; | ||
2800 | compat_uptr_t uptr32; | ||
2801 | struct ifreq __user *uifr; | ||
2802 | |||
2803 | uifr = compat_alloc_user_space(sizeof (*uifr)); | ||
2804 | if (copy_in_user(uifr, uifr32, sizeof(struct compat_ifreq))) | ||
2805 | return -EFAULT; | ||
2806 | |||
2807 | if (get_user(uptr32, &uifr32->ifr_data)) | ||
2808 | return -EFAULT; | ||
2809 | |||
2810 | uptr = compat_ptr(uptr32); | ||
2811 | |||
2812 | if (put_user(uptr, &uifr->ifr_data)) | ||
2813 | return -EFAULT; | ||
2814 | |||
2815 | return dev_ioctl(net, SIOCSHWTSTAMP, uifr); | ||
2816 | } | ||
2817 | |||
2793 | struct rtentry32 { | 2818 | struct rtentry32 { |
2794 | u32 rt_pad1; | 2819 | u32 rt_pad1; |
2795 | struct sockaddr rt_dst; /* target address */ | 2820 | struct sockaddr rt_dst; /* target address */ |
@@ -3081,6 +3106,9 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock, | |||
3081 | return ethtool_ioctl(net, argp); | 3106 | return ethtool_ioctl(net, argp); |
3082 | case SIOCWANDEV: | 3107 | case SIOCWANDEV: |
3083 | return compat_siocwandev(net, argp); | 3108 | return compat_siocwandev(net, argp); |
3109 | case SIOCGIFMAP: | ||
3110 | case SIOCSIFMAP: | ||
3111 | return compat_sioc_ifmap(net, cmd, argp); | ||
3084 | case SIOCBONDENSLAVE: | 3112 | case SIOCBONDENSLAVE: |
3085 | case SIOCBONDRELEASE: | 3113 | case SIOCBONDRELEASE: |
3086 | case SIOCBONDSETHWADDR: | 3114 | case SIOCBONDSETHWADDR: |
@@ -3095,6 +3123,8 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock, | |||
3095 | return do_siocgstamp(net, sock, cmd, argp); | 3123 | return do_siocgstamp(net, sock, cmd, argp); |
3096 | case SIOCGSTAMPNS: | 3124 | case SIOCGSTAMPNS: |
3097 | return do_siocgstampns(net, sock, cmd, argp); | 3125 | return do_siocgstampns(net, sock, cmd, argp); |
3126 | case SIOCSHWTSTAMP: | ||
3127 | return compat_siocshwtstamp(net, argp); | ||
3098 | 3128 | ||
3099 | case FIOSETOWN: | 3129 | case FIOSETOWN: |
3100 | case SIOCSPGRP: | 3130 | case SIOCSPGRP: |
@@ -3121,12 +3151,9 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock, | |||
3121 | case SIOCADDMULTI: | 3151 | case SIOCADDMULTI: |
3122 | case SIOCDELMULTI: | 3152 | case SIOCDELMULTI: |
3123 | case SIOCGIFINDEX: | 3153 | case SIOCGIFINDEX: |
3124 | case SIOCGIFMAP: | ||
3125 | case SIOCSIFMAP: | ||
3126 | case SIOCGIFADDR: | 3154 | case SIOCGIFADDR: |
3127 | case SIOCSIFADDR: | 3155 | case SIOCSIFADDR: |
3128 | case SIOCSIFHWBROADCAST: | 3156 | case SIOCSIFHWBROADCAST: |
3129 | case SIOCSHWTSTAMP: | ||
3130 | case SIOCDIFADDR: | 3157 | case SIOCDIFADDR: |
3131 | case SIOCGIFBRDADDR: | 3158 | case SIOCGIFBRDADDR: |
3132 | case SIOCSIFBRDADDR: | 3159 | case SIOCSIFBRDADDR: |