Merge branch 'fix/hda' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
[linux-drm-fsl-dcu.git] / net / core / ethtool.c
index a0f4964033d289b8d1b4959d4d1196ccc094bdff..75e4ffeb8cc99dae9c03c07c39962b17c4453f43 100644 (file)
@@ -318,23 +318,33 @@ out:
 }
 
 static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev,
-                                               void __user *useraddr)
+                                               u32 cmd, void __user *useraddr)
 {
-       struct ethtool_rxnfc cmd;
+       struct ethtool_rxnfc info;
+       size_t info_size = sizeof(info);
 
        if (!dev->ethtool_ops->set_rxnfc)
                return -EOPNOTSUPP;
 
-       if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
+       /* struct ethtool_rxnfc was originally defined for
+        * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
+        * members.  User-space might still be using that
+        * definition. */
+       if (cmd == ETHTOOL_SRXFH)
+               info_size = (offsetof(struct ethtool_rxnfc, data) +
+                            sizeof(info.data));
+
+       if (copy_from_user(&info, useraddr, info_size))
                return -EFAULT;
 
-       return dev->ethtool_ops->set_rxnfc(dev, &cmd);
+       return dev->ethtool_ops->set_rxnfc(dev, &info);
 }
 
 static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
-                                               void __user *useraddr)
+                                               u32 cmd, void __user *useraddr)
 {
        struct ethtool_rxnfc info;
+       size_t info_size = sizeof(info);
        const struct ethtool_ops *ops = dev->ethtool_ops;
        int ret;
        void *rule_buf = NULL;
@@ -342,13 +352,22 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
        if (!ops->get_rxnfc)
                return -EOPNOTSUPP;
 
-       if (copy_from_user(&info, useraddr, sizeof(info)))
+       /* struct ethtool_rxnfc was originally defined for
+        * ETHTOOL_{G,S}RXFH with only the cmd, flow_type and data
+        * members.  User-space might still be using that
+        * definition. */
+       if (cmd == ETHTOOL_GRXFH)
+               info_size = (offsetof(struct ethtool_rxnfc, data) +
+                            sizeof(info.data));
+
+       if (copy_from_user(&info, useraddr, info_size))
                return -EFAULT;
 
        if (info.cmd == ETHTOOL_GRXCLSRLALL) {
                if (info.rule_cnt > 0) {
-                       rule_buf = kmalloc(info.rule_cnt * sizeof(u32),
-                                          GFP_USER);
+                       if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32))
+                               rule_buf = kmalloc(info.rule_cnt * sizeof(u32),
+                                                  GFP_USER);
                        if (!rule_buf)
                                return -ENOMEM;
                }
@@ -359,7 +378,7 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
                goto err_out;
 
        ret = -EFAULT;
-       if (copy_to_user(useraddr, &info, sizeof(info)))
+       if (copy_to_user(useraddr, &info, info_size))
                goto err_out;
 
        if (rule_buf) {
@@ -1516,12 +1535,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
        case ETHTOOL_GRXCLSRLCNT:
        case ETHTOOL_GRXCLSRULE:
        case ETHTOOL_GRXCLSRLALL:
-               rc = ethtool_get_rxnfc(dev, useraddr);
+               rc = ethtool_get_rxnfc(dev, ethcmd, useraddr);
                break;
        case ETHTOOL_SRXFH:
        case ETHTOOL_SRXCLSRLDEL:
        case ETHTOOL_SRXCLSRLINS:
-               rc = ethtool_set_rxnfc(dev, useraddr);
+               rc = ethtool_set_rxnfc(dev, ethcmd, useraddr);
                break;
        case ETHTOOL_GGRO:
                rc = ethtool_get_gro(dev, useraddr);