From 85d73fe808b031753bbb7ea74b88c9972d4c11d0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 30 Jan 2020 11:18:34 +0100 Subject: [PATCH] 4.19-stable patches added patches: net_sched-ematch-reject-invalid-tcf_em_simple.patch net_sched-fix-ops-bind_class-implementations.patch --- ...-ematch-reject-invalid-tcf_em_simple.patch | 79 ++++ ...d-fix-ops-bind_class-implementations.patch | 364 ++++++++++++++++++ queue-4.19/series | 2 + 3 files changed, 445 insertions(+) create mode 100644 queue-4.19/net_sched-ematch-reject-invalid-tcf_em_simple.patch create mode 100644 queue-4.19/net_sched-fix-ops-bind_class-implementations.patch diff --git a/queue-4.19/net_sched-ematch-reject-invalid-tcf_em_simple.patch b/queue-4.19/net_sched-ematch-reject-invalid-tcf_em_simple.patch new file mode 100644 index 00000000000..6e6615d5476 --- /dev/null +++ b/queue-4.19/net_sched-ematch-reject-invalid-tcf_em_simple.patch @@ -0,0 +1,79 @@ +From foo@baz Thu 30 Jan 2020 11:17:10 AM CET +From: Eric Dumazet +Date: Fri, 24 Jan 2020 14:57:20 -0800 +Subject: net_sched: ematch: reject invalid TCF_EM_SIMPLE + +From: Eric Dumazet + +[ Upstream commit 55cd9f67f1e45de8517cdaab985fb8e56c0bc1d8 ] + +It is possible for malicious userspace to set TCF_EM_SIMPLE bit +even for matches that should not have this bit set. + +This can fool two places using tcf_em_is_simple() + +1) tcf_em_tree_destroy() -> memory leak of em->data + if ops->destroy() is NULL + +2) tcf_em_tree_dump() wrongly report/leak 4 low-order bytes + of a kernel pointer. + +BUG: memory leak +unreferenced object 0xffff888121850a40 (size 32): + comm "syz-executor927", pid 7193, jiffies 4294941655 (age 19.840s) + hex dump (first 32 bytes): + 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + backtrace: + [<00000000f67036ea>] kmemleak_alloc_recursive include/linux/kmemleak.h:43 [inline] + [<00000000f67036ea>] slab_post_alloc_hook mm/slab.h:586 [inline] + [<00000000f67036ea>] slab_alloc mm/slab.c:3320 [inline] + [<00000000f67036ea>] __do_kmalloc mm/slab.c:3654 [inline] + [<00000000f67036ea>] __kmalloc_track_caller+0x165/0x300 mm/slab.c:3671 + [<00000000fab0cc8e>] kmemdup+0x27/0x60 mm/util.c:127 + [<00000000d9992e0a>] kmemdup include/linux/string.h:453 [inline] + [<00000000d9992e0a>] em_nbyte_change+0x5b/0x90 net/sched/em_nbyte.c:32 + [<000000007e04f711>] tcf_em_validate net/sched/ematch.c:241 [inline] + [<000000007e04f711>] tcf_em_tree_validate net/sched/ematch.c:359 [inline] + [<000000007e04f711>] tcf_em_tree_validate+0x332/0x46f net/sched/ematch.c:300 + [<000000007a769204>] basic_set_parms net/sched/cls_basic.c:157 [inline] + [<000000007a769204>] basic_change+0x1d7/0x5f0 net/sched/cls_basic.c:219 + [<00000000e57a5997>] tc_new_tfilter+0x566/0xf70 net/sched/cls_api.c:2104 + [<0000000074b68559>] rtnetlink_rcv_msg+0x3b2/0x4b0 net/core/rtnetlink.c:5415 + [<00000000b7fe53fb>] netlink_rcv_skb+0x61/0x170 net/netlink/af_netlink.c:2477 + [<00000000e83a40d0>] rtnetlink_rcv+0x1d/0x30 net/core/rtnetlink.c:5442 + [<00000000d62ba933>] netlink_unicast_kernel net/netlink/af_netlink.c:1302 [inline] + [<00000000d62ba933>] netlink_unicast+0x223/0x310 net/netlink/af_netlink.c:1328 + [<0000000088070f72>] netlink_sendmsg+0x2c0/0x570 net/netlink/af_netlink.c:1917 + [<00000000f70b15ea>] sock_sendmsg_nosec net/socket.c:639 [inline] + [<00000000f70b15ea>] sock_sendmsg+0x54/0x70 net/socket.c:659 + [<00000000ef95a9be>] ____sys_sendmsg+0x2d0/0x300 net/socket.c:2330 + [<00000000b650f1ab>] ___sys_sendmsg+0x8a/0xd0 net/socket.c:2384 + [<0000000055bfa74a>] __sys_sendmsg+0x80/0xf0 net/socket.c:2417 + [<000000002abac183>] __do_sys_sendmsg net/socket.c:2426 [inline] + [<000000002abac183>] __se_sys_sendmsg net/socket.c:2424 [inline] + [<000000002abac183>] __x64_sys_sendmsg+0x23/0x30 net/socket.c:2424 + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Signed-off-by: Eric Dumazet +Reported-by: syzbot+03c4738ed29d5d366ddf@syzkaller.appspotmail.com +Cc: Cong Wang +Acked-by: Cong Wang +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman +--- + net/sched/ematch.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/net/sched/ematch.c ++++ b/net/sched/ematch.c +@@ -242,6 +242,9 @@ static int tcf_em_validate(struct tcf_pr + goto errout; + + if (em->ops->change) { ++ err = -EINVAL; ++ if (em_hdr->flags & TCF_EM_SIMPLE) ++ goto errout; + err = em->ops->change(net, data, data_len, em); + if (err < 0) + goto errout; diff --git a/queue-4.19/net_sched-fix-ops-bind_class-implementations.patch b/queue-4.19/net_sched-fix-ops-bind_class-implementations.patch new file mode 100644 index 00000000000..b506f36ac4d --- /dev/null +++ b/queue-4.19/net_sched-fix-ops-bind_class-implementations.patch @@ -0,0 +1,364 @@ +From foo@baz Thu 30 Jan 2020 11:17:10 AM CET +From: Cong Wang +Date: Thu, 23 Jan 2020 16:26:18 -0800 +Subject: net_sched: fix ops->bind_class() implementations + +From: Cong Wang + +[ Upstream commit 2e24cd755552350b94a7617617c6877b8cbcb701 ] + +The current implementations of ops->bind_class() are merely +searching for classid and updating class in the struct tcf_result, +without invoking either of cl_ops->bind_tcf() or +cl_ops->unbind_tcf(). This breaks the design of them as qdisc's +like cbq use them to count filters too. This is why syzbot triggered +the warning in cbq_destroy_class(). + +In order to fix this, we have to call cl_ops->bind_tcf() and +cl_ops->unbind_tcf() like the filter binding path. This patch does +so by refactoring out two helper functions __tcf_bind_filter() +and __tcf_unbind_filter(), which are lockless and accept a Qdisc +pointer, then teaching each implementation to call them correctly. + +Note, we merely pass the Qdisc pointer as an opaque pointer to +each filter, they only need to pass it down to the helper +functions without understanding it at all. + +Fixes: 07d79fc7d94e ("net_sched: add reverse binding for tc class") +Reported-and-tested-by: syzbot+0a0596220218fcb603a8@syzkaller.appspotmail.com +Reported-and-tested-by: syzbot+63bdb6006961d8c917c6@syzkaller.appspotmail.com +Cc: Jamal Hadi Salim +Cc: Jiri Pirko +Signed-off-by: Cong Wang +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman +--- + include/net/pkt_cls.h | 33 +++++++++++++++++++-------------- + include/net/sch_generic.h | 3 ++- + net/sched/cls_basic.c | 11 ++++++++--- + net/sched/cls_bpf.c | 11 ++++++++--- + net/sched/cls_flower.c | 11 ++++++++--- + net/sched/cls_fw.c | 11 ++++++++--- + net/sched/cls_matchall.c | 11 ++++++++--- + net/sched/cls_route.c | 11 ++++++++--- + net/sched/cls_rsvp.h | 11 ++++++++--- + net/sched/cls_tcindex.c | 11 ++++++++--- + net/sched/cls_u32.c | 11 ++++++++--- + net/sched/sch_api.c | 6 ++++-- + 12 files changed, 97 insertions(+), 44 deletions(-) + +--- a/include/net/pkt_cls.h ++++ b/include/net/pkt_cls.h +@@ -206,31 +206,38 @@ __cls_set_class(unsigned long *clp, unsi + return xchg(clp, cl); + } + +-static inline unsigned long +-cls_set_class(struct Qdisc *q, unsigned long *clp, unsigned long cl) ++static inline void ++__tcf_bind_filter(struct Qdisc *q, struct tcf_result *r, unsigned long base) + { +- unsigned long old_cl; ++ unsigned long cl; + +- sch_tree_lock(q); +- old_cl = __cls_set_class(clp, cl); +- sch_tree_unlock(q); +- return old_cl; ++ cl = q->ops->cl_ops->bind_tcf(q, base, r->classid); ++ cl = __cls_set_class(&r->class, cl); ++ if (cl) ++ q->ops->cl_ops->unbind_tcf(q, cl); + } + + static inline void + tcf_bind_filter(struct tcf_proto *tp, struct tcf_result *r, unsigned long base) + { + struct Qdisc *q = tp->chain->block->q; +- unsigned long cl; + + /* Check q as it is not set for shared blocks. In that case, + * setting class is not supported. + */ + if (!q) + return; +- cl = q->ops->cl_ops->bind_tcf(q, base, r->classid); +- cl = cls_set_class(q, &r->class, cl); +- if (cl) ++ sch_tree_lock(q); ++ __tcf_bind_filter(q, r, base); ++ sch_tree_unlock(q); ++} ++ ++static inline void ++__tcf_unbind_filter(struct Qdisc *q, struct tcf_result *r) ++{ ++ unsigned long cl; ++ ++ if ((cl = __cls_set_class(&r->class, 0)) != 0) + q->ops->cl_ops->unbind_tcf(q, cl); + } + +@@ -238,12 +245,10 @@ static inline void + tcf_unbind_filter(struct tcf_proto *tp, struct tcf_result *r) + { + struct Qdisc *q = tp->chain->block->q; +- unsigned long cl; + + if (!q) + return; +- if ((cl = __cls_set_class(&r->class, 0)) != 0) +- q->ops->cl_ops->unbind_tcf(q, cl); ++ __tcf_unbind_filter(q, r); + } + + struct tcf_exts { +--- a/include/net/sch_generic.h ++++ b/include/net/sch_generic.h +@@ -273,7 +273,8 @@ struct tcf_proto_ops { + int (*reoffload)(struct tcf_proto *tp, bool add, + tc_setup_cb_t *cb, void *cb_priv, + struct netlink_ext_ack *extack); +- void (*bind_class)(void *, u32, unsigned long); ++ void (*bind_class)(void *, u32, unsigned long, ++ void *, unsigned long); + void * (*tmplt_create)(struct net *net, + struct tcf_chain *chain, + struct nlattr **tca, +--- a/net/sched/cls_basic.c ++++ b/net/sched/cls_basic.c +@@ -254,12 +254,17 @@ skip: + } + } + +-static void basic_bind_class(void *fh, u32 classid, unsigned long cl) ++static void basic_bind_class(void *fh, u32 classid, unsigned long cl, void *q, ++ unsigned long base) + { + struct basic_filter *f = fh; + +- if (f && f->res.classid == classid) +- f->res.class = cl; ++ if (f && f->res.classid == classid) { ++ if (cl) ++ __tcf_bind_filter(q, &f->res, base); ++ else ++ __tcf_unbind_filter(q, &f->res); ++ } + } + + static int basic_dump(struct net *net, struct tcf_proto *tp, void *fh, +--- a/net/sched/cls_bpf.c ++++ b/net/sched/cls_bpf.c +@@ -627,12 +627,17 @@ nla_put_failure: + return -1; + } + +-static void cls_bpf_bind_class(void *fh, u32 classid, unsigned long cl) ++static void cls_bpf_bind_class(void *fh, u32 classid, unsigned long cl, ++ void *q, unsigned long base) + { + struct cls_bpf_prog *prog = fh; + +- if (prog && prog->res.classid == classid) +- prog->res.class = cl; ++ if (prog && prog->res.classid == classid) { ++ if (cl) ++ __tcf_bind_filter(q, &prog->res, base); ++ else ++ __tcf_unbind_filter(q, &prog->res); ++ } + } + + static void cls_bpf_walk(struct tcf_proto *tp, struct tcf_walker *arg) +--- a/net/sched/cls_flower.c ++++ b/net/sched/cls_flower.c +@@ -1942,12 +1942,17 @@ nla_put_failure: + return -EMSGSIZE; + } + +-static void fl_bind_class(void *fh, u32 classid, unsigned long cl) ++static void fl_bind_class(void *fh, u32 classid, unsigned long cl, void *q, ++ unsigned long base) + { + struct cls_fl_filter *f = fh; + +- if (f && f->res.classid == classid) +- f->res.class = cl; ++ if (f && f->res.classid == classid) { ++ if (cl) ++ __tcf_bind_filter(q, &f->res, base); ++ else ++ __tcf_unbind_filter(q, &f->res); ++ } + } + + static struct tcf_proto_ops cls_fl_ops __read_mostly = { +--- a/net/sched/cls_fw.c ++++ b/net/sched/cls_fw.c +@@ -432,12 +432,17 @@ nla_put_failure: + return -1; + } + +-static void fw_bind_class(void *fh, u32 classid, unsigned long cl) ++static void fw_bind_class(void *fh, u32 classid, unsigned long cl, void *q, ++ unsigned long base) + { + struct fw_filter *f = fh; + +- if (f && f->res.classid == classid) +- f->res.class = cl; ++ if (f && f->res.classid == classid) { ++ if (cl) ++ __tcf_bind_filter(q, &f->res, base); ++ else ++ __tcf_unbind_filter(q, &f->res); ++ } + } + + static struct tcf_proto_ops cls_fw_ops __read_mostly = { +--- a/net/sched/cls_matchall.c ++++ b/net/sched/cls_matchall.c +@@ -310,12 +310,17 @@ nla_put_failure: + return -1; + } + +-static void mall_bind_class(void *fh, u32 classid, unsigned long cl) ++static void mall_bind_class(void *fh, u32 classid, unsigned long cl, void *q, ++ unsigned long base) + { + struct cls_mall_head *head = fh; + +- if (head && head->res.classid == classid) +- head->res.class = cl; ++ if (head && head->res.classid == classid) { ++ if (cl) ++ __tcf_bind_filter(q, &head->res, base); ++ else ++ __tcf_unbind_filter(q, &head->res); ++ } + } + + static struct tcf_proto_ops cls_mall_ops __read_mostly = { +--- a/net/sched/cls_route.c ++++ b/net/sched/cls_route.c +@@ -645,12 +645,17 @@ nla_put_failure: + return -1; + } + +-static void route4_bind_class(void *fh, u32 classid, unsigned long cl) ++static void route4_bind_class(void *fh, u32 classid, unsigned long cl, void *q, ++ unsigned long base) + { + struct route4_filter *f = fh; + +- if (f && f->res.classid == classid) +- f->res.class = cl; ++ if (f && f->res.classid == classid) { ++ if (cl) ++ __tcf_bind_filter(q, &f->res, base); ++ else ++ __tcf_unbind_filter(q, &f->res); ++ } + } + + static struct tcf_proto_ops cls_route4_ops __read_mostly = { +--- a/net/sched/cls_rsvp.h ++++ b/net/sched/cls_rsvp.h +@@ -736,12 +736,17 @@ nla_put_failure: + return -1; + } + +-static void rsvp_bind_class(void *fh, u32 classid, unsigned long cl) ++static void rsvp_bind_class(void *fh, u32 classid, unsigned long cl, void *q, ++ unsigned long base) + { + struct rsvp_filter *f = fh; + +- if (f && f->res.classid == classid) +- f->res.class = cl; ++ if (f && f->res.classid == classid) { ++ if (cl) ++ __tcf_bind_filter(q, &f->res, base); ++ else ++ __tcf_unbind_filter(q, &f->res); ++ } + } + + static struct tcf_proto_ops RSVP_OPS __read_mostly = { +--- a/net/sched/cls_tcindex.c ++++ b/net/sched/cls_tcindex.c +@@ -652,12 +652,17 @@ nla_put_failure: + return -1; + } + +-static void tcindex_bind_class(void *fh, u32 classid, unsigned long cl) ++static void tcindex_bind_class(void *fh, u32 classid, unsigned long cl, ++ void *q, unsigned long base) + { + struct tcindex_filter_result *r = fh; + +- if (r && r->res.classid == classid) +- r->res.class = cl; ++ if (r && r->res.classid == classid) { ++ if (cl) ++ __tcf_bind_filter(q, &r->res, base); ++ else ++ __tcf_unbind_filter(q, &r->res); ++ } + } + + static struct tcf_proto_ops cls_tcindex_ops __read_mostly = { +--- a/net/sched/cls_u32.c ++++ b/net/sched/cls_u32.c +@@ -1315,12 +1315,17 @@ static int u32_reoffload(struct tcf_prot + return 0; + } + +-static void u32_bind_class(void *fh, u32 classid, unsigned long cl) ++static void u32_bind_class(void *fh, u32 classid, unsigned long cl, void *q, ++ unsigned long base) + { + struct tc_u_knode *n = fh; + +- if (n && n->res.classid == classid) +- n->res.class = cl; ++ if (n && n->res.classid == classid) { ++ if (cl) ++ __tcf_bind_filter(q, &n->res, base); ++ else ++ __tcf_unbind_filter(q, &n->res); ++ } + } + + static int u32_dump(struct net *net, struct tcf_proto *tp, void *fh, +--- a/net/sched/sch_api.c ++++ b/net/sched/sch_api.c +@@ -1803,8 +1803,9 @@ static int tclass_del_notify(struct net + + struct tcf_bind_args { + struct tcf_walker w; +- u32 classid; ++ unsigned long base; + unsigned long cl; ++ u32 classid; + }; + + static int tcf_node_bind(struct tcf_proto *tp, void *n, struct tcf_walker *arg) +@@ -1815,7 +1816,7 @@ static int tcf_node_bind(struct tcf_prot + struct Qdisc *q = tcf_block_q(tp->chain->block); + + sch_tree_lock(q); +- tp->ops->bind_class(n, a->classid, a->cl); ++ tp->ops->bind_class(n, a->classid, a->cl, q, a->base); + sch_tree_unlock(q); + } + return 0; +@@ -1846,6 +1847,7 @@ static void tc_bind_tclass(struct Qdisc + + arg.w.fn = tcf_node_bind; + arg.classid = clid; ++ arg.base = cl; + arg.cl = new_cl; + tp->ops->walk(tp, &arg.w); + } diff --git a/queue-4.19/series b/queue-4.19/series index 6dc3154d4e4..ba737fcaeec 100644 --- a/queue-4.19/series +++ b/queue-4.19/series @@ -20,3 +20,5 @@ ath9k-fix-storage-endpoint-lookup.patch brcmfmac-fix-interface-sanity-check.patch rtl8xxxu-fix-interface-sanity-check.patch zd1211rw-fix-storage-endpoint-lookup.patch +net_sched-ematch-reject-invalid-tcf_em_simple.patch +net_sched-fix-ops-bind_class-implementations.patch -- 2.47.3