diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2017-10-05 12:59:44 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2018-01-24 19:13:45 -0500 |
commit | 44c02a2c3dc55835e9f0d8ef73966406cd805001 (patch) | |
tree | e75ed9a07fdb0eae51502e0e62f57bb9cf870501 | |
parent | 6a88fbe7257282c19c777d5fe310166e5b3089e8 (diff) |
dev_ioctl(): move copyin/copyout to callers
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | include/linux/netdevice.h | 3 | ||||
-rw-r--r-- | net/core/dev_ioctl.c | 85 | ||||
-rw-r--r-- | net/socket.c | 91 |
3 files changed, 71 insertions, 108 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index df5565d0369c..24a62d590350 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -3315,7 +3315,8 @@ int netdev_rx_handler_register(struct net_device *dev, | |||
3315 | void netdev_rx_handler_unregister(struct net_device *dev); | 3315 | void netdev_rx_handler_unregister(struct net_device *dev); |
3316 | 3316 | ||
3317 | bool dev_valid_name(const char *name); | 3317 | bool dev_valid_name(const char *name); |
3318 | int dev_ioctl(struct net *net, unsigned int cmd, void __user *); | 3318 | int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, |
3319 | bool *need_copyout); | ||
3319 | int dev_ifconf(struct net *net, struct ifconf *, int); | 3320 | int dev_ifconf(struct net *net, struct ifconf *, int); |
3320 | int dev_ethtool(struct net *net, struct ifreq *); | 3321 | int dev_ethtool(struct net *net, struct ifreq *); |
3321 | unsigned int dev_get_flags(const struct net_device *); | 3322 | unsigned int dev_get_flags(const struct net_device *); |
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index d262f159f9fd..0ab1af04296c 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c | |||
@@ -18,26 +18,10 @@ | |||
18 | * match. --pb | 18 | * match. --pb |
19 | */ | 19 | */ |
20 | 20 | ||
21 | static int dev_ifname(struct net *net, struct ifreq __user *arg) | 21 | static int dev_ifname(struct net *net, struct ifreq *ifr) |
22 | { | 22 | { |
23 | struct ifreq ifr; | 23 | ifr->ifr_name[IFNAMSIZ-1] = 0; |
24 | int error; | 24 | return netdev_get_name(net, ifr->ifr_name, ifr->ifr_ifindex); |
25 | |||
26 | /* | ||
27 | * Fetch the caller's info block. | ||
28 | */ | ||
29 | |||
30 | if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) | ||
31 | return -EFAULT; | ||
32 | ifr.ifr_name[IFNAMSIZ-1] = 0; | ||
33 | |||
34 | error = netdev_get_name(net, ifr.ifr_name, ifr.ifr_ifindex); | ||
35 | if (error) | ||
36 | return error; | ||
37 | |||
38 | if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) | ||
39 | return -EFAULT; | ||
40 | return 0; | ||
41 | } | 25 | } |
42 | 26 | ||
43 | static gifconf_func_t *gifconf_list[NPROTO]; | 27 | static gifconf_func_t *gifconf_list[NPROTO]; |
@@ -402,24 +386,24 @@ EXPORT_SYMBOL(dev_load); | |||
402 | * positive or a negative errno code on error. | 386 | * positive or a negative errno code on error. |
403 | */ | 387 | */ |
404 | 388 | ||
405 | int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) | 389 | int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_copyout) |
406 | { | 390 | { |
407 | struct ifreq ifr; | ||
408 | int ret; | 391 | int ret; |
409 | char *colon; | 392 | char *colon; |
410 | 393 | ||
394 | if (need_copyout) | ||
395 | *need_copyout = true; | ||
411 | if (cmd == SIOCGIFNAME) | 396 | if (cmd == SIOCGIFNAME) |
412 | return dev_ifname(net, (struct ifreq __user *)arg); | 397 | return dev_ifname(net, ifr); |
413 | |||
414 | if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) | ||
415 | return -EFAULT; | ||
416 | 398 | ||
417 | ifr.ifr_name[IFNAMSIZ-1] = 0; | 399 | ifr->ifr_name[IFNAMSIZ-1] = 0; |
418 | 400 | ||
419 | colon = strchr(ifr.ifr_name, ':'); | 401 | colon = strchr(ifr->ifr_name, ':'); |
420 | if (colon) | 402 | if (colon) |
421 | *colon = 0; | 403 | *colon = 0; |
422 | 404 | ||
405 | dev_load(net, ifr->ifr_name); | ||
406 | |||
423 | /* | 407 | /* |
424 | * See which interface the caller is talking about. | 408 | * See which interface the caller is talking about. |
425 | */ | 409 | */ |
@@ -439,31 +423,19 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) | |||
439 | case SIOCGIFMAP: | 423 | case SIOCGIFMAP: |
440 | case SIOCGIFINDEX: | 424 | case SIOCGIFINDEX: |
441 | case SIOCGIFTXQLEN: | 425 | case SIOCGIFTXQLEN: |
442 | dev_load(net, ifr.ifr_name); | ||
443 | rcu_read_lock(); | 426 | rcu_read_lock(); |
444 | ret = dev_ifsioc_locked(net, &ifr, cmd); | 427 | ret = dev_ifsioc_locked(net, ifr, cmd); |
445 | rcu_read_unlock(); | 428 | rcu_read_unlock(); |
446 | if (!ret) { | 429 | if (colon) |
447 | if (colon) | 430 | *colon = ':'; |
448 | *colon = ':'; | ||
449 | if (copy_to_user(arg, &ifr, | ||
450 | sizeof(struct ifreq))) | ||
451 | ret = -EFAULT; | ||
452 | } | ||
453 | return ret; | 431 | return ret; |
454 | 432 | ||
455 | case SIOCETHTOOL: | 433 | case SIOCETHTOOL: |
456 | dev_load(net, ifr.ifr_name); | ||
457 | rtnl_lock(); | 434 | rtnl_lock(); |
458 | ret = dev_ethtool(net, &ifr); | 435 | ret = dev_ethtool(net, ifr); |
459 | rtnl_unlock(); | 436 | rtnl_unlock(); |
460 | if (!ret) { | 437 | if (colon) |
461 | if (colon) | 438 | *colon = ':'; |
462 | *colon = ':'; | ||
463 | if (copy_to_user(arg, &ifr, | ||
464 | sizeof(struct ifreq))) | ||
465 | ret = -EFAULT; | ||
466 | } | ||
467 | return ret; | 439 | return ret; |
468 | 440 | ||
469 | /* | 441 | /* |
@@ -477,17 +449,11 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) | |||
477 | case SIOCSIFNAME: | 449 | case SIOCSIFNAME: |
478 | if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) | 450 | if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
479 | return -EPERM; | 451 | return -EPERM; |
480 | dev_load(net, ifr.ifr_name); | ||
481 | rtnl_lock(); | 452 | rtnl_lock(); |
482 | ret = dev_ifsioc(net, &ifr, cmd); | 453 | ret = dev_ifsioc(net, ifr, cmd); |
483 | rtnl_unlock(); | 454 | rtnl_unlock(); |
484 | if (!ret) { | 455 | if (colon) |
485 | if (colon) | 456 | *colon = ':'; |
486 | *colon = ':'; | ||
487 | if (copy_to_user(arg, &ifr, | ||
488 | sizeof(struct ifreq))) | ||
489 | ret = -EFAULT; | ||
490 | } | ||
491 | return ret; | 457 | return ret; |
492 | 458 | ||
493 | /* | 459 | /* |
@@ -528,10 +494,11 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) | |||
528 | /* fall through */ | 494 | /* fall through */ |
529 | case SIOCBONDSLAVEINFOQUERY: | 495 | case SIOCBONDSLAVEINFOQUERY: |
530 | case SIOCBONDINFOQUERY: | 496 | case SIOCBONDINFOQUERY: |
531 | dev_load(net, ifr.ifr_name); | ||
532 | rtnl_lock(); | 497 | rtnl_lock(); |
533 | ret = dev_ifsioc(net, &ifr, cmd); | 498 | ret = dev_ifsioc(net, ifr, cmd); |
534 | rtnl_unlock(); | 499 | rtnl_unlock(); |
500 | if (need_copyout) | ||
501 | *need_copyout = false; | ||
535 | return ret; | 502 | return ret; |
536 | 503 | ||
537 | case SIOCGIFMEM: | 504 | case SIOCGIFMEM: |
@@ -551,13 +518,9 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) | |||
551 | cmd == SIOCGHWTSTAMP || | 518 | cmd == SIOCGHWTSTAMP || |
552 | (cmd >= SIOCDEVPRIVATE && | 519 | (cmd >= SIOCDEVPRIVATE && |
553 | cmd <= SIOCDEVPRIVATE + 15)) { | 520 | cmd <= SIOCDEVPRIVATE + 15)) { |
554 | dev_load(net, ifr.ifr_name); | ||
555 | rtnl_lock(); | 521 | rtnl_lock(); |
556 | ret = dev_ifsioc(net, &ifr, cmd); | 522 | ret = dev_ifsioc(net, ifr, cmd); |
557 | rtnl_unlock(); | 523 | rtnl_unlock(); |
558 | if (!ret && copy_to_user(arg, &ifr, | ||
559 | sizeof(struct ifreq))) | ||
560 | ret = -EFAULT; | ||
561 | return ret; | 524 | return ret; |
562 | } | 525 | } |
563 | return -ENOTTY; | 526 | return -ENOTTY; |
diff --git a/net/socket.c b/net/socket.c index 1ad02d9edbef..45d51555ce47 100644 --- a/net/socket.c +++ b/net/socket.c | |||
@@ -973,10 +973,17 @@ static long sock_do_ioctl(struct net *net, struct socket *sock, | |||
973 | rtnl_unlock(); | 973 | rtnl_unlock(); |
974 | if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf))) | 974 | if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf))) |
975 | err = -EFAULT; | 975 | err = -EFAULT; |
976 | return err; | 976 | } else { |
977 | struct ifreq ifr; | ||
978 | bool need_copyout; | ||
979 | if (copy_from_user(&ifr, argp, sizeof(struct ifreq))) | ||
980 | return -EFAULT; | ||
981 | err = dev_ioctl(net, cmd, &ifr, &need_copyout); | ||
982 | if (!err && need_copyout) | ||
983 | if (copy_to_user(argp, &ifr, sizeof(struct ifreq))) | ||
984 | return -EFAULT; | ||
977 | } | 985 | } |
978 | 986 | return err; | |
979 | return dev_ioctl(net, cmd, argp); | ||
980 | } | 987 | } |
981 | 988 | ||
982 | /* | 989 | /* |
@@ -1000,8 +1007,15 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg) | |||
1000 | sock = file->private_data; | 1007 | sock = file->private_data; |
1001 | sk = sock->sk; | 1008 | sk = sock->sk; |
1002 | net = sock_net(sk); | 1009 | net = sock_net(sk); |
1003 | if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) { | 1010 | if (unlikely(cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15))) { |
1004 | err = dev_ioctl(net, cmd, argp); | 1011 | struct ifreq ifr; |
1012 | bool need_copyout; | ||
1013 | if (copy_from_user(&ifr, argp, sizeof(struct ifreq))) | ||
1014 | return -EFAULT; | ||
1015 | err = dev_ioctl(net, cmd, &ifr, &need_copyout); | ||
1016 | if (!err && need_copyout) | ||
1017 | if (copy_to_user(argp, &ifr, sizeof(struct ifreq))) | ||
1018 | return -EFAULT; | ||
1005 | } else | 1019 | } else |
1006 | #ifdef CONFIG_WEXT_CORE | 1020 | #ifdef CONFIG_WEXT_CORE |
1007 | if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { | 1021 | if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { |
@@ -2695,9 +2709,9 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32) | |||
2695 | { | 2709 | { |
2696 | struct compat_ethtool_rxnfc __user *compat_rxnfc; | 2710 | struct compat_ethtool_rxnfc __user *compat_rxnfc; |
2697 | bool convert_in = false, convert_out = false; | 2711 | bool convert_in = false, convert_out = false; |
2698 | size_t buf_size = ALIGN(sizeof(struct ifreq), 8); | 2712 | size_t buf_size = 0; |
2699 | struct ethtool_rxnfc __user *rxnfc; | 2713 | struct ethtool_rxnfc __user *rxnfc = NULL; |
2700 | struct ifreq __user *ifr; | 2714 | struct ifreq ifr; |
2701 | u32 rule_cnt = 0, actual_rule_cnt; | 2715 | u32 rule_cnt = 0, actual_rule_cnt; |
2702 | u32 ethcmd; | 2716 | u32 ethcmd; |
2703 | u32 data; | 2717 | u32 data; |
@@ -2734,18 +2748,14 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32) | |||
2734 | case ETHTOOL_SRXCLSRLDEL: | 2748 | case ETHTOOL_SRXCLSRLDEL: |
2735 | buf_size += sizeof(struct ethtool_rxnfc); | 2749 | buf_size += sizeof(struct ethtool_rxnfc); |
2736 | convert_in = true; | 2750 | convert_in = true; |
2751 | rxnfc = compat_alloc_user_space(buf_size); | ||
2737 | break; | 2752 | break; |
2738 | } | 2753 | } |
2739 | 2754 | ||
2740 | ifr = compat_alloc_user_space(buf_size); | 2755 | if (copy_from_user(&ifr.ifr_name, &ifr32->ifr_name, IFNAMSIZ)) |
2741 | rxnfc = (void __user *)ifr + ALIGN(sizeof(struct ifreq), 8); | ||
2742 | |||
2743 | if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ)) | ||
2744 | return -EFAULT; | 2756 | return -EFAULT; |
2745 | 2757 | ||
2746 | if (put_user(convert_in ? rxnfc : compat_ptr(data), | 2758 | ifr.ifr_data = convert_in ? rxnfc : (void __user *)compat_rxnfc; |
2747 | &ifr->ifr_ifru.ifru_data)) | ||
2748 | return -EFAULT; | ||
2749 | 2759 | ||
2750 | if (convert_in) { | 2760 | if (convert_in) { |
2751 | /* We expect there to be holes between fs.m_ext and | 2761 | /* We expect there to be holes between fs.m_ext and |
@@ -2773,7 +2783,7 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32) | |||
2773 | return -EFAULT; | 2783 | return -EFAULT; |
2774 | } | 2784 | } |
2775 | 2785 | ||
2776 | ret = dev_ioctl(net, SIOCETHTOOL, ifr); | 2786 | ret = dev_ioctl(net, SIOCETHTOOL, &ifr, NULL); |
2777 | if (ret) | 2787 | if (ret) |
2778 | return ret; | 2788 | return ret; |
2779 | 2789 | ||
@@ -2814,50 +2824,43 @@ static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32) | |||
2814 | 2824 | ||
2815 | static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32) | 2825 | static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32) |
2816 | { | 2826 | { |
2817 | void __user *uptr; | ||
2818 | compat_uptr_t uptr32; | 2827 | compat_uptr_t uptr32; |
2819 | struct ifreq __user *uifr; | 2828 | struct ifreq ifr; |
2829 | void __user *saved; | ||
2830 | int err; | ||
2820 | 2831 | ||
2821 | uifr = compat_alloc_user_space(sizeof(*uifr)); | 2832 | if (copy_from_user(&ifr, uifr32, sizeof(struct compat_ifreq))) |
2822 | if (copy_in_user(uifr, uifr32, sizeof(struct compat_ifreq))) | ||
2823 | return -EFAULT; | 2833 | return -EFAULT; |
2824 | 2834 | ||
2825 | if (get_user(uptr32, &uifr32->ifr_settings.ifs_ifsu)) | 2835 | if (get_user(uptr32, &uifr32->ifr_settings.ifs_ifsu)) |
2826 | return -EFAULT; | 2836 | return -EFAULT; |
2827 | 2837 | ||
2828 | uptr = compat_ptr(uptr32); | 2838 | saved = ifr.ifr_settings.ifs_ifsu.raw_hdlc; |
2829 | 2839 | ifr.ifr_settings.ifs_ifsu.raw_hdlc = compat_ptr(uptr32); | |
2830 | if (put_user(uptr, &uifr->ifr_settings.ifs_ifsu.raw_hdlc)) | ||
2831 | return -EFAULT; | ||
2832 | 2840 | ||
2833 | return dev_ioctl(net, SIOCWANDEV, uifr); | 2841 | err = dev_ioctl(net, SIOCWANDEV, &ifr, NULL); |
2842 | if (!err) { | ||
2843 | ifr.ifr_settings.ifs_ifsu.raw_hdlc = saved; | ||
2844 | if (copy_to_user(uifr32, &ifr, sizeof(struct compat_ifreq))) | ||
2845 | err = -EFAULT; | ||
2846 | } | ||
2847 | return err; | ||
2834 | } | 2848 | } |
2835 | 2849 | ||
2836 | /* Handle ioctls that use ifreq::ifr_data and just need struct ifreq converted */ | 2850 | /* Handle ioctls that use ifreq::ifr_data and just need struct ifreq converted */ |
2837 | static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd, | 2851 | static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd, |
2838 | struct compat_ifreq __user *u_ifreq32) | 2852 | struct compat_ifreq __user *u_ifreq32) |
2839 | { | 2853 | { |
2840 | struct ifreq __user *u_ifreq64; | 2854 | struct ifreq ifreq; |
2841 | char tmp_buf[IFNAMSIZ]; | ||
2842 | void __user *data64; | ||
2843 | u32 data32; | 2855 | u32 data32; |
2844 | 2856 | ||
2845 | if (copy_from_user(&tmp_buf[0], &(u_ifreq32->ifr_ifrn.ifrn_name[0]), | 2857 | if (copy_from_user(ifreq.ifr_name, u_ifreq32->ifr_name, IFNAMSIZ)) |
2846 | IFNAMSIZ)) | ||
2847 | return -EFAULT; | 2858 | return -EFAULT; |
2848 | if (get_user(data32, &u_ifreq32->ifr_ifru.ifru_data)) | 2859 | if (get_user(data32, &u_ifreq32->ifr_data)) |
2849 | return -EFAULT; | 2860 | return -EFAULT; |
2850 | data64 = compat_ptr(data32); | 2861 | ifreq.ifr_data = compat_ptr(data32); |
2851 | 2862 | ||
2852 | u_ifreq64 = compat_alloc_user_space(sizeof(*u_ifreq64)); | 2863 | return dev_ioctl(net, cmd, &ifreq, NULL); |
2853 | |||
2854 | if (copy_to_user(&u_ifreq64->ifr_ifrn.ifrn_name[0], &tmp_buf[0], | ||
2855 | IFNAMSIZ)) | ||
2856 | return -EFAULT; | ||
2857 | if (put_user(data64, &u_ifreq64->ifr_ifru.ifru_data)) | ||
2858 | return -EFAULT; | ||
2859 | |||
2860 | return dev_ioctl(net, cmd, u_ifreq64); | ||
2861 | } | 2864 | } |
2862 | 2865 | ||
2863 | static int compat_sioc_ifmap(struct net *net, unsigned int cmd, | 2866 | static int compat_sioc_ifmap(struct net *net, unsigned int cmd, |
@@ -2865,7 +2868,6 @@ static int compat_sioc_ifmap(struct net *net, unsigned int cmd, | |||
2865 | { | 2868 | { |
2866 | struct ifreq ifr; | 2869 | struct ifreq ifr; |
2867 | struct compat_ifmap __user *uifmap32; | 2870 | struct compat_ifmap __user *uifmap32; |
2868 | mm_segment_t old_fs; | ||
2869 | int err; | 2871 | int err; |
2870 | 2872 | ||
2871 | uifmap32 = &uifr32->ifr_ifru.ifru_map; | 2873 | uifmap32 = &uifr32->ifr_ifru.ifru_map; |
@@ -2879,10 +2881,7 @@ static int compat_sioc_ifmap(struct net *net, unsigned int cmd, | |||
2879 | if (err) | 2881 | if (err) |
2880 | return -EFAULT; | 2882 | return -EFAULT; |
2881 | 2883 | ||
2882 | old_fs = get_fs(); | 2884 | err = dev_ioctl(net, cmd, &ifr, NULL); |
2883 | set_fs(KERNEL_DS); | ||
2884 | err = dev_ioctl(net, cmd, (void __user __force *)&ifr); | ||
2885 | set_fs(old_fs); | ||
2886 | 2885 | ||
2887 | if (cmd == SIOCGIFMAP && !err) { | 2886 | if (cmd == SIOCGIFMAP && !err) { |
2888 | err = copy_to_user(uifr32, &ifr, sizeof(ifr.ifr_name)); | 2887 | err = copy_to_user(uifr32, &ifr, sizeof(ifr.ifr_name)); |