diff options
-rw-r--r-- | net/wireless/wext.c | 141 |
1 files changed, 74 insertions, 67 deletions
diff --git a/net/wireless/wext.c b/net/wireless/wext.c index d17c0f45accb..a1cd19add6d8 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c | |||
@@ -890,25 +890,22 @@ static int ioctl_standard_call(struct net_device * dev, | |||
890 | * a iw_handler but process it in your ioctl handler (i.e. use the | 890 | * a iw_handler but process it in your ioctl handler (i.e. use the |
891 | * old driver API). | 891 | * old driver API). |
892 | */ | 892 | */ |
893 | static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, | 893 | static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd, |
894 | unsigned int cmd, iw_handler handler) | 894 | const struct iw_priv_args **descrp) |
895 | { | 895 | { |
896 | struct iwreq * iwr = (struct iwreq *) ifr; | 896 | const struct iw_priv_args *descr; |
897 | const struct iw_priv_args * descr = NULL; | 897 | int i, extra_size; |
898 | struct iw_request_info info; | ||
899 | int extra_size = 0; | ||
900 | int i; | ||
901 | int ret = -EINVAL; | ||
902 | 898 | ||
903 | /* Get the description of the IOCTL */ | 899 | descr = NULL; |
904 | for (i = 0; i < dev->wireless_handlers->num_private_args; i++) | 900 | for (i = 0; i < dev->wireless_handlers->num_private_args; i++) { |
905 | if (cmd == dev->wireless_handlers->private_args[i].cmd) { | 901 | if (cmd == dev->wireless_handlers->private_args[i].cmd) { |
906 | descr = &(dev->wireless_handlers->private_args[i]); | 902 | descr = &dev->wireless_handlers->private_args[i]; |
907 | break; | 903 | break; |
908 | } | 904 | } |
905 | } | ||
909 | 906 | ||
910 | /* Compute the size of the set/get arguments */ | 907 | extra_size = 0; |
911 | if (descr != NULL) { | 908 | if (descr) { |
912 | if (IW_IS_SET(cmd)) { | 909 | if (IW_IS_SET(cmd)) { |
913 | int offset = 0; /* For sub-ioctls */ | 910 | int offset = 0; /* For sub-ioctls */ |
914 | /* Check for sub-ioctl handler */ | 911 | /* Check for sub-ioctl handler */ |
@@ -933,72 +930,82 @@ static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, | |||
933 | extra_size = 0; | 930 | extra_size = 0; |
934 | } | 931 | } |
935 | } | 932 | } |
933 | *descrp = descr; | ||
934 | return extra_size; | ||
935 | } | ||
936 | 936 | ||
937 | /* Prepare the call */ | 937 | static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd, |
938 | info.cmd = cmd; | 938 | const struct iw_priv_args *descr, |
939 | info.flags = 0; | 939 | iw_handler handler, struct net_device *dev, |
940 | struct iw_request_info *info, int extra_size) | ||
941 | { | ||
942 | char *extra; | ||
943 | int err; | ||
940 | 944 | ||
941 | /* Check if we have a pointer to user space data or not. */ | 945 | /* Check what user space is giving us */ |
942 | if (extra_size == 0) { | 946 | if (IW_IS_SET(cmd)) { |
943 | /* No extra arguments. Trivial to handle */ | 947 | if (!iwp->pointer && iwp->length != 0) |
944 | ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); | 948 | return -EFAULT; |
945 | } else { | ||
946 | char * extra; | ||
947 | int err; | ||
948 | 949 | ||
949 | /* Check what user space is giving us */ | 950 | if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK)) |
950 | if (IW_IS_SET(cmd)) { | 951 | return -E2BIG; |
951 | /* Check NULL pointer */ | 952 | } else if (!iwp->pointer) |
952 | if ((iwr->u.data.pointer == NULL) && | 953 | return -EFAULT; |
953 | (iwr->u.data.length != 0)) | ||
954 | return -EFAULT; | ||
955 | 954 | ||
956 | /* Does it fits within bounds ? */ | 955 | extra = kmalloc(extra_size, GFP_KERNEL); |
957 | if (iwr->u.data.length > (descr->set_args & | 956 | if (!extra) |
958 | IW_PRIV_SIZE_MASK)) | 957 | return -ENOMEM; |
959 | return -E2BIG; | ||
960 | } else if (iwr->u.data.pointer == NULL) | ||
961 | return -EFAULT; | ||
962 | 958 | ||
963 | /* Always allocate for max space. Easier, and won't last | 959 | /* If it is a SET, get all the extra data in here */ |
964 | * long... */ | 960 | if (IW_IS_SET(cmd) && (iwp->length != 0)) { |
965 | extra = kmalloc(extra_size, GFP_KERNEL); | 961 | if (copy_from_user(extra, iwp->pointer, extra_size)) { |
966 | if (extra == NULL) | 962 | err = -EFAULT; |
967 | return -ENOMEM; | 963 | goto out; |
968 | |||
969 | /* If it is a SET, get all the extra data in here */ | ||
970 | if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { | ||
971 | err = copy_from_user(extra, iwr->u.data.pointer, | ||
972 | extra_size); | ||
973 | if (err) { | ||
974 | kfree(extra); | ||
975 | return -EFAULT; | ||
976 | } | ||
977 | } | 964 | } |
965 | } | ||
978 | 966 | ||
979 | /* Call the handler */ | 967 | /* Call the handler */ |
980 | ret = handler(dev, &info, &(iwr->u), extra); | 968 | err = handler(dev, info, (union iwreq_data *) iwp, extra); |
981 | 969 | ||
982 | /* If we have something to return to the user */ | 970 | /* If we have something to return to the user */ |
983 | if (!ret && IW_IS_GET(cmd)) { | 971 | if (!err && IW_IS_GET(cmd)) { |
972 | /* Adjust for the actual length if it's variable, | ||
973 | * avoid leaking kernel bits outside. | ||
974 | */ | ||
975 | if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) | ||
976 | extra_size = adjust_priv_size(descr->get_args, iwp); | ||
984 | 977 | ||
985 | /* Adjust for the actual length if it's variable, | 978 | if (copy_to_user(iwp->pointer, extra, extra_size)) |
986 | * avoid leaking kernel bits outside. */ | 979 | err = -EFAULT; |
987 | if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) { | 980 | } |
988 | extra_size = adjust_priv_size(descr->get_args, | ||
989 | &(iwr->u.data)); | ||
990 | } | ||
991 | 981 | ||
992 | err = copy_to_user(iwr->u.data.pointer, extra, | 982 | out: |
993 | extra_size); | 983 | kfree(extra); |
994 | if (err) | 984 | return err; |
995 | ret = -EFAULT; | 985 | } |
996 | } | ||
997 | 986 | ||
998 | /* Cleanup - I told you it wasn't that long ;-) */ | 987 | static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, |
999 | kfree(extra); | 988 | unsigned int cmd, iw_handler handler) |
1000 | } | 989 | { |
990 | struct iwreq *iwr = (struct iwreq *) ifr; | ||
991 | int extra_size = 0, ret = -EINVAL; | ||
992 | const struct iw_priv_args *descr; | ||
993 | struct iw_request_info info; | ||
1001 | 994 | ||
995 | extra_size = get_priv_descr_and_size(dev, cmd, &descr); | ||
996 | |||
997 | /* Prepare the call */ | ||
998 | info.cmd = cmd; | ||
999 | info.flags = 0; | ||
1000 | |||
1001 | /* Check if we have a pointer to user space data or not. */ | ||
1002 | if (extra_size == 0) { | ||
1003 | /* No extra arguments. Trivial to handle */ | ||
1004 | ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); | ||
1005 | } else { | ||
1006 | ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr, | ||
1007 | handler, dev, &info, extra_size); | ||
1008 | } | ||
1002 | 1009 | ||
1003 | /* Call commit handler if needed and defined */ | 1010 | /* Call commit handler if needed and defined */ |
1004 | if (ret == -EIWCOMMIT) | 1011 | if (ret == -EIWCOMMIT) |