From 3e859adf3643c2da9765cd5088170738d7918567 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 17 Apr 2019 23:48:01 -0400 Subject: compat_ioctl: unify copy-in of ppp filters Now that isdn4linux is gone, the is only one implementation of PPPIOCSPASS and PPPIOCSACTIVE in ppp_generic.c, so this is where the compat_ioctl support should be implemented. The two commands are implemented in very similar ways, so introduce new helpers to allow sharing between the two and between native and compat mode. Signed-off-by: Al Viro [arnd: rebased, and added changelog text] Cc: netdev@vger.kernel.org Cc: linux-ppp@vger.kernel.org Cc: Paul Mackerras Cc: "David S. Miller" Signed-off-by: Arnd Bergmann --- drivers/net/ppp/ppp_generic.c | 169 +++++++++++++++++++++++++++--------------- 1 file changed, 108 insertions(+), 61 deletions(-) (limited to 'drivers/net/ppp') diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 9a1b006904a7..7f8430e6b137 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -554,29 +554,58 @@ 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) - *p = code; - return uprog.len; +static struct bpf_prog *compat_ppp_get_filter(struct sock_fprog32 __user *p) +{ + struct sock_fprog32 uprog32; + struct sock_fprog uprog; + + 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) { @@ -753,55 +782,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; + struct bpf_prog *filter = ppp_get_filter(argp); + struct bpf_prog **which; - err = get_filter(argp, &code); - if (err >= 0) { - struct bpf_prog *active_filter = NULL; - struct sock_fprog_kern fprog = { - .len = err, - .filter = code, - }; - - 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 +826,51 @@ out: return err; } +#ifdef CONFIG_COMPAT +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 */ + } + } + mutex_unlock(&ppp_mutex); + + 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 +939,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, -- cgit v1.2.3 From 5b6c02df50fb28d23c733a24df5a06d0a3f28b93 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 18 Apr 2019 00:31:54 -0400 Subject: compat_ioctl: move PPPIOCSCOMPRESS to ppp_generic Rather than using a compat_alloc_user_space() buffer, moving this next to the native handler allows sharing most of the code, leaving only the user copy portion distinct. Signed-off-by: Al Viro Cc: netdev@vger.kernel.org Cc: linux-ppp@vger.kernel.org Cc: Paul Mackerras Cc: "David S. Miller" Signed-off-by: Arnd Bergmann --- drivers/net/ppp/ppp_generic.c | 53 +++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 15 deletions(-) (limited to 'drivers/net/ppp') diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 7f8430e6b137..fb8e0ac099b8 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -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); @@ -708,9 +708,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; @@ -827,6 +832,13 @@ out: } #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; @@ -863,6 +875,21 @@ static long ppp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long 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); @@ -2783,24 +2810,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( @@ -2810,8 +2833,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; @@ -2829,7 +2852,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; -- cgit v1.2.3 From 17c7e7f407085f510a815c0c99b3fd25d5b13110 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 Apr 2019 22:19:44 +0200 Subject: compat_ioctl: handle PPPIOCGIDLE for 64-bit time_t The ppp_idle structure is defined in terms of __kernel_time_t, which is defined as 'long' on all architectures, and this usage is not affected by the y2038 problem since it transports a time interval rather than an absolute time. However, the ppp user space defines the same structure as time_t, which may be 64-bit wide on new libc versions even on 32-bit architectures. It's easy enough to just handle both possible structure layouts on all architectures, to deal with the possibility that a user space ppp implementation comes with its own ppp_idle structure definition, as well as to document the fact that the driver is y2038-safe. Doing this also avoids the need for a special compat mode translation, since 32-bit and 64-bit kernels now support the same interfaces. The old 32-bit structure is also available on native 64-bit architectures now, but this is harmless. Cc: netdev@vger.kernel.org Cc: linux-ppp@vger.kernel.org Cc: Paul Mackerras Cc: "David S. Miller" Signed-off-by: Arnd Bergmann --- drivers/net/ppp/ppp_generic.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/net/ppp') diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index fb8e0ac099b8..ce4dd45c541d 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -612,7 +612,8 @@ 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; @@ -735,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; -- cgit v1.2.3 From 8f5d9f2ce3020be1a7924b4cc4a1f51164f2c0b7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 26 Jul 2019 15:21:39 +0200 Subject: compat_ioctl: ppp: move simple commands into ppp_generic.c All ppp commands that are not already handled in ppp_compat_ioctl() are compatible, so they can now handled by calling the native ppp_ioctl() directly. Without CONFIG_BLOCK, the generic compat_ioctl table is now empty, so add a check to avoid a build failure in the looking function for that configuration. Cc: netdev@vger.kernel.org Cc: linux-ppp@vger.kernel.org Cc: Paul Mackerras Cc: "David S. Miller" Signed-off-by: Arnd Bergmann --- drivers/net/ppp/ppp_generic.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net/ppp') diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index ce4dd45c541d..267fe2c58087 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -903,6 +903,10 @@ static long ppp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long } 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 -- cgit v1.2.3