path: root/drivers/net/ppp/ppp_generic.c
diff options
authorGuillaume Nault <g.nault@alphalink.fr>2016-02-26 18:45:34 +0100
committerDavid S. Miller <davem@davemloft.net>2016-03-01 16:15:07 -0500
commitedffc2178d4ba10a61e150b42ef5e7d797714eb3 (patch)
treeffea2b44f6560408081be5f8d26fbce1f10f1eda /drivers/net/ppp/ppp_generic.c
parentbna: fix list corruption (diff)
ppp: lock ppp->flags in ppp_read() and ppp_poll()
ppp_read() and ppp_poll() can be called concurrently with ppp_ioctl(). In this case, ppp_ioctl() might call ppp_ccp_closed(), which may update ppp->flags while ppp_read() or ppp_poll() is reading it. The update done by ppp_ccp_closed() isn't atomic due to the bit mask operation ('ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP)'), so concurrent readers might get transient values. Reading incorrect ppp->flags may disturb the 'ppp->flags & SC_LOOP_TRAFFIC' test in ppp_read() and ppp_poll(), which in turn can lead to improper decision on whether the PPP unit file is ready for reading or not. Since ppp_ccp_closed() is protected by the Rx and Tx locks (with ppp_lock()), taking the Rx lock is enough for ppp_read() and ppp_poll() to guarantee that ppp_ccp_closed() won't update ppp->flags concurrently. The same reasoning applies to ppp->n_channels. The 'n_channels' field can also be written to concurrently by ppp_ioctl() (through ppp_connect_channel() or ppp_disconnect_channel()). These writes aren't atomic (simple increment/decrement), but are protected by both the Rx and Tx locks (like in the ppp->flags case). So holding the Rx lock before reading ppp->n_channels also prevents concurrent writes. Signed-off-by: Guillaume Nault <g.nault@alphalink.fr> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ppp/ppp_generic.c')
1 files changed, 9 insertions, 1 deletions
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index fc8ad001bc94..e8a5936289c5 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -443,9 +443,14 @@ static ssize_t ppp_read(struct file *file, char __user *buf,
* network traffic (demand mode).
struct ppp *ppp = PF_TO_PPP(pf);
+ ppp_recv_lock(ppp);
if (ppp->n_channels == 0 &&
- (ppp->flags & SC_LOOP_TRAFFIC) == 0)
+ (ppp->flags & SC_LOOP_TRAFFIC) == 0) {
+ ppp_recv_unlock(ppp);
+ }
+ ppp_recv_unlock(ppp);
ret = -EAGAIN;
if (file->f_flags & O_NONBLOCK)
@@ -532,9 +537,12 @@ static unsigned int ppp_poll(struct file *file, poll_table *wait)
else if (pf->kind == INTERFACE) {
/* see comment in ppp_read */
struct ppp *ppp = PF_TO_PPP(pf);
+ ppp_recv_lock(ppp);
if (ppp->n_channels == 0 &&
(ppp->flags & SC_LOOP_TRAFFIC) == 0)
+ ppp_recv_unlock(ppp);
return mask;