--- /dev/null
+From foo@baz Thu 30 Jan 2020 11:17:10 AM CET
+From: Eric Dumazet <edumazet@google.com>
+Date: Fri, 24 Jan 2020 14:57:20 -0800
+Subject: net_sched: ematch: reject invalid TCF_EM_SIMPLE
+
+From: Eric Dumazet <edumazet@google.com>
+
+[ 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 <edumazet@google.com>
+Reported-by: syzbot+03c4738ed29d5d366ddf@syzkaller.appspotmail.com
+Cc: Cong Wang <xiyou.wangcong@gmail.com>
+Acked-by: Cong Wang <xiyou.wangcong@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
--- /dev/null
+From foo@baz Thu 30 Jan 2020 11:17:10 AM CET
+From: Cong Wang <xiyou.wangcong@gmail.com>
+Date: Thu, 23 Jan 2020 16:26:18 -0800
+Subject: net_sched: fix ops->bind_class() implementations
+
+From: Cong Wang <xiyou.wangcong@gmail.com>
+
+[ 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 <jhs@mojatatu.com>
+Cc: Jiri Pirko <jiri@resnulli.us>
+Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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);
+ }