[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <23b24a2a-f23c-725e-69f5-06f1eacef379@virtuozzo.com>
Date: Tue, 13 Feb 2018 14:03:41 +0300
From: Kirill Tkhai <ktkhai@...tuozzo.com>
To: Ying Xue <ying.xue@...driver.com>, davem@...emloft.net,
jon.maloy@...csson.com
Cc: netdev@...r.kernel.org, syzkaller-bugs@...glegroups.com,
tipc-discussion@...ts.sourceforge.net
Subject: Re: [PATCH net v3] tipc: fix missing RTNL lock protection during
setting link properties
Hi, Ying,
On 13.02.2018 12:04, Ying Xue wrote:
> Currently when user changes link properties, TIPC first checks if
> user's command message contains media name or bearer name through
> tipc_media_find() or tipc_bearer_find() which is protected by RTNL
> lock. But when tipc_nl_compat_link_set() conducts the checking with
> the two functions, it doesn't hold RTNL lock at all, as a result,
> the following complaints were reported:
>
> audit: type=1400 audit(1514679888.244:9): avc: denied { write } for
> pid=3194 comm="syzkaller021477" path="socket:[11143]" dev="sockfs"
> ino=11143 scontext=unconfined_u:system_r:insmod_t:s0-s0:c0.c1023
> tcontext=unconfined_u:system_r:insmod_t:s0-s0:c0.c1023
> tclass=netlink_generic_socket permissive=1
> =============================
> WARNING: suspicious RCU usage
> 4.15.0-rc5+ #152 Not tainted
> -----------------------------
> net/tipc/bearer.c:177 suspicious rcu_dereference_protected() usage!
>
> other info that might help us debug this:
>
> rcu_scheduler_active = 2, debug_locks = 1
> 2 locks held by syzkaller021477/3194:
> #0: (cb_lock){++++}, at: [<00000000d20133ea>] genl_rcv+0x19/0x40
> net/netlink/genetlink.c:634
> #1: (genl_mutex){+.+.}, at: [<00000000fcc5d1bc>] genl_lock
> net/netlink/genetlink.c:33 [inline]
> #1: (genl_mutex){+.+.}, at: [<00000000fcc5d1bc>] genl_rcv_msg+0x115/0x140
> net/netlink/genetlink.c:622
>
> stack backtrace:
> CPU: 1 PID: 3194 Comm: syzkaller021477 Not tainted 4.15.0-rc5+ #152
> Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS
> Google 01/01/2011
> Call Trace:
> __dump_stack lib/dump_stack.c:17 [inline]
> dump_stack+0x194/0x257 lib/dump_stack.c:53
> lockdep_rcu_suspicious+0x123/0x170 kernel/locking/lockdep.c:4585
> tipc_bearer_find+0x2b4/0x3b0 net/tipc/bearer.c:177
> tipc_nl_compat_link_set+0x329/0x9f0 net/tipc/netlink_compat.c:729
> __tipc_nl_compat_doit net/tipc/netlink_compat.c:288 [inline]
> tipc_nl_compat_doit+0x15b/0x660 net/tipc/netlink_compat.c:335
> tipc_nl_compat_handle net/tipc/netlink_compat.c:1119 [inline]
> tipc_nl_compat_recv+0x112f/0x18f0 net/tipc/netlink_compat.c:1201
> genl_family_rcv_msg+0x7b7/0xfb0 net/netlink/genetlink.c:599
> genl_rcv_msg+0xb2/0x140 net/netlink/genetlink.c:624
> netlink_rcv_skb+0x21e/0x460 net/netlink/af_netlink.c:2408
> genl_rcv+0x28/0x40 net/netlink/genetlink.c:635
> netlink_unicast_kernel net/netlink/af_netlink.c:1275 [inline]
> netlink_unicast+0x4e8/0x6f0 net/netlink/af_netlink.c:1301
> netlink_sendmsg+0xa4a/0xe60 net/netlink/af_netlink.c:1864
> sock_sendmsg_nosec net/socket.c:636 [inline]
> sock_sendmsg+0xca/0x110 net/socket.c:646
> sock_write_iter+0x31a/0x5d0 net/socket.c:915
> call_write_iter include/linux/fs.h:1772 [inline]
> new_sync_write fs/read_write.c:469 [inline]
> __vfs_write+0x684/0x970 fs/read_write.c:482
> vfs_write+0x189/0x510 fs/read_write.c:544
> SYSC_write fs/read_write.c:589 [inline]
> SyS_write+0xef/0x220 fs/read_write.c:581
> do_syscall_32_irqs_on arch/x86/entry/common.c:327 [inline]
> do_fast_syscall_32+0x3ee/0xf9d arch/x86/entry/common.c:389
> entry_SYSENTER_compat+0x54/0x63 arch/x86/entry/entry_64_compat.S:129
>
> In order to correct the mistake, __tipc_nl_compat_doit() has been
> protected by RTNL lock, which means the whole operation of setting
> bearer/media properties is under RTNL protection.
>
> Signed-off-by: Ying Xue <ying.xue@...driver.com>
> Reported-by: syzbot <syzbot+6345fd433db009b29413@...kaller.appspotmail.com>
> ---
> v3:
> - Optimized return method of __tipc_nl_bearer_enable() regarding
> the comments from David M and Kirill Tkhai
> - Moved the allocations of memory in __tipc_nl_compat_doit() out
> of RTNL lock to minimize the time of holding RTNL lock according
> to the suggestion of Kirill Tkhai.
> v2:
> - The whole operation of setting bearer/media properties has been
> protected under RTNL, as per feedback from David M.
>
> net/tipc/bearer.c | 82 +++++++++++++++++++++++++++++------------------
> net/tipc/bearer.h | 4 +++
> net/tipc/net.c | 15 +++++++--
> net/tipc/net.h | 1 +
> net/tipc/netlink_compat.c | 43 +++++++++++++------------
> 5 files changed, 91 insertions(+), 54 deletions(-)
>
> diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
> index c800147..3e3dce3 100644
> --- a/net/tipc/bearer.c
> +++ b/net/tipc/bearer.c
> @@ -813,7 +813,7 @@ int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info)
> return err;
> }
>
> -int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info)
> +int __tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info)
> {
> int err;
> char *name;
> @@ -835,20 +835,27 @@ int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info)
>
> name = nla_data(attrs[TIPC_NLA_BEARER_NAME]);
>
> - rtnl_lock();
> bearer = tipc_bearer_find(net, name);
> - if (!bearer) {
> - rtnl_unlock();
> + if (!bearer)
> return -EINVAL;
> - }
>
> bearer_disable(net, bearer);
> - rtnl_unlock();
>
> return 0;
> }
>
> -int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info)
> +int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info)
> +{
> + int err;
> +
> + rtnl_lock();
> + err = __tipc_nl_bearer_disable(skb, info);
> + rtnl_unlock();
> +
> + return err;
> +}
> +
> +int __tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info)
> {
> int err;
> char *bearer;
> @@ -890,15 +897,18 @@ int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info)
> prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
> }
>
> + return tipc_enable_bearer(net, bearer, domain, prio, attrs);
> +}
> +
> +int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info)
> +{
> + int err;
> +
> rtnl_lock();
> - err = tipc_enable_bearer(net, bearer, domain, prio, attrs);
> - if (err) {
> - rtnl_unlock();
> - return err;
> - }
> + err = __tipc_nl_bearer_enable(skb, info);
> rtnl_unlock();
>
> - return 0;
> + return err;
> }
>
> int tipc_nl_bearer_add(struct sk_buff *skb, struct genl_info *info)
> @@ -944,7 +954,7 @@ int tipc_nl_bearer_add(struct sk_buff *skb, struct genl_info *info)
> return 0;
> }
>
> -int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info)
> +int __tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info)
> {
> int err;
> char *name;
> @@ -965,22 +975,17 @@ int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info)
> return -EINVAL;
> name = nla_data(attrs[TIPC_NLA_BEARER_NAME]);
>
> - rtnl_lock();
> b = tipc_bearer_find(net, name);
> - if (!b) {
> - rtnl_unlock();
> + if (!b)
> return -EINVAL;
> - }
>
> if (attrs[TIPC_NLA_BEARER_PROP]) {
> struct nlattr *props[TIPC_NLA_PROP_MAX + 1];
>
> err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_BEARER_PROP],
> props);
> - if (err) {
> - rtnl_unlock();
> + if (err)
> return err;
> - }
>
> if (props[TIPC_NLA_PROP_TOL])
> b->tolerance = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
> @@ -989,11 +994,21 @@ int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info)
> if (props[TIPC_NLA_PROP_WIN])
> b->window = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
> }
> - rtnl_unlock();
>
> return 0;
> }
>
> +int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info)
> +{
> + int err;
> +
> + rtnl_lock();
> + err = __tipc_nl_bearer_set(skb, info);
> + rtnl_unlock();
> +
> + return err;
> +}
> +
> static int __tipc_nl_add_media(struct tipc_nl_msg *msg,
> struct tipc_media *media, int nlflags)
> {
> @@ -1115,7 +1130,7 @@ int tipc_nl_media_get(struct sk_buff *skb, struct genl_info *info)
> return err;
> }
>
> -int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info)
> +int __tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info)
> {
> int err;
> char *name;
> @@ -1133,22 +1148,17 @@ int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info)
> return -EINVAL;
> name = nla_data(attrs[TIPC_NLA_MEDIA_NAME]);
>
> - rtnl_lock();
> m = tipc_media_find(name);
> - if (!m) {
> - rtnl_unlock();
> + if (!m)
> return -EINVAL;
> - }
>
> if (attrs[TIPC_NLA_MEDIA_PROP]) {
> struct nlattr *props[TIPC_NLA_PROP_MAX + 1];
>
> err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_MEDIA_PROP],
> props);
> - if (err) {
> - rtnl_unlock();
> + if (err)
> return err;
> - }
>
> if (props[TIPC_NLA_PROP_TOL])
> m->tolerance = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
> @@ -1157,7 +1167,17 @@ int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info)
> if (props[TIPC_NLA_PROP_WIN])
> m->window = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
> }
> - rtnl_unlock();
>
> return 0;
> }
> +
> +int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info)
> +{
> + int err;
> +
> + rtnl_lock();
> + err = __tipc_nl_media_set(skb, info);
> + rtnl_unlock();
> +
> + return err;
> +}
> diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h
> index 42d6eee..a53613d 100644
> --- a/net/tipc/bearer.h
> +++ b/net/tipc/bearer.h
> @@ -188,15 +188,19 @@ extern struct tipc_media udp_media_info;
> #endif
>
> int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info);
> +int __tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info);
> int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info);
> +int __tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info);
> int tipc_nl_bearer_dump(struct sk_buff *skb, struct netlink_callback *cb);
> int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info);
> int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info);
> +int __tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info);
> int tipc_nl_bearer_add(struct sk_buff *skb, struct genl_info *info);
>
> int tipc_nl_media_dump(struct sk_buff *skb, struct netlink_callback *cb);
> int tipc_nl_media_get(struct sk_buff *skb, struct genl_info *info);
> int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info);
> +int __tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info);
>
> int tipc_media_set_priority(const char *name, u32 new_value);
> int tipc_media_set_window(const char *name, u32 new_value);
> diff --git a/net/tipc/net.c b/net/tipc/net.c
> index 719c592..1a2fde0 100644
> --- a/net/tipc/net.c
> +++ b/net/tipc/net.c
> @@ -200,7 +200,7 @@ int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb)
> return skb->len;
> }
>
> -int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info)
> +int __tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info)
> {
> struct net *net = sock_net(skb->sk);
> struct tipc_net *tn = net_generic(net, tipc_net_id);
> @@ -241,10 +241,19 @@ int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info)
> if (!tipc_addr_node_valid(addr))
> return -EINVAL;
>
> - rtnl_lock();
> tipc_net_start(net, addr);
> - rtnl_unlock();
> }
>
> return 0;
> }
> +
> +int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info)
> +{
> + int err;
> +
> + rtnl_lock();
> + err = __tipc_nl_net_set(skb, info);
> + rtnl_unlock();
> +
> + return err;
> +}
> diff --git a/net/tipc/net.h b/net/tipc/net.h
> index c7c2549..c0306aa 100644
> --- a/net/tipc/net.h
> +++ b/net/tipc/net.h
> @@ -47,5 +47,6 @@ void tipc_net_stop(struct net *net);
>
> int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb);
> int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info);
> +int __tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info);
>
> #endif
> diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c
> index e48f0b2..4492cda 100644
> --- a/net/tipc/netlink_compat.c
> +++ b/net/tipc/netlink_compat.c
> @@ -285,10 +285,6 @@ static int __tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd,
> if (!trans_buf)
> return -ENOMEM;
>
> - err = (*cmd->transcode)(cmd, trans_buf, msg);
> - if (err)
> - goto trans_out;
> -
> attrbuf = kmalloc((tipc_genl_family.maxattr + 1) *
> sizeof(struct nlattr *), GFP_KERNEL);
> if (!attrbuf) {
> @@ -296,27 +292,34 @@ static int __tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd,
> goto trans_out;
> }
>
> - err = nla_parse(attrbuf, tipc_genl_family.maxattr,
> - (const struct nlattr *)trans_buf->data,
> - trans_buf->len, NULL, NULL);
> - if (err)
> - goto parse_out;
> -
> doit_buf = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
> if (!doit_buf) {
> err = -ENOMEM;
> - goto parse_out;
> + goto attrbuf_out;
> }
>
> - doit_buf->sk = msg->dst_sk;
> -
> memset(&info, 0, sizeof(info));
> info.attrs = attrbuf;
>
> + rtnl_lock();
> + err = (*cmd->transcode)(cmd, trans_buf, msg);
> + if (err)
> + goto doit_out;
> +
> + err = nla_parse(attrbuf, tipc_genl_family.maxattr,
> + (const struct nlattr *)trans_buf->data,
> + trans_buf->len, NULL, NULL);
> + if (err)
> + goto doit_out;
> +
> + doit_buf->sk = msg->dst_sk;
> +
> err = (*cmd->doit)(doit_buf, &info);
> +doit_out:
> + rtnl_unlock();
>
> kfree_skb(doit_buf);
> -parse_out:
> +attrbuf_out:
> kfree(attrbuf);
> trans_out:
> kfree_skb(trans_buf);
> @@ -722,13 +725,13 @@ static int tipc_nl_compat_link_set(struct tipc_nl_compat_cmd_doit *cmd,
>
> media = tipc_media_find(lc->name);
> if (media) {
> - cmd->doit = &tipc_nl_media_set;
> + cmd->doit = &__tipc_nl_media_set;
> return tipc_nl_compat_media_set(skb, msg);
> }
>
> bearer = tipc_bearer_find(msg->net, lc->name);
> if (bearer) {
> - cmd->doit = &tipc_nl_bearer_set;
> + cmd->doit = &__tipc_nl_bearer_set;
> return tipc_nl_compat_bearer_set(skb, msg);
> }
>
> @@ -1089,12 +1092,12 @@ static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg)
> return tipc_nl_compat_dumpit(&dump, msg);
> case TIPC_CMD_ENABLE_BEARER:
> msg->req_type = TIPC_TLV_BEARER_CONFIG;
> - doit.doit = tipc_nl_bearer_enable;
> + doit.doit = __tipc_nl_bearer_enable;
> doit.transcode = tipc_nl_compat_bearer_enable;
> return tipc_nl_compat_doit(&doit, msg);
> case TIPC_CMD_DISABLE_BEARER:
> msg->req_type = TIPC_TLV_BEARER_NAME;
> - doit.doit = tipc_nl_bearer_disable;
> + doit.doit = __tipc_nl_bearer_disable;
> doit.transcode = tipc_nl_compat_bearer_disable;
> return tipc_nl_compat_doit(&doit, msg);
> case TIPC_CMD_SHOW_LINK_STATS:
> @@ -1148,12 +1151,12 @@ static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg)
> return tipc_nl_compat_dumpit(&dump, msg);
> case TIPC_CMD_SET_NODE_ADDR:
> msg->req_type = TIPC_TLV_NET_ADDR;
> - doit.doit = tipc_nl_net_set;
> + doit.doit = __tipc_nl_net_set;
> doit.transcode = tipc_nl_compat_net_set;
> return tipc_nl_compat_doit(&doit, msg);
> case TIPC_CMD_SET_NETID:
> msg->req_type = TIPC_TLV_UNSIGNED;
> - doit.doit = tipc_nl_net_set;
> + doit.doit = __tipc_nl_net_set;
> doit.transcode = tipc_nl_compat_net_set;
> return tipc_nl_compat_doit(&doit, msg);
> case TIPC_CMD_GET_NETID:
The patch is logically OK for me. The only thing I'm confused,
I had to split it in 7 patches to review, otherwise the patch
looks difficult to do. There is possible to extract:
1)Refactoring in __tipc_nl_compat_doit
2)Introduce __tipc_nl_bearer_disable()
3)Introduce __tipc_nl_bearer_enable()
4)Introduce __tipc_nl_bearer_set()
5)Introduce __tipc_nl_media_set()
6)Introduce __tipc_nl_net_set()
7)tipc: fix missing RTNL lock protection during setting link properties
After that it becomes very easy to review the change you made in this patch.
I attached them to message, and you may take the patches if there is one
more version.
Thanks,
Kirill
View attachment "p6" of type "text/plain" (1429 bytes)
View attachment "p5" of type "text/plain" (2206 bytes)
View attachment "p4" of type "text/plain" (2358 bytes)
View attachment "p3" of type "text/plain" (1902 bytes)
View attachment "p2" of type "text/plain" (1786 bytes)
View attachment "p1" of type "text/plain" (1586 bytes)
View attachment "tipc-fix-missing-rtnl-lock" of type "text/plain" (5878 bytes)
View attachment "series" of type "text/plain" (122 bytes)
Powered by blists - more mailing lists