Re: [PATCH] net: sched: Fix memory exposure from short TCA_U32_SEL

From: Al Viro
Date: Sun Aug 26 2018 - 18:57:56 EST


On Sun, Aug 26, 2018 at 06:32:37PM +0100, Al Viro wrote:

> As far as I can tell, the solution is
[snip long and painful reasoning]
> pointers, and not in provably opaque fashion. Theoretically, the three tcf_...
> inlines above need another look; fortunately, they don't use ->next at all, not to
> mention not being used anywhere outside of net/sched/*.c
>
> The 80 lines above prove that we only need to grep net/sched/*.c for
> tcf_proto_ops method calls. And only because we don't have (thank $DEITY)
> anything that could deconstruct types - as soon as some bastard grows means
> to say "type of the second argument of the function pointed to by p", this
> kind of analysis, painful as it is, goes out of window. Even as it is,
> do you really like the idea of newbies trying to get through the exercises
> like the one above?

BTW, would there be any problem if we took the definitions of tcf_proto and
tcf_proto_ops to e.g. net/sched/tcf_proto.h (along with the three inlines in
in pkt_cls.h), left forwards in sch_generic.h and added includes of "tcf_proto.h"
where needed in net/sched/*.c?

That would make tcf_proto/tcf_proto_ops opaque outside of net/sched, reducing
the exposure of internals. Something like a diff below (against net/master,
builds clean, ought to result in identical binary):

diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index ef727f71336e..35f8eec3f7c0 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -217,35 +217,6 @@ cls_set_class(struct Qdisc *q, unsigned long *clp, unsigned long cl)
return old_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)
- q->ops->cl_ops->unbind_tcf(q, cl);
-}
-
-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);
-}
-
struct tcf_exts {
#ifdef CONFIG_NET_CLS_ACT
__u32 type; /* for backward compat(TCA_OLD_COMPAT) */
@@ -708,18 +679,6 @@ static inline bool tc_in_hw(u32 flags)
return (flags & TCA_CLS_FLAGS_IN_HW) ? true : false;
}

-static inline void
-tc_cls_common_offload_init(struct tc_cls_common_offload *cls_common,
- const struct tcf_proto *tp, u32 flags,
- struct netlink_ext_ack *extack)
-{
- cls_common->chain_index = tp->chain->index;
- cls_common->protocol = tp->protocol;
- cls_common->prio = tp->prio;
- if (tc_skip_sw(flags) || flags & TCA_CLS_FLAGS_VERBOSE)
- cls_common->extack = extack;
-}
-
enum tc_fl_command {
TC_CLSFLOWER_REPLACE,
TC_CLSFLOWER_DESTROY,
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index a6d00093f35e..72dbb96fc549 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -246,65 +246,7 @@ struct tcf_result {

struct tcf_chain;

-struct tcf_proto_ops {
- struct list_head head;
- char kind[IFNAMSIZ];
-
- int (*classify)(struct sk_buff *,
- const struct tcf_proto *,
- struct tcf_result *);
- int (*init)(struct tcf_proto*);
- void (*destroy)(struct tcf_proto *tp,
- struct netlink_ext_ack *extack);
-
- void* (*get)(struct tcf_proto*, u32 handle);
- int (*change)(struct net *net, struct sk_buff *,
- struct tcf_proto*, unsigned long,
- u32 handle, struct nlattr **,
- void **, bool,
- struct netlink_ext_ack *);
- int (*delete)(struct tcf_proto *tp, void *arg,
- bool *last,
- struct netlink_ext_ack *);
- void (*walk)(struct tcf_proto*, struct tcf_walker *arg);
- 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 * (*tmplt_create)(struct net *net,
- struct tcf_chain *chain,
- struct nlattr **tca,
- struct netlink_ext_ack *extack);
- void (*tmplt_destroy)(void *tmplt_priv);
-
- /* rtnetlink specific */
- int (*dump)(struct net*, struct tcf_proto*, void *,
- struct sk_buff *skb, struct tcmsg*);
- int (*tmplt_dump)(struct sk_buff *skb,
- struct net *net,
- void *tmplt_priv);
-
- struct module *owner;
-};
-
-struct tcf_proto {
- /* Fast access part */
- struct tcf_proto __rcu *next;
- void __rcu *root;
-
- /* called under RCU BH lock*/
- int (*classify)(struct sk_buff *,
- const struct tcf_proto *,
- struct tcf_result *);
- __be16 protocol;
-
- /* All the rest */
- u32 prio;
- void *data;
- const struct tcf_proto_ops *ops;
- struct tcf_chain *chain;
- struct rcu_head rcu;
-};
+struct tcf_proto_ops;

struct qdisc_skb_cb {
unsigned int pkt_len;
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 229d63c99be2..e946ada18299 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -25,11 +25,12 @@
#include <linux/list.h>
#include <net/net_namespace.h>
#include <net/sock.h>
-#include <net/sch_generic.h>
#include <net/pkt_cls.h>
#include <net/act_api.h>
#include <net/netlink.h>

+#include "tcf_proto.h"
+
static int tcf_action_goto_chain_init(struct tc_action *a, struct tcf_proto *tp)
{
u32 chain_index = a->tcfa_action & TC_ACT_EXT_VAL_MASK;
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 31bd1439cf60..be5fba6355c5 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -31,6 +31,8 @@
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+
/* The list of all installed classifier types */
static LIST_HEAD(tcf_proto_base);

diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
index 6a5dce8baf19..3772432889f2 100644
--- a/net/sched/cls_basic.c
+++ b/net/sched/cls_basic.c
@@ -22,6 +22,8 @@
#include <net/act_api.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+
struct basic_head {
struct list_head flist;
struct idr handle_idr;
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
index fa6fe2fe0f32..fb2478e357cd 100644
--- a/net/sched/cls_bpf.c
+++ b/net/sched/cls_bpf.c
@@ -23,6 +23,8 @@
#include <net/pkt_cls.h>
#include <net/sock.h>

+#include "tcf_proto.h"
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Daniel Borkmann <dborkman@xxxxxxxxxx>");
MODULE_DESCRIPTION("TC BPF based classifier");
diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c
index 3bc01bdde165..5638c711e53c 100644
--- a/net/sched/cls_cgroup.c
+++ b/net/sched/cls_cgroup.c
@@ -18,6 +18,8 @@
#include <net/sock.h>
#include <net/cls_cgroup.h>

+#include "tcf_proto.h"
+
struct cls_cgroup_head {
u32 handle;
struct tcf_exts exts;
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index 2bb043cd436b..7e60e432e3a8 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -33,6 +33,8 @@
#include <net/netfilter/nf_conntrack.h>
#endif

+#include "tcf_proto.h"
+
struct flow_head {
struct list_head filters;
struct rcu_head rcu;
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 6fd9bdd93796..b36c61f7ee44 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -20,7 +20,6 @@
#include <linux/ip.h>
#include <linux/mpls.h>

-#include <net/sch_generic.h>
#include <net/pkt_cls.h>
#include <net/ip.h>
#include <net/flow_dissector.h>
@@ -29,6 +28,8 @@
#include <net/dst.h>
#include <net/dst_metadata.h>

+#include "tcf_proto.h"
+
struct fl_flow_key {
int indev_ifindex;
struct flow_dissector_key_control control;
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index 29eeeaf3ea44..be872b1653f5 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -28,7 +28,8 @@
#include <net/netlink.h>
#include <net/act_api.h>
#include <net/pkt_cls.h>
-#include <net/sch_generic.h>
+
+#include "tcf_proto.h"

#define HTSIZE 256

diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c
index 856fa79d4ffd..708faf62ecab 100644
--- a/net/sched/cls_matchall.c
+++ b/net/sched/cls_matchall.c
@@ -13,9 +13,10 @@
#include <linux/init.h>
#include <linux/module.h>

-#include <net/sch_generic.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+
struct cls_mall_head {
struct tcf_exts exts;
struct tcf_result res;
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index 0404aa5fa7cb..d40ae6d14b2d 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -22,6 +22,8 @@
#include <net/act_api.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+
/*
* 1. For now we assume that route tags < 256.
* It allows to use direct table lookups, instead of hash tables.
diff --git a/net/sched/cls_rsvp.c b/net/sched/cls_rsvp.c
index cbb5e0d600f3..131a81aeaa4e 100644
--- a/net/sched/cls_rsvp.c
+++ b/net/sched/cls_rsvp.c
@@ -20,6 +20,8 @@
#include <net/act_api.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+
#define RSVP_DST_LEN 1
#define RSVP_ID "rsvp"
#define RSVP_OPS cls_rsvp_ops
diff --git a/net/sched/cls_rsvp6.c b/net/sched/cls_rsvp6.c
index dd08aea2aee5..159dc01cf251 100644
--- a/net/sched/cls_rsvp6.c
+++ b/net/sched/cls_rsvp6.c
@@ -20,6 +20,8 @@
#include <net/pkt_cls.h>
#include <net/netlink.h>

+#include "tcf_proto.h"
+
#define RSVP_DST_LEN 4
#define RSVP_ID "rsvp6"
#define RSVP_OPS cls_rsvp6_ops
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c
index 9ccc93f257db..e7d06c3d40a3 100644
--- a/net/sched/cls_tcindex.c
+++ b/net/sched/cls_tcindex.c
@@ -13,7 +13,8 @@
#include <net/act_api.h>
#include <net/netlink.h>
#include <net/pkt_cls.h>
-#include <net/sch_generic.h>
+
+#include "tcf_proto.h"

/*
* Passing parameters to the root seems to be done more awkwardly than really
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index d5d2a6dc3921..7b3bdfd80001 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -47,6 +47,8 @@
#include <net/pkt_cls.h>
#include <linux/idr.h>

+#include "tcf_proto.h"
+
struct tc_u_knode {
struct tc_u_knode __rcu *next;
u32 handle;
diff --git a/net/sched/ematch.c b/net/sched/ematch.c
index 1331a4c2d8ff..b123880fbe07 100644
--- a/net/sched/ematch.c
+++ b/net/sched/ematch.c
@@ -90,6 +90,8 @@
#include <linux/skbuff.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+
static LIST_HEAD(ematch_ops);
static DEFINE_RWLOCK(ematch_mod_lock);

diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 98541c6399db..d6ac218811d0 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -37,6 +37,8 @@
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+
/*

Short review.
diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c
index cd49afca9617..6bf259e55319 100644
--- a/net/sched/sch_atm.c
+++ b/net/sched/sch_atm.c
@@ -17,6 +17,8 @@
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+
/*
* The ATM queuing discipline provides a framework for invoking classifiers
* (aka "filters"), which in turn select classes of this queuing discipline.
diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index 35fc7252187c..fcfd5f321447 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -75,6 +75,8 @@
#include <net/netfilter/nf_conntrack_core.h>
#endif

+#include "tcf_proto.h"
+
#define CAKE_SET_WAYS (8)
#define CAKE_MAX_TINS (8)
#define CAKE_QUEUES (1024)
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index f42025d53cfe..8021ba377dfd 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -21,6 +21,8 @@
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+

/* Class-Based Queueing (CBQ) algorithm.
=======================================
diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c
index e0b0cf8a9939..19a48fa95b9b 100644
--- a/net/sched/sch_drr.c
+++ b/net/sched/sch_drr.c
@@ -18,6 +18,8 @@
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+
struct drr_class {
struct Qdisc_class_common common;
unsigned int filter_cnt;
diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c
index 049714c57075..b3a4537afbcb 100644
--- a/net/sched/sch_dsmark.c
+++ b/net/sched/sch_dsmark.c
@@ -18,6 +18,8 @@
#include <net/inet_ecn.h>
#include <asm/byteorder.h>

+#include "tcf_proto.h"
+
/*
* classid class marking
* ------- ----- -------
diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index 6c0a9d5dbf94..8868a8e1a81f 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -28,6 +28,8 @@
#include <net/codel_impl.h>
#include <net/codel_qdisc.h>

+#include "tcf_proto.h"
+
/* Fair Queue CoDel.
*
* Principles :
diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
index 3278a76f6861..9c75b77da56e 100644
--- a/net/sched/sch_hfsc.c
+++ b/net/sched/sch_hfsc.c
@@ -68,6 +68,8 @@
#include <net/pkt_cls.h>
#include <asm/div64.h>

+#include "tcf_proto.h"
+
/*
* kernel internal service curve representation:
* coordinates are given by 64 bit unsigned integers.
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index 43c4bfe625a9..c206b3cfdfb2 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -38,10 +38,10 @@
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <net/netlink.h>
-#include <net/sch_generic.h>
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
/* HTB algorithm.
Author: devik@xxxxxx
========================================================================
diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c
index 1da7ea8de0ad..107563c14e24 100644
--- a/net/sched/sch_multiq.c
+++ b/net/sched/sch_multiq.c
@@ -27,6 +27,8 @@
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+
struct multiq_sched_data {
u16 bands;
u16 max_bands;
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
index 222e53d3d27a..4fed3fd38dd3 100644
--- a/net/sched/sch_prio.c
+++ b/net/sched/sch_prio.c
@@ -22,6 +22,8 @@
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+
struct prio_sched_data {
int bands;
struct tcf_proto __rcu *filter_list;
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
index bb1a9c11fc54..32f68e639037 100644
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -19,6 +19,8 @@
#include <net/pkt_sched.h>
#include <net/pkt_cls.h>

+#include "tcf_proto.h"
+

/* Quick Fair Queueing Plus
========================
diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c
index 7cbdad8419b7..5465249c600f 100644
--- a/net/sched/sch_sfb.c
+++ b/net/sched/sch_sfb.c
@@ -28,6 +28,8 @@
#include <net/pkt_cls.h>
#include <net/inet_ecn.h>

+#include "tcf_proto.h"
+
/*
* SFB uses two B[l][n] : L x N arrays of bins (L levels, N bins per level)
* This implementation uses L = 8 and N = 16
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index 2f2678197760..abc1598e87e7 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -26,6 +26,8 @@
#include <net/pkt_cls.h>
#include <net/red.h>

+#include "tcf_proto.h"
+

/* Stochastic Fairness Queuing algorithm.
=======================================
diff --git a/net/sched/tcf_proto.h b/net/sched/tcf_proto.h
new file mode 100644
index 000000000000..b8d0e15e7f26
--- /dev/null
+++ b/net/sched/tcf_proto.h
@@ -0,0 +1,104 @@
+/* struct tcf_proto internal details - outside of net/sched it's opaque */
+
+#include <net/sch_generic.h>
+
+struct tcf_proto {
+ /* Fast access part */
+ struct tcf_proto __rcu *next;
+ void __rcu *root;
+
+ /* called under RCU BH lock*/
+ int (*classify)(struct sk_buff *,
+ const struct tcf_proto *,
+ struct tcf_result *);
+ __be16 protocol;
+
+ /* All the rest */
+ u32 prio;
+ void *data;
+ const struct tcf_proto_ops *ops;
+ struct tcf_chain *chain;
+ struct rcu_head rcu;
+};
+
+struct tcf_proto_ops {
+ struct list_head head;
+ char kind[IFNAMSIZ];
+
+ int (*classify)(struct sk_buff *,
+ const struct tcf_proto *,
+ struct tcf_result *);
+ int (*init)(struct tcf_proto*);
+ void (*destroy)(struct tcf_proto *tp,
+ struct netlink_ext_ack *extack);
+
+ void* (*get)(struct tcf_proto*, u32 handle);
+ int (*change)(struct net *net, struct sk_buff *,
+ struct tcf_proto*, unsigned long,
+ u32 handle, struct nlattr **,
+ void **, bool,
+ struct netlink_ext_ack *);
+ int (*delete)(struct tcf_proto *tp, void *arg,
+ bool *last,
+ struct netlink_ext_ack *);
+ void (*walk)(struct tcf_proto*, struct tcf_walker *arg);
+ 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 * (*tmplt_create)(struct net *net,
+ struct tcf_chain *chain,
+ struct nlattr **tca,
+ struct netlink_ext_ack *extack);
+ void (*tmplt_destroy)(void *tmplt_priv);
+
+ /* rtnetlink specific */
+ int (*dump)(struct net*, struct tcf_proto*, void *,
+ struct sk_buff *skb, struct tcmsg*);
+ int (*tmplt_dump)(struct sk_buff *skb,
+ struct net *net,
+ void *tmplt_priv);
+
+ struct module *owner;
+};
+
+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)
+ q->ops->cl_ops->unbind_tcf(q, cl);
+}
+
+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);
+}
+
+static inline void
+tc_cls_common_offload_init(struct tc_cls_common_offload *cls_common,
+ const struct tcf_proto *tp, u32 flags,
+ struct netlink_ext_ack *extack)
+{
+ cls_common->chain_index = tp->chain->index;
+ cls_common->protocol = tp->protocol;
+ cls_common->prio = tp->prio;
+ if (tc_skip_sw(flags) || flags & TCA_CLS_FLAGS_VERBOSE)
+ cls_common->extack = extack;
+}