Merge tag 'compat-ioctl-5.5' of git://git.kernel.org:/pub/scm/linux/kernel/git/arnd...
[linux-2.6-block.git] / drivers / net / ppp / ppp_generic.c
index 61824bbb55887232ce61c0d0b5f5bc304274ed3e..0cb1c2d0a8bcb84bf14eaffdef3235fbcbff89fb 100644 (file)
@@ -270,7 +270,7 @@ static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb);
 static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp);
 static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb);
 #endif /* CONFIG_PPP_MULTILINK */
-static int ppp_set_compress(struct ppp *ppp, unsigned long arg);
+static int ppp_set_compress(struct ppp *ppp, struct ppp_option_data *data);
 static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound);
 static void ppp_ccp_closed(struct ppp *ppp);
 static struct compressor *find_compressor(int type);
@@ -554,36 +554,66 @@ static __poll_t ppp_poll(struct file *file, poll_table *wait)
 }
 
 #ifdef CONFIG_PPP_FILTER
-static int get_filter(void __user *arg, struct sock_filter **p)
+static struct bpf_prog *get_filter(struct sock_fprog *uprog)
+{
+       struct sock_fprog_kern fprog;
+       struct bpf_prog *res = NULL;
+       int err;
+
+       if (!uprog->len)
+               return NULL;
+
+       /* uprog->len is unsigned short, so no overflow here */
+       fprog.len = uprog->len * sizeof(struct sock_filter);
+       fprog.filter = memdup_user(uprog->filter, fprog.len);
+       if (IS_ERR(fprog.filter))
+               return ERR_CAST(fprog.filter);
+
+       err = bpf_prog_create(&res, &fprog);
+       kfree(fprog.filter);
+
+       return err ? ERR_PTR(err) : res;
+}
+
+static struct bpf_prog *ppp_get_filter(struct sock_fprog __user *p)
 {
        struct sock_fprog uprog;
-       struct sock_filter *code = NULL;
-       int len;
 
-       if (copy_from_user(&uprog, arg, sizeof(uprog)))
-               return -EFAULT;
+       if (copy_from_user(&uprog, p, sizeof(struct sock_fprog)))
+               return ERR_PTR(-EFAULT);
+       return get_filter(&uprog);
+}
 
-       if (!uprog.len) {
-               *p = NULL;
-               return 0;
-       }
+#ifdef CONFIG_COMPAT
+struct sock_fprog32 {
+       unsigned short len;
+       compat_caddr_t filter;
+};
 
-       len = uprog.len * sizeof(struct sock_filter);
-       code = memdup_user(uprog.filter, len);
-       if (IS_ERR(code))
-               return PTR_ERR(code);
+#define PPPIOCSPASS32          _IOW('t', 71, struct sock_fprog32)
+#define PPPIOCSACTIVE32                _IOW('t', 70, struct sock_fprog32)
+
+static struct bpf_prog *compat_ppp_get_filter(struct sock_fprog32 __user *p)
+{
+       struct sock_fprog32 uprog32;
+       struct sock_fprog uprog;
 
-       *p = code;
-       return uprog.len;
+       if (copy_from_user(&uprog32, p, sizeof(struct sock_fprog32)))
+               return ERR_PTR(-EFAULT);
+       uprog.len = uprog32.len;
+       uprog.filter = compat_ptr(uprog32.filter);
+       return get_filter(&uprog);
 }
-#endif /* CONFIG_PPP_FILTER */
+#endif
+#endif
 
 static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct ppp_file *pf;
        struct ppp *ppp;
        int err = -EFAULT, val, val2, i;
-       struct ppp_idle idle;
+       struct ppp_idle32 idle32;
+       struct ppp_idle64 idle64;
        struct npioctl npi;
        int unit, cflags;
        struct slcompress *vj;
@@ -679,9 +709,14 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                break;
 
        case PPPIOCSCOMPRESS:
-               err = ppp_set_compress(ppp, arg);
+       {
+               struct ppp_option_data data;
+               if (copy_from_user(&data, argp, sizeof(data)))
+                       err = -EFAULT;
+               else
+                       err = ppp_set_compress(ppp, &data);
                break;
-
+       }
        case PPPIOCGUNIT:
                if (put_user(ppp->file.index, p))
                        break;
@@ -701,10 +736,18 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                err = 0;
                break;
 
-       case PPPIOCGIDLE:
-               idle.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
-               idle.recv_idle = (jiffies - ppp->last_recv) / HZ;
-               if (copy_to_user(argp, &idle, sizeof(idle)))
+       case PPPIOCGIDLE32:
+                idle32.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
+                idle32.recv_idle = (jiffies - ppp->last_recv) / HZ;
+                if (copy_to_user(argp, &idle32, sizeof(idle32)))
+                       break;
+               err = 0;
+               break;
+
+       case PPPIOCGIDLE64:
+               idle64.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
+               idle64.recv_idle = (jiffies - ppp->last_recv) / HZ;
+               if (copy_to_user(argp, &idle64, sizeof(idle64)))
                        break;
                err = 0;
                break;
@@ -753,55 +796,25 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
 #ifdef CONFIG_PPP_FILTER
        case PPPIOCSPASS:
-       {
-               struct sock_filter *code;
-
-               err = get_filter(argp, &code);
-               if (err >= 0) {
-                       struct bpf_prog *pass_filter = NULL;
-                       struct sock_fprog_kern fprog = {
-                               .len = err,
-                               .filter = code,
-                       };
-
-                       err = 0;
-                       if (fprog.filter)
-                               err = bpf_prog_create(&pass_filter, &fprog);
-                       if (!err) {
-                               ppp_lock(ppp);
-                               if (ppp->pass_filter)
-                                       bpf_prog_destroy(ppp->pass_filter);
-                               ppp->pass_filter = pass_filter;
-                               ppp_unlock(ppp);
-                       }
-                       kfree(code);
-               }
-               break;
-       }
        case PPPIOCSACTIVE:
        {
-               struct sock_filter *code;
-
-               err = get_filter(argp, &code);
-               if (err >= 0) {
-                       struct bpf_prog *active_filter = NULL;
-                       struct sock_fprog_kern fprog = {
-                               .len = err,
-                               .filter = code,
-                       };
+               struct bpf_prog *filter = ppp_get_filter(argp);
+               struct bpf_prog **which;
 
-                       err = 0;
-                       if (fprog.filter)
-                               err = bpf_prog_create(&active_filter, &fprog);
-                       if (!err) {
-                               ppp_lock(ppp);
-                               if (ppp->active_filter)
-                                       bpf_prog_destroy(ppp->active_filter);
-                               ppp->active_filter = active_filter;
-                               ppp_unlock(ppp);
-                       }
-                       kfree(code);
+               if (IS_ERR(filter)) {
+                       err = PTR_ERR(filter);
+                       break;
                }
+               if (cmd == PPPIOCSPASS)
+                       which = &ppp->pass_filter;
+               else
+                       which = &ppp->active_filter;
+               ppp_lock(ppp);
+               if (*which)
+                       bpf_prog_destroy(*which);
+               *which = filter;
+               ppp_unlock(ppp);
+               err = 0;
                break;
        }
 #endif /* CONFIG_PPP_FILTER */
@@ -827,6 +840,77 @@ out:
        return err;
 }
 
+#ifdef CONFIG_COMPAT
+struct ppp_option_data32 {
+       compat_uptr_t           ptr;
+       u32                     length;
+       compat_int_t            transmit;
+};
+#define PPPIOCSCOMPRESS32      _IOW('t', 77, struct ppp_option_data32)
+
+static long ppp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct ppp_file *pf;
+       int err = -ENOIOCTLCMD;
+       void __user *argp = (void __user *)arg;
+
+       mutex_lock(&ppp_mutex);
+
+       pf = file->private_data;
+       if (pf && pf->kind == INTERFACE) {
+               struct ppp *ppp = PF_TO_PPP(pf);
+               switch (cmd) {
+#ifdef CONFIG_PPP_FILTER
+               case PPPIOCSPASS32:
+               case PPPIOCSACTIVE32:
+               {
+                       struct bpf_prog *filter = compat_ppp_get_filter(argp);
+                       struct bpf_prog **which;
+
+                       if (IS_ERR(filter)) {
+                               err = PTR_ERR(filter);
+                               break;
+                       }
+                       if (cmd == PPPIOCSPASS32)
+                               which = &ppp->pass_filter;
+                       else
+                               which = &ppp->active_filter;
+                       ppp_lock(ppp);
+                       if (*which)
+                               bpf_prog_destroy(*which);
+                       *which = filter;
+                       ppp_unlock(ppp);
+                       err = 0;
+                       break;
+               }
+#endif /* CONFIG_PPP_FILTER */
+               case PPPIOCSCOMPRESS32:
+               {
+                       struct ppp_option_data32 data32;
+                       if (copy_from_user(&data32, argp, sizeof(data32))) {
+                               err = -EFAULT;
+                       } else {
+                               struct ppp_option_data data = {
+                                       .ptr = compat_ptr(data32.ptr),
+                                       .length = data32.length,
+                                       .transmit = data32.transmit
+                               };
+                               err = ppp_set_compress(ppp, &data);
+                       }
+                       break;
+               }
+               }
+       }
+       mutex_unlock(&ppp_mutex);
+
+       /* all other commands have compatible arguments */
+       if (err == -ENOIOCTLCMD)
+               err = ppp_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+
+       return err;
+}
+#endif
+
 static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
                        struct file *file, unsigned int cmd, unsigned long arg)
 {
@@ -895,6 +979,9 @@ static const struct file_operations ppp_device_fops = {
        .write          = ppp_write,
        .poll           = ppp_poll,
        .unlocked_ioctl = ppp_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = ppp_compat_ioctl,
+#endif
        .open           = ppp_open,
        .release        = ppp_release,
        .llseek         = noop_llseek,
@@ -2734,24 +2821,20 @@ ppp_output_wakeup(struct ppp_channel *chan)
 
 /* Process the PPPIOCSCOMPRESS ioctl. */
 static int
-ppp_set_compress(struct ppp *ppp, unsigned long arg)
+ppp_set_compress(struct ppp *ppp, struct ppp_option_data *data)
 {
-       int err;
+       int err = -EFAULT;
        struct compressor *cp, *ocomp;
-       struct ppp_option_data data;
        void *state, *ostate;
        unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];
 
-       err = -EFAULT;
-       if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
-               goto out;
-       if (data.length > CCP_MAX_OPTION_LENGTH)
+       if (data->length > CCP_MAX_OPTION_LENGTH)
                goto out;
-       if (copy_from_user(ccp_option, (void __user *) data.ptr, data.length))
+       if (copy_from_user(ccp_option, data->ptr, data->length))
                goto out;
 
        err = -EINVAL;
-       if (data.length < 2 || ccp_option[1] < 2 || ccp_option[1] > data.length)
+       if (data->length < 2 || ccp_option[1] < 2 || ccp_option[1] > data->length)
                goto out;
 
        cp = try_then_request_module(
@@ -2761,8 +2844,8 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg)
                goto out;
 
        err = -ENOBUFS;
-       if (data.transmit) {
-               state = cp->comp_alloc(ccp_option, data.length);
+       if (data->transmit) {
+               state = cp->comp_alloc(ccp_option, data->length);
                if (state) {
                        ppp_xmit_lock(ppp);
                        ppp->xstate &= ~SC_COMP_RUN;
@@ -2780,7 +2863,7 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg)
                        module_put(cp->owner);
 
        } else {
-               state = cp->decomp_alloc(ccp_option, data.length);
+               state = cp->decomp_alloc(ccp_option, data->length);
                if (state) {
                        ppp_recv_lock(ppp);
                        ppp->rstate &= ~SC_DECOMP_RUN;