Commit 02388bf8 authored by Meng Xu's avatar Meng Xu Committed by David S. Miller

isdn/i4l: fetch the ppp_write buffer in one shot

In isdn_ppp_write(), the header (i.e., protobuf) of the buffer is
fetched twice from userspace. The first fetch is used to peek at the
protocol of the message and reset the huptimer if necessary; while the
second fetch copies in the whole buffer. However, given that buf resides
in userspace memory, a user process can race to change its memory content
across fetches. By doing so, we can either avoid resetting the huptimer
for any type of packets (by first setting proto to PPP_LCP and later
change to the actual type) or force resetting the huptimer for LCP
packets.

This patch changes this double-fetch behavior into two single fetches
decided by condition (lp->isdn_device < 0 || lp->isdn_channel <0).
A more detailed discussion can be found at
https://marc.info/?l=linux-kernel&m=150586376926123&w=2Signed-off-by: default avatarMeng Xu <mengxu.gatech@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e24ee278
...@@ -825,7 +825,6 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) ...@@ -825,7 +825,6 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count)
isdn_net_local *lp; isdn_net_local *lp;
struct ippp_struct *is; struct ippp_struct *is;
int proto; int proto;
unsigned char protobuf[4];
is = file->private_data; is = file->private_data;
...@@ -839,24 +838,28 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) ...@@ -839,24 +838,28 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count)
if (!lp) if (!lp)
printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n"); printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
else { else {
/* if (lp->isdn_device < 0 || lp->isdn_channel < 0) {
* Don't reset huptimer for unsigned char protobuf[4];
* LCP packets. (Echo requests). /*
*/ * Don't reset huptimer for
if (copy_from_user(protobuf, buf, 4)) * LCP packets. (Echo requests).
return -EFAULT; */
proto = PPP_PROTOCOL(protobuf); if (copy_from_user(protobuf, buf, 4))
if (proto != PPP_LCP) return -EFAULT;
lp->huptimer = 0;
proto = PPP_PROTOCOL(protobuf);
if (proto != PPP_LCP)
lp->huptimer = 0;
if (lp->isdn_device < 0 || lp->isdn_channel < 0)
return 0; return 0;
}
if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) && if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) &&
lp->dialstate == 0 && lp->dialstate == 0 &&
(lp->flags & ISDN_NET_CONNECTED)) { (lp->flags & ISDN_NET_CONNECTED)) {
unsigned short hl; unsigned short hl;
struct sk_buff *skb; struct sk_buff *skb;
unsigned char *cpy_buf;
/* /*
* we need to reserve enough space in front of * we need to reserve enough space in front of
* sk_buff. old call to dev_alloc_skb only reserved * sk_buff. old call to dev_alloc_skb only reserved
...@@ -869,11 +872,21 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) ...@@ -869,11 +872,21 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count)
return count; return count;
} }
skb_reserve(skb, hl); skb_reserve(skb, hl);
if (copy_from_user(skb_put(skb, count), buf, count)) cpy_buf = skb_put(skb, count);
if (copy_from_user(cpy_buf, buf, count))
{ {
kfree_skb(skb); kfree_skb(skb);
return -EFAULT; return -EFAULT;
} }
/*
* Don't reset huptimer for
* LCP packets. (Echo requests).
*/
proto = PPP_PROTOCOL(cpy_buf);
if (proto != PPP_LCP)
lp->huptimer = 0;
if (is->debug & 0x40) { if (is->debug & 0x40) {
printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
isdn_ppp_frame_log("xmit", skb->data, skb->len, 32, is->unit, lp->ppp_slot); isdn_ppp_frame_log("xmit", skb->data, skb->len, 32, is->unit, lp->ppp_slot);
......
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