PPPoE: miscellaneous smaller cleanups
Subsystems (2)
PATCH 1/4 PPPoE: miscellaneous smaller cleanups
below is a patch that just removes dead code/initializers without any effect (first access is an assignment) that I stumbled accross while reading the source.
--- drivers/net/pppoe.c | 21 ++++++++------------- 1 files changed, 8 insertions(+), 13 deletions(-)
drivers/net/pppoe.c +8 -13
@@ -207,7 +207,7 @@ static inline struct pppox_sock *get_item(unsigned long sid,static inline struct pppox_sock *get_item_by_addr (struct sockaddr_pppox *sp) { -struct net_device *dev =NULL ; +struct net_device *dev;int ifindex; dev =dev_get_by_name (sp->sa_addr.pppoe.dev);
@@ -222,9 +222,6 @@ static inline int set_item(struct pppox_sock *po) {int i; -if (!po) -return -EINVAL ; -write_lock_bh (&pppoe_hash_lock); i =__set_item (po);write_unlock_bh (&pppoe_hash_lock);
@@ -344,7 +341,7 @@ static struct notifier_block pppoe_notifier = {static int pppoe_rcv_core (struct sock *sk,struct sk_buff *skb) {struct pppox_sock *po =pppox_sk (sk); -struct pppox_sock *relay_po =NULL ; +struct pppox_sock *relay_po;if (sk->sk_state &PPPOX_BOUND ) {struct pppoe_hdr *ph = (struct pppoe_hdr *) skb->nh.raw;
@@ -514,7 +511,6 @@ static int pppoe_release(struct socket *sock) {struct sock *sk = sock->sk;struct pppox_sock *po; -int error =0 ;if (!sk)return 0 ;
@@ -543,7 +539,7 @@ static int pppoe_release(struct socket *sock)skb_queue_purge (&sk->sk_receive_queue);sock_put (sk); -return error; +return 0 ; }
@@ -762,10 +758,10 @@ static int pppoe_ioctl(struct socket *sock, unsigned int cmd,static int pppoe_sendmsg (struct kiocb *iocb,struct socket *sock,struct msghdr *m,size_t total_len) { -struct sk_buff *skb =NULL ; +struct sk_buff *skb;struct sock *sk = sock->sk;struct pppox_sock *po =pppox_sk (sk); -int error =0 ; +int error;struct pppoe_hdr hdr;struct pppoe_hdr *ph;struct net_device *dev;
@@ -929,10 +925,10 @@ static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t total_len, int flags) { struct sock *sk = sock->sk; -struct sk_buff *skb =NULL ; +struct sk_buff *skb;int error =0 ;int len; -struct pppoe_hdr *ph =NULL ; +struct pppoe_hdr *ph;if (sk->sk_state &PPPOX_BOUND ) { error = -EIO ;
@@ -949,7 +945,6 @@ static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock, m->msg_namelen =0 ;if (skb) { - error =0 ; ph = (struct pppoe_hdr *) skb->nh.raw; len =ntohs (ph->length);
@@ -991,7 +986,7 @@ out:static __inline__struct pppox_sock *pppoe_get_idx (loff_t pos) { -struct pppox_sock *po =NULL ; +struct pppox_sock *po;int i =0 ;for (; i <PPPOE_HASH_SIZE ; i++) {
Signature · 1 line
1.5.0.g78e90
PATCH 2/4 PPPOE: race between interface going down and connect()
below you find a patch that (hopefully) fixes a race between an interface going down and a connect() to a peer on that interface. Before, connect() would determine that an interface is up, then the interface could go down and all entries referring to that interface in the item_hash_table would be marked as ZOMBIEs and their references to the device would be freed, and after that, connect() would put a new entry into the hash table referring to the device that meanwhile is down already - which also would cause unregister_netdevice() to wait until the socket has been release()d.
This patch does not suffice if we are not allowed to accept connect()s referring to a device that we already acked a NETDEV_GOING_DOWN for (that is: all references are only guaranteed to be freed after NETDEV_DOWN has been acknowledged, not necessarily after the NETDEV_GOING_DOWN already). And if we are allowed to, we could avoid looking through the hash table upon NETDEV_GOING_DOWN completely and only do that once we get the NETDEV_DOWN ...
mostrows:pppoe_flush_dev is called on NETDEV_GOING_DOWN and NETDEV_DOWN to deal with this "late connect" issue. Ideally one would hope to notify users at the "NETDEV_GOING_DOWN" phase (just to pretend to be nice). However, it is the NETDEV_DOWN scan that takes all the responsibility for ensuring nobody is hanging around at that time.
--- drivers/net/pppoe.c | 19 ++++++------------- 1 files changed, 6 insertions(+), 13 deletions(-)
drivers/net/pppoe.c +6 -13
@@ -218,17 +218,6 @@ static inline struct pppox_sock *get_item_by_addr(struct sockaddr_pppox *sp)return get_item (sp->sa_addr.pppoe.sid, sp->sa_addr.pppoe.remote, ifindex); } -static inline int set_item (struct pppox_sock *po) -{ -int i; - -write_lock_bh (&pppoe_hash_lock); - i =__set_item (po); -write_unlock_bh (&pppoe_hash_lock); - -return i; -} -static inline struct pppox_sock *delete_item (unsigned long sid,char *addr,int ifindex) {struct pppox_sock *ret;
@@ -595,14 +584,18 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, po->pppoe_dev = dev; po->pppoe_ifindex = dev->ifindex; -if (!(dev->flags &IFF_UP )) +write_lock_bh (&pppoe_hash_lock); +if (!(dev->flags &IFF_UP )){ +write_unlock_bh (&pppoe_hash_lock); goto err_put; + }memcpy (&po->pppoe_pa, &sp->sa_addr.pppoe,sizeof (struct pppoe_addr )); - error =set_item (po); + error =__set_item (po); +write_unlock_bh (&pppoe_hash_lock);if (error <0 ) goto err_put;
Signature · 1 line
1.5.0.g78e90
PATCH 3/4 PPPOE: memory leak when socket is release()d before PPPIOCGCHAN has been called on it
below you find a patch that fixes a memory leak when a PPPoE socket is release()d after it has been connect()ed, but before the PPPIOCGCHAN ioctl ever has been called on it.
This is somewhat of a security problem, too, since PPPoE sockets can be created by any user, so any user can easily allocate all the machine's RAM to non-swappable address space and thus DoS the system.
Is there any specific reason for PPPoE sockets being available to any unprivileged process, BTW? After all, you need a packet socket for the discovery stage anyway, so it's unlikely that any unprivileged process will ever need to create a PPPoE socket, no? Allocating all session IDs for a known AC is a kind of DoS, too, after all - with Juniper ERXes, this is really easy, actually, since they don't ever assign session ids above 8000 ...
--- drivers/net/pppox.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/net/pppox.c +1 -1
@@ -58,7 +58,7 @@ void pppox_unbind_sock(struct sock *sk) {/* Clear connection to ppp device, if attached. */ -if (sk->sk_state & (PPPOX_BOUND |PPPOX_ZOMBIE )) { +if (sk->sk_state & (PPPOX_BOUND |PPPOX_CONNECTED |PPPOX_ZOMBIE )) {ppp_unregister_channel (&pppox_sk (sk)->chan); sk->sk_state =PPPOX_DEAD ; }
Signature · 1 line
1.5.0.g78e90
PATCH 4/4 PPPOE: Fix device tear-down notification.
pppoe_flush_dev() kicks all sockets bound to a device that is going down. In doing so, locks must be taken in the right order consistently (sock lock, followed by the pppoe_hash_lock). However, the scan process is based on us holding the sock lock. So, when something is found in the scan we must release the lock we're holding and grab the sock lock.
This patch fixes race conditions between this code and pppoe_release(), both of which perform similar functions but would naturally prefer to grab locks in opposing orders. Both code paths are now going after these locks in a consistent manner.
pppoe_hash_lock protects the contents of the "pppox_sock" objects that reside inside the hash. Thus, NULL'ing out the pppoe_dev field should be done under the protection of this lock.
--- drivers/net/pppoe.c | 93 +++++++++++++++++++++++++++++---------------------- 1 files changed, 53 insertions(+), 40 deletions(-)
drivers/net/pppoe.c +53 -40
@@ -241,54 +241,53 @@ static inline struct pppox_sock *delete_item(unsigned long sid, char *addr, intstatic void pppoe_flush_dev (struct net_device *dev) {int hash; -BUG_ON (dev ==NULL ); -read_lock_bh (&pppoe_hash_lock); +write_lock_bh (&pppoe_hash_lock);for (hash =0 ; hash <PPPOE_HASH_SIZE ; hash++) {struct pppox_sock *po = item_hash_table[hash];while (po !=NULL ) { -if (po->pppoe_dev == dev) { -struct sock *sk =sk_pppox (po); - -sock_hold (sk); - po->pppoe_dev =NULL ; - -/* We hold a reference to SK, now drop the -* hash table lock so that we may attempt -* to lock the socket (which can sleep). -*/ -read_unlock_bh (&pppoe_hash_lock); - -lock_sock (sk); - -if (sk->sk_state & - (PPPOX_CONNECTED |PPPOX_BOUND )) { -pppox_unbind_sock (sk); -dev_put (dev); - sk->sk_state =PPPOX_ZOMBIE ; - sk->sk_state_change (sk); - } - -release_sock (sk); +struct sock *sk =sk_pppox (po); +if (po->pppoe_dev != dev) { + po = po->next; +continue ; + } + po->pppoe_dev =NULL ; +dev_put (dev); + + +/* We always grab the socket lock, followed by the +* pppoe_hash_lock, in that order. Since we should +* hold the sock lock while doing any unbinding, +* we need to release the lock we're holding. +* Hold a reference to the sock so it doesn't disappear +* as we're jumping between locks. +*/ -sock_put (sk); +sock_hold (sk); -read_lock_bh (&pppoe_hash_lock); +write_unlock_bh (&pppoe_hash_lock); +lock_sock (sk); -/* Now restart from the beginning of this -* hash chain. We always NULL out pppoe_dev -* so we are guaranteed to make forward -* progress. -*/ - po = item_hash_table[hash]; -continue ; +if (sk->sk_state & (PPPOX_CONNECTED |PPPOX_BOUND )) { +pppox_unbind_sock (sk); + sk->sk_state =PPPOX_ZOMBIE ; + sk->sk_state_change (sk); } - po = po->next; + +release_sock (sk); +sock_put (sk); + +/* Restart scan at the beginning of this hash chain. +* While the lock was dropped the chain contents may +* have changed. +*/ +write_lock_bh (&pppoe_hash_lock); + po = item_hash_table[hash]; } } -read_unlock_bh (&pppoe_hash_lock); +write_unlock_bh (&pppoe_hash_lock); } static int pppoe_device_event(struct notifier_block *this,
@@ -504,28 +503,42 @@ static int pppoe_release(struct socket *sock)if (!sk)return 0 ; -if (sock_flag (sk,SOCK_DEAD )) +lock_sock (sk); +if (sock_flag (sk,SOCK_DEAD )){ +release_sock (sk);return -EBADF ; + }pppox_unbind_sock (sk);/* Signal the death of the socket. */ sk->sk_state =PPPOX_DEAD ; + +/* Write lock on hash lock protects the entire "po" struct from +* concurrent updates via pppoe_flush_dev. The "po" struct should +* be considered part of the hash table contents, thus protected +* by the hash table lock */ +write_lock_bh (&pppoe_hash_lock); + po =pppox_sk (sk);if (po->pppoe_pa.sid) { -delete_item (po->pppoe_pa.sid, po->pppoe_pa.remote, po->pppoe_ifindex); +__delete_item (po->pppoe_pa.sid, + po->pppoe_pa.remote, po->pppoe_ifindex); } -if (po->pppoe_dev) +if (po->pppoe_dev) {dev_put (po->pppoe_dev); + po->pppoe_dev =NULL ; + } - po->pppoe_dev =NULL ; +write_unlock_bh (&pppoe_hash_lock);sock_orphan (sk); sock->sk =NULL ;skb_queue_purge (&sk->sk_receive_queue); +release_sock (sk);sock_put (sk);return 0 ;
Signature · 1 line
1.5.0.g78e90
David Miller
maintainerRe: [PATCH 4/4] PPPOE: Fix device tear-down notification.
From: Michal Ostrowski <[email protected]>
Date: Tue, 13 Mar 2007 09:09:35 -0500
Quoted 11 lines
pppoe_flush_dev()kicks all sockets bound to a device that is going down. In doing so, locks must be taken in the right order consistently (sock lock, followed by thepppoe_hash_lock). However, the scan process is based on us holding the sock lock. So, when something is found in the scan we must release the lock we're holding and grab the sock lock.This patch fixes race conditions between this code and
pppoe_release(), both of which perform similar functions but would naturally prefer to grab locks in opposing orders. Both code paths are now going after these locks in a consistent manner.
Quoted 5 lines
pppoe_hash_lockprotects the contents of the "pppox_sock" objects that reside inside the hash. Thus, NULL'ing out thepppoe_devfield should be done under the protection of this lock.Signed-off-by Michal Ostrowski <[email protected]>
Aha, this one applies cleanly :-)
But I had to edit out some trailing whitespace this patch added, please be mindful of this in the future, thanks!
Adds trailing whitespace.
diff:48:
Adds trailing whitespace.
diff:52: * hold the sock lock while doing any unbinding,
Adds trailing whitespace.
diff:53: * we need to release the lock we're holding.
Adds trailing whitespace.
diff:81:
Adds trailing whitespace.
diff:121: __delete_item(po->pppoe_pa.sid,
warning: 5 lines add trailing whitespaces.
Re: [PATCH 3/4] PPPOE: memory leak when socket is release()d before PPPIOCGCHAN has been called on it
In reply to Michal OstrowskiFrom: Michal Ostrowski <[email protected]>
Date: Tue, 13 Mar 2007 09:09:34 -0500
Quoted 16 lines
below you find a patch that fixes a memory leak when a PPPoE socket is release()d after it has been connect()ed, but before the PPPIOCGCHAN ioctl ever has been called on it.
This is somewhat of a security problem, too, since PPPoE sockets can be created by any user, so any user can easily allocate all the machine's RAM to non-swappable address space and thus DoS the system.
Is there any specific reason for PPPoE sockets being available to any unprivileged process, BTW? After all, you need a packet socket for the discovery stage anyway, so it's unlikely that any unprivileged process will ever need to create a PPPoE socket, no? Allocating all session IDs for a known AC is a kind of DoS, too, after all - with Juniper ERXes, this is really easy, actually, since they don't ever assign session ids above 8000 ...
Quoted 2 lines
Signed-off-by Florian Zumbiehl <[email protected]>Acked-by Michal Ostrowski <[email protected]>
Applied to net-2.6.22
Re: [PATCH 2/4] PPPOE: race between interface going down and connect()
In reply to Michal OstrowskiFrom: Michal Ostrowski <[email protected]>
Date: Tue, 13 Mar 2007 09:09:33 -0500
Quoted 25 lines
below you find a patch that (hopefully) fixes a race between an interface going down and a
connect()to a peer on that interface. Before,connect()would determine that an interface is up, then the interface could go down and all entries referring to that interface in theitem_hash_tablewould be marked as ZOMBIEs and their references to the device would be freed, and after that,connect()would put a new entry into the hash table referring to the device that meanwhile is down already - which also would causeunregister_netdevice()to wait until the socket has been release()d.This patch does not suffice if we are not allowed to accept connect()s referring to a device that we already acked a
NETDEV_GOING_DOWNfor (that is: all references are only guaranteed to be freed afterNETDEV_DOWNhas been acknowledged, not necessarily after theNETDEV_GOING_DOWNalready). And if we are allowed to, we could avoid looking through the hash table uponNETDEV_GOING_DOWNcompletely and only do that once we get theNETDEV_DOWN...mostrows:
pppoe_flush_devis called onNETDEV_GOING_DOWNandNETDEV_DOWNto deal with this "late connect" issue. Ideally one would hope to notify users at the "NETDEV_GOING_DOWN" phase (just to pretend to be nice). However, it is theNETDEV_DOWNscan that takes all the responsibility for ensuring nobody is hanging around at that time.
Quoted 2 lines
Signed-off-by Florian Zumbiehl <[email protected]>Acked-by Michal Ostrowski <[email protected]>
Applied to net-2.6.22, thanks!
David Miller
maintainerFrom: Michal Ostrowski <[email protected]>
Date: Tue, 13 Mar 2007 09:09:32 -0500
Quoted 6 lines
below is a patch that just removes dead code/initializers without any effect (first access is an assignment) that I stumbled accross while reading the source.
Signed-off-by Florian Zumbiehl <[email protected]>Acked-by Michal Ostrowski <[email protected]>
I munged this patch into net-2.6.22, thanks!