]>
Commit | Line | Data |
---|---|---|
014275a0 GKH |
1 | From foo@baz Mon Oct 9 09:32:35 CEST 2017 |
2 | From: Meng Xu <mengxu.gatech@gmail.com> | |
3 | Date: Tue, 19 Sep 2017 21:49:55 -0400 | |
4 | Subject: isdn/i4l: fetch the ppp_write buffer in one shot | |
5 | ||
6 | From: Meng Xu <mengxu.gatech@gmail.com> | |
7 | ||
8 | ||
9 | [ Upstream commit 02388bf87f72e1d47174cd8f81c34443920eb5a0 ] | |
10 | ||
11 | In isdn_ppp_write(), the header (i.e., protobuf) of the buffer is | |
12 | fetched twice from userspace. The first fetch is used to peek at the | |
13 | protocol of the message and reset the huptimer if necessary; while the | |
14 | second fetch copies in the whole buffer. However, given that buf resides | |
15 | in userspace memory, a user process can race to change its memory content | |
16 | across fetches. By doing so, we can either avoid resetting the huptimer | |
17 | for any type of packets (by first setting proto to PPP_LCP and later | |
18 | change to the actual type) or force resetting the huptimer for LCP | |
19 | packets. | |
20 | ||
21 | This patch changes this double-fetch behavior into two single fetches | |
22 | decided by condition (lp->isdn_device < 0 || lp->isdn_channel <0). | |
23 | A more detailed discussion can be found at | |
24 | https://marc.info/?l=linux-kernel&m=150586376926123&w=2 | |
25 | ||
26 | Signed-off-by: Meng Xu <mengxu.gatech@gmail.com> | |
27 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
28 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
29 | --- | |
30 | drivers/isdn/i4l/isdn_ppp.c | 37 +++++++++++++++++++++++++------------ | |
31 | 1 file changed, 25 insertions(+), 12 deletions(-) | |
32 | ||
33 | --- a/drivers/isdn/i4l/isdn_ppp.c | |
34 | +++ b/drivers/isdn/i4l/isdn_ppp.c | |
35 | @@ -825,7 +825,6 @@ isdn_ppp_write(int min, struct file *fil | |
36 | isdn_net_local *lp; | |
37 | struct ippp_struct *is; | |
38 | int proto; | |
39 | - unsigned char protobuf[4]; | |
40 | ||
41 | is = file->private_data; | |
42 | ||
43 | @@ -839,24 +838,28 @@ isdn_ppp_write(int min, struct file *fil | |
44 | if (!lp) | |
45 | printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n"); | |
46 | else { | |
47 | - /* | |
48 | - * Don't reset huptimer for | |
49 | - * LCP packets. (Echo requests). | |
50 | - */ | |
51 | - if (copy_from_user(protobuf, buf, 4)) | |
52 | - return -EFAULT; | |
53 | - proto = PPP_PROTOCOL(protobuf); | |
54 | - if (proto != PPP_LCP) | |
55 | - lp->huptimer = 0; | |
56 | + if (lp->isdn_device < 0 || lp->isdn_channel < 0) { | |
57 | + unsigned char protobuf[4]; | |
58 | + /* | |
59 | + * Don't reset huptimer for | |
60 | + * LCP packets. (Echo requests). | |
61 | + */ | |
62 | + if (copy_from_user(protobuf, buf, 4)) | |
63 | + return -EFAULT; | |
64 | + | |
65 | + proto = PPP_PROTOCOL(protobuf); | |
66 | + if (proto != PPP_LCP) | |
67 | + lp->huptimer = 0; | |
68 | ||
69 | - if (lp->isdn_device < 0 || lp->isdn_channel < 0) | |
70 | return 0; | |
71 | + } | |
72 | ||
73 | if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) && | |
74 | lp->dialstate == 0 && | |
75 | (lp->flags & ISDN_NET_CONNECTED)) { | |
76 | unsigned short hl; | |
77 | struct sk_buff *skb; | |
78 | + unsigned char *cpy_buf; | |
79 | /* | |
80 | * we need to reserve enough space in front of | |
81 | * sk_buff. old call to dev_alloc_skb only reserved | |
82 | @@ -869,11 +872,21 @@ isdn_ppp_write(int min, struct file *fil | |
83 | return count; | |
84 | } | |
85 | skb_reserve(skb, hl); | |
86 | - if (copy_from_user(skb_put(skb, count), buf, count)) | |
87 | + cpy_buf = skb_put(skb, count); | |
88 | + if (copy_from_user(cpy_buf, buf, count)) | |
89 | { | |
90 | kfree_skb(skb); | |
91 | return -EFAULT; | |
92 | } | |
93 | + | |
94 | + /* | |
95 | + * Don't reset huptimer for | |
96 | + * LCP packets. (Echo requests). | |
97 | + */ | |
98 | + proto = PPP_PROTOCOL(cpy_buf); | |
99 | + if (proto != PPP_LCP) | |
100 | + lp->huptimer = 0; | |
101 | + | |
102 | if (is->debug & 0x40) { | |
103 | printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); | |
104 | isdn_ppp_frame_log("xmit", skb->data, skb->len, 32, is->unit, lp->ppp_slot); |