提交 85af7809 编写于 作者: T Tetsuo Handa 提交者: Yongqiang Liu

can: bcm/raw/isotp: use per module netdevice notifier

stable inclusion
from stable-v4.19.196
commit d42c3ebb315618ca536ef764e3f929ce1d5c3485
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I5O0T8
CVE: NA

--------------------------------

commit 8d0caedb 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: Nsyzbot <syzbot+355f8edb2ff45d5f95fa@syzkaller.appspotmail.com>
Reported-by: Nsyzbot <syzbot+0f1827363a305f74996f@syzkaller.appspotmail.com>
Reviewed-by: NKirill Tkhai <ktkhai@virtuozzo.com>
Tested-by: Nsyzbot <syzbot+355f8edb2ff45d5f95fa@syzkaller.appspotmail.com>
Tested-by: NOliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: NTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: NMarc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: NZiyang Xuan <william.xuanziyang@huawei.com>
Reviewed-by: NYue Haibing <yuehaibing@huawei.com>
Reviewed-by: NWei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: NYongqiang Liu <liuyongqiang13@huawei.com>
上级 28b546c6
...@@ -124,7 +124,7 @@ struct bcm_sock { ...@@ -124,7 +124,7 @@ struct bcm_sock {
struct sock sk; struct sock sk;
int bound; int bound;
int ifindex; int ifindex;
struct notifier_block notifier; struct list_head notifier;
struct list_head rx_ops; struct list_head rx_ops;
struct list_head tx_ops; struct list_head tx_ops;
unsigned long dropped_usr_msgs; unsigned long dropped_usr_msgs;
...@@ -132,6 +132,10 @@ struct bcm_sock { ...@@ -132,6 +132,10 @@ struct bcm_sock {
char procname [32]; /* inode number in decimal with \0 */ char procname [32]; /* inode number in decimal with \0 */
}; };
static LIST_HEAD(bcm_notifier_list);
static DEFINE_SPINLOCK(bcm_notifier_lock);
static struct bcm_sock *bcm_busy_notifier;
static inline struct bcm_sock *bcm_sk(const struct sock *sk) static inline struct bcm_sock *bcm_sk(const struct sock *sk)
{ {
return (struct bcm_sock *)sk; return (struct bcm_sock *)sk;
...@@ -1381,20 +1385,15 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) ...@@ -1381,20 +1385,15 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
/* /*
* notification handler for netdevice status changes * notification handler for netdevice status changes
*/ */
static int bcm_notifier(struct notifier_block *nb, unsigned long msg, static void bcm_notify(struct bcm_sock *bo, unsigned long msg,
void *ptr) struct net_device *dev)
{ {
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct bcm_sock *bo = container_of(nb, struct bcm_sock, notifier);
struct sock *sk = &bo->sk; struct sock *sk = &bo->sk;
struct bcm_op *op; struct bcm_op *op;
int notify_enodev = 0; int notify_enodev = 0;
if (!net_eq(dev_net(dev), sock_net(sk))) if (!net_eq(dev_net(dev), sock_net(sk)))
return NOTIFY_DONE; return;
if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE;
switch (msg) { switch (msg) {
...@@ -1429,7 +1428,28 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg, ...@@ -1429,7 +1428,28 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
sk->sk_error_report(sk); sk->sk_error_report(sk);
} }
} }
}
static int bcm_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(bcm_busy_notifier)) /* Check for reentrant bug. */
return NOTIFY_DONE;
spin_lock(&bcm_notifier_lock);
list_for_each_entry(bcm_busy_notifier, &bcm_notifier_list, notifier) {
spin_unlock(&bcm_notifier_lock);
bcm_notify(bcm_busy_notifier, msg, dev);
spin_lock(&bcm_notifier_lock);
}
bcm_busy_notifier = NULL;
spin_unlock(&bcm_notifier_lock);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
...@@ -1449,9 +1469,9 @@ static int bcm_init(struct sock *sk) ...@@ -1449,9 +1469,9 @@ static int bcm_init(struct sock *sk)
INIT_LIST_HEAD(&bo->rx_ops); INIT_LIST_HEAD(&bo->rx_ops);
/* set notifier */ /* set notifier */
bo->notifier.notifier_call = bcm_notifier; spin_lock(&bcm_notifier_lock);
list_add_tail(&bo->notifier, &bcm_notifier_list);
register_netdevice_notifier(&bo->notifier); spin_unlock(&bcm_notifier_lock);
return 0; return 0;
} }
...@@ -1474,7 +1494,14 @@ static int bcm_release(struct socket *sock) ...@@ -1474,7 +1494,14 @@ static int bcm_release(struct socket *sock)
/* remove bcm_ops, timer, rx_unregister(), etc. */ /* remove bcm_ops, timer, rx_unregister(), etc. */
unregister_netdevice_notifier(&bo->notifier); spin_lock(&bcm_notifier_lock);
while (bcm_busy_notifier == bo) {
spin_unlock(&bcm_notifier_lock);
schedule_timeout_uninterruptible(1);
spin_lock(&bcm_notifier_lock);
}
list_del(&bo->notifier);
spin_unlock(&bcm_notifier_lock);
lock_sock(sk); lock_sock(sk);
...@@ -1693,6 +1720,10 @@ static struct pernet_operations canbcm_pernet_ops __read_mostly = { ...@@ -1693,6 +1720,10 @@ static struct pernet_operations canbcm_pernet_ops __read_mostly = {
.exit = canbcm_pernet_exit, .exit = canbcm_pernet_exit,
}; };
static struct notifier_block canbcm_notifier = {
.notifier_call = bcm_notifier
};
static int __init bcm_module_init(void) static int __init bcm_module_init(void)
{ {
int err; int err;
...@@ -1706,12 +1737,14 @@ static int __init bcm_module_init(void) ...@@ -1706,12 +1737,14 @@ static int __init bcm_module_init(void)
} }
register_pernet_subsys(&canbcm_pernet_ops); register_pernet_subsys(&canbcm_pernet_ops);
register_netdevice_notifier(&canbcm_notifier);
return 0; return 0;
} }
static void __exit bcm_module_exit(void) static void __exit bcm_module_exit(void)
{ {
can_proto_unregister(&bcm_can_proto); can_proto_unregister(&bcm_can_proto);
unregister_netdevice_notifier(&canbcm_notifier);
unregister_pernet_subsys(&canbcm_pernet_ops); unregister_pernet_subsys(&canbcm_pernet_ops);
} }
......
...@@ -84,7 +84,7 @@ struct raw_sock { ...@@ -84,7 +84,7 @@ struct raw_sock {
struct sock sk; struct sock sk;
int bound; int bound;
int ifindex; int ifindex;
struct notifier_block notifier; struct list_head notifier;
int loopback; int loopback;
int recv_own_msgs; int recv_own_msgs;
int fd_frames; int fd_frames;
...@@ -96,6 +96,10 @@ struct raw_sock { ...@@ -96,6 +96,10 @@ struct raw_sock {
struct uniqframe __percpu *uniq; 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(). * Return pointer to store the extra msg flags for raw_recvmsg().
* We use the space of one unsigned int beyond the 'struct sockaddr_can' * We use the space of one unsigned int beyond the 'struct sockaddr_can'
...@@ -266,21 +270,16 @@ static int raw_enable_allfilters(struct net *net, struct net_device *dev, ...@@ -266,21 +270,16 @@ static int raw_enable_allfilters(struct net *net, struct net_device *dev,
return err; return err;
} }
static int raw_notifier(struct notifier_block *nb, static void raw_notify(struct raw_sock *ro, unsigned long msg,
unsigned long msg, void *ptr) 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; struct sock *sk = &ro->sk;
if (!net_eq(dev_net(dev), sock_net(sk))) if (!net_eq(dev_net(dev), sock_net(sk)))
return NOTIFY_DONE; return;
if (dev->type != ARPHRD_CAN)
return NOTIFY_DONE;
if (ro->ifindex != dev->ifindex) if (ro->ifindex != dev->ifindex)
return NOTIFY_DONE; return;
switch (msg) { switch (msg) {
...@@ -309,7 +308,28 @@ static int raw_notifier(struct notifier_block *nb, ...@@ -309,7 +308,28 @@ static int raw_notifier(struct notifier_block *nb,
sk->sk_error_report(sk); sk->sk_error_report(sk);
break; 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; return NOTIFY_DONE;
} }
...@@ -338,9 +358,9 @@ static int raw_init(struct sock *sk) ...@@ -338,9 +358,9 @@ static int raw_init(struct sock *sk)
return -ENOMEM; return -ENOMEM;
/* set notifier */ /* set notifier */
ro->notifier.notifier_call = raw_notifier; spin_lock(&raw_notifier_lock);
list_add_tail(&ro->notifier, &raw_notifier_list);
register_netdevice_notifier(&ro->notifier); spin_unlock(&raw_notifier_lock);
return 0; return 0;
} }
...@@ -355,7 +375,14 @@ static int raw_release(struct socket *sock) ...@@ -355,7 +375,14 @@ static int raw_release(struct socket *sock)
ro = raw_sk(sk); 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); lock_sock(sk);
...@@ -886,6 +913,10 @@ static const struct can_proto raw_can_proto = { ...@@ -886,6 +913,10 @@ static const struct can_proto raw_can_proto = {
.prot = &raw_proto, .prot = &raw_proto,
}; };
static struct notifier_block canraw_notifier = {
.notifier_call = raw_notifier
};
static __init int raw_module_init(void) static __init int raw_module_init(void)
{ {
int err; int err;
...@@ -895,6 +926,8 @@ static __init int raw_module_init(void) ...@@ -895,6 +926,8 @@ static __init int raw_module_init(void)
err = can_proto_register(&raw_can_proto); err = can_proto_register(&raw_can_proto);
if (err < 0) if (err < 0)
printk(KERN_ERR "can: registration of raw protocol failed\n"); printk(KERN_ERR "can: registration of raw protocol failed\n");
else
register_netdevice_notifier(&canraw_notifier);
return err; return err;
} }
...@@ -902,6 +935,7 @@ static __init int raw_module_init(void) ...@@ -902,6 +935,7 @@ static __init int raw_module_init(void)
static __exit void raw_module_exit(void) static __exit void raw_module_exit(void)
{ {
can_proto_unregister(&raw_can_proto); can_proto_unregister(&raw_can_proto);
unregister_netdevice_notifier(&canraw_notifier);
} }
module_init(raw_module_init); module_init(raw_module_init);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册