Commit 3e859adf authored by Al Viro's avatar Al Viro Committed by Arnd Bergmann

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
[arnd: rebased, and added changelog text]
Cc: netdev@vger.kernel.org
Cc: linux-ppp@vger.kernel.org
Cc: Paul Mackerras <paulus@samba.org>
Cc: "David S. Miller" <davem@davemloft.net>
Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parent b7aff093
...@@ -554,29 +554,58 @@ static __poll_t ppp_poll(struct file *file, poll_table *wait) ...@@ -554,29 +554,58 @@ static __poll_t ppp_poll(struct file *file, poll_table *wait)
} }
#ifdef CONFIG_PPP_FILTER #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_fprog uprog;
struct sock_filter *code = NULL;
int len;
if (copy_from_user(&uprog, arg, sizeof(uprog))) if (copy_from_user(&uprog, p, sizeof(struct sock_fprog)))
return -EFAULT; return ERR_PTR(-EFAULT);
return get_filter(&uprog);
}
if (!uprog.len) { #ifdef CONFIG_COMPAT
*p = NULL; struct sock_fprog32 {
return 0; unsigned short len;
} compat_caddr_t filter;
};
#define PPPIOCSPASS32 _IOW('t', 71, struct sock_fprog32)
#define PPPIOCSACTIVE32 _IOW('t', 70, struct sock_fprog32)
len = uprog.len * sizeof(struct sock_filter); static struct bpf_prog *compat_ppp_get_filter(struct sock_fprog32 __user *p)
code = memdup_user(uprog.filter, len); {
if (IS_ERR(code)) struct sock_fprog32 uprog32;
return PTR_ERR(code); struct sock_fprog uprog;
*p = code; if (copy_from_user(&uprog32, p, sizeof(struct sock_fprog32)))
return uprog.len; 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) 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) ...@@ -753,55 +782,25 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
#ifdef CONFIG_PPP_FILTER #ifdef CONFIG_PPP_FILTER
case PPPIOCSPASS: case PPPIOCSPASS:
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 *pass_filter = NULL;
struct sock_fprog_kern fprog = {
.len = err,
.filter = code,
};
err = 0; if (IS_ERR(filter)) {
if (fprog.filter) err = PTR_ERR(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; break;
} }
case PPPIOCSACTIVE: if (cmd == PPPIOCSPASS)
{ which = &ppp->pass_filter;
struct sock_filter *code; else
which = &ppp->active_filter;
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); ppp_lock(ppp);
if (ppp->active_filter) if (*which)
bpf_prog_destroy(ppp->active_filter); bpf_prog_destroy(*which);
ppp->active_filter = active_filter; *which = filter;
ppp_unlock(ppp); ppp_unlock(ppp);
} err = 0;
kfree(code);
}
break; break;
} }
#endif /* CONFIG_PPP_FILTER */ #endif /* CONFIG_PPP_FILTER */
...@@ -827,6 +826,51 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -827,6 +826,51 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return err; 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, static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
struct file *file, unsigned int cmd, unsigned long arg) struct file *file, unsigned int cmd, unsigned long arg)
{ {
...@@ -895,6 +939,9 @@ static const struct file_operations ppp_device_fops = { ...@@ -895,6 +939,9 @@ static const struct file_operations ppp_device_fops = {
.write = ppp_write, .write = ppp_write,
.poll = ppp_poll, .poll = ppp_poll,
.unlocked_ioctl = ppp_ioctl, .unlocked_ioctl = ppp_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ppp_compat_ioctl,
#endif
.open = ppp_open, .open = ppp_open,
.release = ppp_release, .release = ppp_release,
.llseek = noop_llseek, .llseek = noop_llseek,
......
...@@ -99,40 +99,6 @@ static int sg_grt_trans(struct file *file, ...@@ -99,40 +99,6 @@ static int sg_grt_trans(struct file *file,
} }
#endif /* CONFIG_BLOCK */ #endif /* CONFIG_BLOCK */
struct sock_fprog32 {
unsigned short len;
compat_caddr_t filter;
};
#define PPPIOCSPASS32 _IOW('t', 71, struct sock_fprog32)
#define PPPIOCSACTIVE32 _IOW('t', 70, struct sock_fprog32)
static int ppp_sock_fprog_ioctl_trans(struct file *file,
unsigned int cmd, struct sock_fprog32 __user *u_fprog32)
{
struct sock_fprog __user *u_fprog64 = compat_alloc_user_space(sizeof(struct sock_fprog));
void __user *fptr64;
u32 fptr32;
u16 flen;
if (get_user(flen, &u_fprog32->len) ||
get_user(fptr32, &u_fprog32->filter))
return -EFAULT;
fptr64 = compat_ptr(fptr32);
if (put_user(flen, &u_fprog64->len) ||
put_user(fptr64, &u_fprog64->filter))
return -EFAULT;
if (cmd == PPPIOCSPASS32)
cmd = PPPIOCSPASS;
else
cmd = PPPIOCSACTIVE;
return do_ioctl(file, cmd, (unsigned long) u_fprog64);
}
struct ppp_option_data32 { struct ppp_option_data32 {
compat_caddr_t ptr; compat_caddr_t ptr;
u32 length; u32 length;
...@@ -285,9 +251,6 @@ static long do_ioctl_trans(unsigned int cmd, ...@@ -285,9 +251,6 @@ static long do_ioctl_trans(unsigned int cmd,
return ppp_gidle(file, cmd, argp); return ppp_gidle(file, cmd, argp);
case PPPIOCSCOMPRESS32: case PPPIOCSCOMPRESS32:
return ppp_scompress(file, cmd, argp); return ppp_scompress(file, cmd, argp);
case PPPIOCSPASS32:
case PPPIOCSACTIVE32:
return ppp_sock_fprog_ioctl_trans(file, cmd, argp);
#ifdef CONFIG_BLOCK #ifdef CONFIG_BLOCK
case SG_GET_REQUEST_TABLE: case SG_GET_REQUEST_TABLE:
return sg_grt_trans(file, cmd, argp); return sg_grt_trans(file, cmd, argp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment