aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2009-11-10 22:39:40 -0500
committerDavid S. Miller <davem@davemloft.net>2009-11-11 22:22:22 -0500
commita2116ed223c88b6c424f42398e54d1607dc785ba (patch)
tree6a01a5d91706b4079a0db3a6d80b9ea606a954ae /net
parentc029f4440fd3f0dcc6923f917536fd62d6ef5d1d (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.c117
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,
2723static int dev_ifsioc(struct net *net, struct socket *sock, 2723static 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
2759static 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
2797static 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
2793struct rtentry32 { 2818struct 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: