From: Pablo Neira Ayuso pablo@netfilter.org
commit 8866df9264a34e675b4ee8a151db819b87cce2d3 upstream
Otherwise, we hit a NULL pointer deference since handlers always assume default timeout policy is passed.
netlink: 24 bytes leftover after parsing attributes in process `syz-executor2'. kasan: CONFIG_KASAN_INLINE enabled kasan: GPF could be caused by NULL-ptr deref or user memory access general protection fault: 0000 [#1] PREEMPT SMP KASAN CPU: 0 PID: 9575 Comm: syz-executor1 Not tainted 4.19.0+ #312 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 RIP: 0010:icmp_timeout_obj_to_nlattr+0x77/0x170 net/netfilter/nf_conntrack_proto_icmp.c:297
Fixes: c779e849608a ("netfilter: conntrack: remove get_timeout() indirection") Reported-by: Eric Dumazet eric.dumazet@gmail.com Signed-off-by: Pablo Neira Ayuso pablo@netfilter.org Signed-off-by: Zubin Mithra zsm@chromium.org --- Notes: * Syzkaller reported a general protection fault in icmp_timeout_obj_to_nlattri when fuzzing a 4.19 kernel. Call Trace: cttimeout_default_fill_info net/netfilter/nfnetlink_cttimeout.c:424 [inline] cttimeout_default_get+0x574/0x766 net/netfilter/nfnetlink_cttimeout.c:471 nfnetlink_rcv_msg+0x544/0x5bd net/netfilter/nfnetlink.c:228 netlink_rcv_skb+0x1cf/0x2a1 net/netlink/af_netlink.c:2454 nfnetlink_rcv+0x30f/0x34a net/netfilter/nfnetlink.c:560 netlink_unicast_kernel net/netlink/af_netlink.c:1317 [inline] netlink_unicast+0x3c5/0x4c3 net/netlink/af_netlink.c:1343 netlink_sendmsg+0x829/0x88e net/netlink/af_netlink.c:1908 sock_sendmsg_nosec+0x8c/0xad net/socket.c:621 sock_sendmsg+0x4f/0x60 net/socket.c:631 ___sys_sendmsg+0x4d6/0x63f net/socket.c:2116 __sys_sendmsg+0xee/0x17b net/socket.c:2154 do_syscall_64+0xf8/0x133 arch/x86/entry/common.c:291 entry_SYSCALL_64_after_hwframe+0x49/0xbe
* This patch resolves the following conflicts when applying to 4.19: - l3num is not used as an argument for cttimeout_default_fill_info() in 4.19 - nf_icmp_pernet, nf_tcp_pernet, nf_udp_pernet, nf_dccp_pernet, nf_icmpv6_pernet, nf_sctp_pernet, nf_generic_pernet do not exist in 4.19. Expand the usage of those functions into one-liners.
* Tests run: Chrome OS tryjobs, Syzkaller reproducer
net/netfilter/nfnetlink_cttimeout.c | 46 +++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-)
diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index a30f8ba4b89a..1dc4ea327cbe 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -392,7 +392,8 @@ static int cttimeout_default_set(struct net *net, struct sock *ctnl, static int cttimeout_default_fill_info(struct net *net, struct sk_buff *skb, u32 portid, u32 seq, u32 type, int event, - const struct nf_conntrack_l4proto *l4proto) + const struct nf_conntrack_l4proto *l4proto, + const unsigned int *timeouts) { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; @@ -421,7 +422,7 @@ cttimeout_default_fill_info(struct net *net, struct sk_buff *skb, u32 portid, if (!nest_parms) goto nla_put_failure;
- ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, NULL); + ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, timeouts); if (ret < 0) goto nla_put_failure;
@@ -444,6 +445,7 @@ static int cttimeout_default_get(struct net *net, struct sock *ctnl, struct netlink_ext_ack *extack) { const struct nf_conntrack_l4proto *l4proto; + unsigned int *timeouts = NULL; struct sk_buff *skb2; int ret, err; __u16 l3num; @@ -456,12 +458,44 @@ static int cttimeout_default_get(struct net *net, struct sock *ctnl, l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]); l4proto = nf_ct_l4proto_find_get(l3num, l4num);
- /* This protocol is not supported, skip. */ - if (l4proto->l4proto != l4num) { - err = -EOPNOTSUPP; + err = -EOPNOTSUPP; + if (l4proto->l4proto != l4num) goto err; + + switch (l4proto->l4proto) { + case IPPROTO_ICMP: + timeouts = &net->ct.nf_ct_proto.icmp.timeout; + break; + case IPPROTO_TCP: + timeouts = net->ct.nf_ct_proto.tcp.timeouts; + break; + case IPPROTO_UDP: + timeouts = net->ct.nf_ct_proto.udp.timeouts; + break; + case IPPROTO_DCCP: +#ifdef CONFIG_NF_CT_PROTO_DCCP + timeouts = net->ct.nf_ct_proto.dccp.dccp_timeout; +#endif + break; + case IPPROTO_ICMPV6: + timeouts = &net->ct.nf_ct_proto.icmpv6.timeout; + break; + case IPPROTO_SCTP: +#ifdef CONFIG_NF_CT_PROTO_SCTP + timeouts = net->ct.nf_ct_proto.sctp.timeouts; +#endif + break; + case 255: + timeouts = &net->ct.nf_ct_proto.generic.timeout; + break; + default: + WARN_ON_ONCE(1); + break; }
+ if (!timeouts) + goto err; + skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (skb2 == NULL) { err = -ENOMEM; @@ -472,7 +506,7 @@ static int cttimeout_default_get(struct net *net, struct sock *ctnl, nlh->nlmsg_seq, NFNL_MSG_TYPE(nlh->nlmsg_type), IPCTNL_MSG_TIMEOUT_DEFAULT_SET, - l4proto); + l4proto, timeouts); if (ret <= 0) { kfree_skb(skb2); err = -ENOMEM;
From: Florian Westphal fw@strlen.de
commit 89259088c1b7fecb43e8e245dc931909132a4e03 upstream
syzbot was able to trigger the WARN in cttimeout_default_get() by passing UDPLITE as l4protocol. Alias UDPLITE to UDP, both use same timeout values.
Furthermore, also fetch GRE timeouts. GRE is a bit more complicated, as it still can be a module and its netns_proto_gre struct layout isn't visible outside of the gre module. Can't move timeouts around, it appears conntrack sysctl unregister assumes net_generic() returns nf_proto_net, so we get crash. Expose layout of netns_proto_gre instead.
A followup nf-next patch could make gre tracker be built-in as well if needed, its not that large.
Last, make the WARN() mention the missing protocol value in case anything else is missing.
Reported-by: syzbot+2fae8fa157dd92618cae@syzkaller.appspotmail.com Fixes: 8866df9264a3 ("netfilter: nfnetlink_cttimeout: pass default timeout policy to obj_to_nlattr") Signed-off-by: Florian Westphal fw@strlen.de Signed-off-by: Pablo Neira Ayuso pablo@netfilter.org Signed-off-by: Zubin Mithra zsm@chromium.org --- include/linux/netfilter/nf_conntrack_proto_gre.h | 13 +++++++++++++ net/netfilter/nf_conntrack_proto_gre.c | 14 ++------------ net/netfilter/nfnetlink_cttimeout.c | 15 +++++++++++++-- 3 files changed, 28 insertions(+), 14 deletions(-)
diff --git a/include/linux/netfilter/nf_conntrack_proto_gre.h b/include/linux/netfilter/nf_conntrack_proto_gre.h index b8d95564bd53..14edb795ab43 100644 --- a/include/linux/netfilter/nf_conntrack_proto_gre.h +++ b/include/linux/netfilter/nf_conntrack_proto_gre.h @@ -21,6 +21,19 @@ struct nf_ct_gre_keymap { struct nf_conntrack_tuple tuple; };
+enum grep_conntrack { + GRE_CT_UNREPLIED, + GRE_CT_REPLIED, + GRE_CT_MAX +}; + +struct netns_proto_gre { + struct nf_proto_net nf; + rwlock_t keymap_lock; + struct list_head keymap_list; + unsigned int gre_timeouts[GRE_CT_MAX]; +}; + /* add new tuple->key_reply pair to keymap */ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, struct nf_conntrack_tuple *t); diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 650eb4fba2c5..841c472aae1c 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -43,24 +43,12 @@ #include <linux/netfilter/nf_conntrack_proto_gre.h> #include <linux/netfilter/nf_conntrack_pptp.h>
-enum grep_conntrack { - GRE_CT_UNREPLIED, - GRE_CT_REPLIED, - GRE_CT_MAX -}; - static const unsigned int gre_timeouts[GRE_CT_MAX] = { [GRE_CT_UNREPLIED] = 30*HZ, [GRE_CT_REPLIED] = 180*HZ, };
static unsigned int proto_gre_net_id __read_mostly; -struct netns_proto_gre { - struct nf_proto_net nf; - rwlock_t keymap_lock; - struct list_head keymap_list; - unsigned int gre_timeouts[GRE_CT_MAX]; -};
static inline struct netns_proto_gre *gre_pernet(struct net *net) { @@ -408,6 +396,8 @@ static int __init nf_ct_proto_gre_init(void) { int ret;
+ BUILD_BUG_ON(offsetof(struct netns_proto_gre, nf) != 0); + ret = register_pernet_subsys(&proto_gre_net_ops); if (ret < 0) goto out_pernet; diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index 1dc4ea327cbe..70a7382b9787 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -469,7 +469,8 @@ static int cttimeout_default_get(struct net *net, struct sock *ctnl, case IPPROTO_TCP: timeouts = net->ct.nf_ct_proto.tcp.timeouts; break; - case IPPROTO_UDP: + case IPPROTO_UDP: /* fallthrough */ + case IPPROTO_UDPLITE: timeouts = net->ct.nf_ct_proto.udp.timeouts; break; case IPPROTO_DCCP: @@ -483,13 +484,23 @@ static int cttimeout_default_get(struct net *net, struct sock *ctnl, case IPPROTO_SCTP: #ifdef CONFIG_NF_CT_PROTO_SCTP timeouts = net->ct.nf_ct_proto.sctp.timeouts; +#endif + break; + case IPPROTO_GRE: +#ifdef CONFIG_NF_CT_PROTO_GRE + if (l4proto->net_id) { + struct netns_proto_gre *net_gre; + + net_gre = net_generic(net, *l4proto->net_id); + timeouts = net_gre->gre_timeouts; + } #endif break; case 255: timeouts = &net->ct.nf_ct_proto.generic.timeout; break; default: - WARN_ON_ONCE(1); + WARN_ONCE(1, "Missing timeouts for proto %d", l4proto->l4proto); break; }
On Fri, Apr 12, 2019 at 10:55:02AM -0700, Zubin Mithra wrote:
From: Pablo Neira Ayuso pablo@netfilter.org
commit 8866df9264a34e675b4ee8a151db819b87cce2d3 upstream
Otherwise, we hit a NULL pointer deference since handlers always assume default timeout policy is passed.
netlink: 24 bytes leftover after parsing attributes in process `syz-executor2'. kasan: CONFIG_KASAN_INLINE enabled kasan: GPF could be caused by NULL-ptr deref or user memory access general protection fault: 0000 [#1] PREEMPT SMP KASAN CPU: 0 PID: 9575 Comm: syz-executor1 Not tainted 4.19.0+ #312 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 RIP: 0010:icmp_timeout_obj_to_nlattr+0x77/0x170 net/netfilter/nf_conntrack_proto_icmp.c:297
Fixes: c779e849608a ("netfilter: conntrack: remove get_timeout() indirection") Reported-by: Eric Dumazet eric.dumazet@gmail.com Signed-off-by: Pablo Neira Ayuso pablo@netfilter.org Signed-off-by: Zubin Mithra zsm@chromium.org
Notes:
- Syzkaller reported a general protection fault in icmp_timeout_obj_to_nlattri
when fuzzing a 4.19 kernel. Call Trace: cttimeout_default_fill_info net/netfilter/nfnetlink_cttimeout.c:424 [inline] cttimeout_default_get+0x574/0x766 net/netfilter/nfnetlink_cttimeout.c:471 nfnetlink_rcv_msg+0x544/0x5bd net/netfilter/nfnetlink.c:228 netlink_rcv_skb+0x1cf/0x2a1 net/netlink/af_netlink.c:2454 nfnetlink_rcv+0x30f/0x34a net/netfilter/nfnetlink.c:560 netlink_unicast_kernel net/netlink/af_netlink.c:1317 [inline] netlink_unicast+0x3c5/0x4c3 net/netlink/af_netlink.c:1343 netlink_sendmsg+0x829/0x88e net/netlink/af_netlink.c:1908 sock_sendmsg_nosec+0x8c/0xad net/socket.c:621 sock_sendmsg+0x4f/0x60 net/socket.c:631 ___sys_sendmsg+0x4d6/0x63f net/socket.c:2116 __sys_sendmsg+0xee/0x17b net/socket.c:2154 do_syscall_64+0xf8/0x133 arch/x86/entry/common.c:291 entry_SYSCALL_64_after_hwframe+0x49/0xbe
- This patch resolves the following conflicts when applying to 4.19:
- l3num is not used as an argument for cttimeout_default_fill_info() in 4.19
- nf_icmp_pernet, nf_tcp_pernet, nf_udp_pernet, nf_dccp_pernet,
nf_icmpv6_pernet, nf_sctp_pernet, nf_generic_pernet do not exist in 4.19. Expand the usage of those functions into one-liners.
- Tests run: Chrome OS tryjobs, Syzkaller reproducer
I've queued these 2 patches up. It's very nice to see syzkaller running on -stable kernels, thanks all!
-- Thanks, Sasha
linux-stable-mirror@lists.linaro.org