can: bcm/raw/isotp: use per module netdevice notifier
commit 8d0caedb759683041d9db82069937525999ada53 upstream. syzbot is reporting hung task at register_netdevice_notifier() [1] and unregister_netdevice_notifier() [2], for cleanup_net() might perform time consuming operations while CAN driver's raw/bcm/isotp modules are calling {register,unregister}_netdevice_notifier() on each socket. Change raw/bcm/isotp modules to call register_netdevice_notifier() from module's __init function and call unregister_netdevice_notifier() from module's __exit function, as with gw/j1939 modules are doing. Link: https://syzkaller.appspot.com/bug?id=391b9498827788b3cc6830226d4ff5be87107c30 [1] Link: https://syzkaller.appspot.com/bug?id=1724d278c83ca6e6df100a2e320c10d991cf2bce [2] Link: https://lore.kernel.org/r/54a5f451-05ed-f977-8534-79e7aa2bcc8f@i-love.sakura.ne.jp Cc: linux-stable <stable@vger.kernel.org> Reported-by: syzbot <syzbot+355f8edb2ff45d5f95fa@syzkaller.appspotmail.com> Reported-by: syzbot <syzbot+0f1827363a305f74996f@syzkaller.appspotmail.com> Reviewed-by: Kirill Tkhai <ktkhai@virtuozzo.com> Tested-by: syzbot <syzbot+355f8edb2ff45d5f95fa@syzkaller.appspotmail.com> Tested-by: Oliver Hartkopp <socketcan@hartkopp.net> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
acb755be1f
commit
0cf4b37790
@@ -83,7 +83,7 @@ struct raw_sock {
|
||||
struct sock sk;
|
||||
int bound;
|
||||
int ifindex;
|
||||
struct notifier_block notifier;
|
||||
struct list_head notifier;
|
||||
int loopback;
|
||||
int recv_own_msgs;
|
||||
int fd_frames;
|
||||
@@ -95,6 +95,10 @@ struct raw_sock {
|
||||
struct uniqframe __percpu *uniq;
|
||||
};
|
||||
|
||||
static LIST_HEAD(raw_notifier_list);
|
||||
static DEFINE_SPINLOCK(raw_notifier_lock);
|
||||
static struct raw_sock *raw_busy_notifier;
|
||||
|
||||
/* Return pointer to store the extra msg flags for raw_recvmsg().
|
||||
* We use the space of one unsigned int beyond the 'struct sockaddr_can'
|
||||
* in skb->cb.
|
||||
@@ -263,21 +267,16 @@ static int raw_enable_allfilters(struct net *net, struct net_device *dev,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int raw_notifier(struct notifier_block *nb,
|
||||
unsigned long msg, void *ptr)
|
||||
static void raw_notify(struct raw_sock *ro, unsigned long msg,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
struct raw_sock *ro = container_of(nb, struct raw_sock, notifier);
|
||||
struct sock *sk = &ro->sk;
|
||||
|
||||
if (!net_eq(dev_net(dev), sock_net(sk)))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (dev->type != ARPHRD_CAN)
|
||||
return NOTIFY_DONE;
|
||||
return;
|
||||
|
||||
if (ro->ifindex != dev->ifindex)
|
||||
return NOTIFY_DONE;
|
||||
return;
|
||||
|
||||
switch (msg) {
|
||||
case NETDEV_UNREGISTER:
|
||||
@@ -305,7 +304,28 @@ static int raw_notifier(struct notifier_block *nb,
|
||||
sk->sk_error_report(sk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int raw_notifier(struct notifier_block *nb, unsigned long msg,
|
||||
void *ptr)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
|
||||
if (dev->type != ARPHRD_CAN)
|
||||
return NOTIFY_DONE;
|
||||
if (msg != NETDEV_UNREGISTER && msg != NETDEV_DOWN)
|
||||
return NOTIFY_DONE;
|
||||
if (unlikely(raw_busy_notifier)) /* Check for reentrant bug. */
|
||||
return NOTIFY_DONE;
|
||||
|
||||
spin_lock(&raw_notifier_lock);
|
||||
list_for_each_entry(raw_busy_notifier, &raw_notifier_list, notifier) {
|
||||
spin_unlock(&raw_notifier_lock);
|
||||
raw_notify(raw_busy_notifier, msg, dev);
|
||||
spin_lock(&raw_notifier_lock);
|
||||
}
|
||||
raw_busy_notifier = NULL;
|
||||
spin_unlock(&raw_notifier_lock);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
@@ -334,9 +354,9 @@ static int raw_init(struct sock *sk)
|
||||
return -ENOMEM;
|
||||
|
||||
/* set notifier */
|
||||
ro->notifier.notifier_call = raw_notifier;
|
||||
|
||||
register_netdevice_notifier(&ro->notifier);
|
||||
spin_lock(&raw_notifier_lock);
|
||||
list_add_tail(&ro->notifier, &raw_notifier_list);
|
||||
spin_unlock(&raw_notifier_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -351,7 +371,14 @@ static int raw_release(struct socket *sock)
|
||||
|
||||
ro = raw_sk(sk);
|
||||
|
||||
unregister_netdevice_notifier(&ro->notifier);
|
||||
spin_lock(&raw_notifier_lock);
|
||||
while (raw_busy_notifier == ro) {
|
||||
spin_unlock(&raw_notifier_lock);
|
||||
schedule_timeout_uninterruptible(1);
|
||||
spin_lock(&raw_notifier_lock);
|
||||
}
|
||||
list_del(&ro->notifier);
|
||||
spin_unlock(&raw_notifier_lock);
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
@@ -881,6 +908,10 @@ static const struct can_proto raw_can_proto = {
|
||||
.prot = &raw_proto,
|
||||
};
|
||||
|
||||
static struct notifier_block canraw_notifier = {
|
||||
.notifier_call = raw_notifier
|
||||
};
|
||||
|
||||
static __init int raw_module_init(void)
|
||||
{
|
||||
int err;
|
||||
@@ -890,6 +921,8 @@ static __init int raw_module_init(void)
|
||||
err = can_proto_register(&raw_can_proto);
|
||||
if (err < 0)
|
||||
pr_err("can: registration of raw protocol failed\n");
|
||||
else
|
||||
register_netdevice_notifier(&canraw_notifier);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -897,6 +930,7 @@ static __init int raw_module_init(void)
|
||||
static __exit void raw_module_exit(void)
|
||||
{
|
||||
can_proto_unregister(&raw_can_proto);
|
||||
unregister_netdevice_notifier(&canraw_notifier);
|
||||
}
|
||||
|
||||
module_init(raw_module_init);
|
||||
|
Reference in New Issue
Block a user