net_sched: fix a NULL pointer deref in ipt action [Linux 4.19.72]

This Linux kernel change "net_sched: fix a NULL pointer deref in ipt action" is included in the Linux 4.19.72 release. This change is authored by Cong Wang <xiyou.wangcong [at] gmail.com> on Sun Aug 25 10:01:32 2019 -0700. The commit for this change in Linux stable tree is 3816693 (patch) which is from upstream commit 981471b. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 981471b.

net_sched: fix a NULL pointer deref in ipt action

[ Upstream commit 981471bd3abf4d572097645d765391533aac327d ]

The net pointer in struct xt_tgdtor_param is not explicitly
initialized therefore is still NULL when dereferencing it.
So we have to find a way to pass the correct net pointer to
ipt_destroy_target().

The best way I find is just saving the net pointer inside the per
netns struct tcf_idrinfo, which could make this patch smaller.

Fixes: 0c66dc1ea3f0 ("netfilter: conntrack: register hooks in netns when needed by ruleset")
Reported-and-tested-by: itugrok@yahoo.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>

There are 45 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 include/net/act_api.h      |  4 +++-
 net/sched/act_bpf.c        |  2 +-
 net/sched/act_connmark.c   |  2 +-
 net/sched/act_csum.c       |  2 +-
 net/sched/act_gact.c       |  2 +-
 net/sched/act_ife.c        |  2 +-
 net/sched/act_ipt.c        | 11 ++++++-----
 net/sched/act_mirred.c     |  2 +-
 net/sched/act_nat.c        |  2 +-
 net/sched/act_pedit.c      |  2 +-
 net/sched/act_police.c     |  2 +-
 net/sched/act_sample.c     |  2 +-
 net/sched/act_simple.c     |  2 +-
 net/sched/act_skbedit.c    |  2 +-
 net/sched/act_skbmod.c     |  2 +-
 net/sched/act_tunnel_key.c |  2 +-
 net/sched/act_vlan.c       |  2 +-
 17 files changed, 24 insertions(+), 21 deletions(-)

diff --git a/include/net/act_api.h b/include/net/act_api.h
index 9703034..0c82d7e 100644
--- a/include/net/act_api.h
+++ b/include/net/act_api.h
@@ -15,6 +15,7 @@
 struct tcf_idrinfo {
    spinlock_t  lock;
    struct idr  action_idr;
+   struct net  *net;
 };

 struct tc_action_ops;
@@ -107,7 +108,7 @@ struct tc_action_net {
 };

 static inline
-int tc_action_net_init(struct tc_action_net *tn,
+int tc_action_net_init(struct net *net, struct tc_action_net *tn,
               const struct tc_action_ops *ops)
 {
    int err = 0;
@@ -116,6 +117,7 @@ int tc_action_net_init(struct tc_action_net *tn,
    if (!tn->idrinfo)
        return -ENOMEM;
    tn->ops = ops;
+   tn->idrinfo->net = net;
    spin_lock_init(&tn->idrinfo->lock);
    idr_init(&tn->idrinfo->action_idr);
    return err;
diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c
index 20fae5c..800846d 100644
--- a/net/sched/act_bpf.c
+++ b/net/sched/act_bpf.c
@@ -413,7 +413,7 @@ static __net_init int bpf_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, bpf_net_id);

-   return tc_action_net_init(tn, &act_bpf_ops);
+   return tc_action_net_init(net, tn, &act_bpf_ops);
 }

 static void __net_exit bpf_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c
index 6054367..538dedd 100644
--- a/net/sched/act_connmark.c
+++ b/net/sched/act_connmark.c
@@ -215,7 +215,7 @@ static __net_init int connmark_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, connmark_net_id);

-   return tc_action_net_init(tn, &act_connmark_ops);
+   return tc_action_net_init(net, tn, &act_connmark_ops);
 }

 static void __net_exit connmark_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c
index 4043719..1e26944 100644
--- a/net/sched/act_csum.c
+++ b/net/sched/act_csum.c
@@ -678,7 +678,7 @@ static __net_init int csum_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, csum_net_id);

-   return tc_action_net_init(tn, &act_csum_ops);
+   return tc_action_net_init(net, tn, &act_csum_ops);
 }

 static void __net_exit csum_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index 72d3347..dfef962 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -263,7 +263,7 @@ static __net_init int gact_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, gact_net_id);

-   return tc_action_net_init(tn, &act_gact_ops);
+   return tc_action_net_init(net, tn, &act_gact_ops);
 }

 static void __net_exit gact_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c
index 24047e0..bac353b 100644
--- a/net/sched/act_ife.c
+++ b/net/sched/act_ife.c
@@ -887,7 +887,7 @@ static __net_init int ife_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, ife_net_id);

-   return tc_action_net_init(tn, &act_ife_ops);
+   return tc_action_net_init(net, tn, &act_ife_ops);
 }

 static void __net_exit ife_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
index 334f3a05..01d3669 100644
--- a/net/sched/act_ipt.c
+++ b/net/sched/act_ipt.c
@@ -65,12 +65,13 @@ static int ipt_init_target(struct net *net, struct xt_entry_target *t,
    return 0;
 }

-static void ipt_destroy_target(struct xt_entry_target *t)
+static void ipt_destroy_target(struct xt_entry_target *t, struct net *net)
 {
    struct xt_tgdtor_param par = {
        .target   = t->u.kernel.target,
        .targinfo = t->data,
        .family   = NFPROTO_IPV4,
+       .net      = net,
    };
    if (par.target->destroy != NULL)
        par.target->destroy(&par);
@@ -82,7 +83,7 @@ static void tcf_ipt_release(struct tc_action *a)
    struct tcf_ipt *ipt = to_ipt(a);

    if (ipt->tcfi_t) {
-       ipt_destroy_target(ipt->tcfi_t);
+       ipt_destroy_target(ipt->tcfi_t, a->idrinfo->net);
        kfree(ipt->tcfi_t);
    }
    kfree(ipt->tcfi_tname);
@@ -182,7 +183,7 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla,

    spin_lock_bh(&ipt->tcf_lock);
    if (ret != ACT_P_CREATED) {
-       ipt_destroy_target(ipt->tcfi_t);
+       ipt_destroy_target(ipt->tcfi_t, net);
        kfree(ipt->tcfi_tname);
        kfree(ipt->tcfi_t);
    }
@@ -353,7 +354,7 @@ static __net_init int ipt_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, ipt_net_id);

-   return tc_action_net_init(tn, &act_ipt_ops);
+   return tc_action_net_init(net, tn, &act_ipt_ops);
 }

 static void __net_exit ipt_exit_net(struct list_head *net_list)
@@ -403,7 +404,7 @@ static __net_init int xt_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, xt_net_id);

-   return tc_action_net_init(tn, &act_xt_ops);
+   return tc_action_net_init(net, tn, &act_xt_ops);
 }

 static void __net_exit xt_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index 548614b..399e3be 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -419,7 +419,7 @@ static __net_init int mirred_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, mirred_net_id);

-   return tc_action_net_init(tn, &act_mirred_ops);
+   return tc_action_net_init(net, tn, &act_mirred_ops);
 }

 static void __net_exit mirred_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c
index 6198289..d1b47a1 100644
--- a/net/sched/act_nat.c
+++ b/net/sched/act_nat.c
@@ -317,7 +317,7 @@ static __net_init int nat_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, nat_net_id);

-   return tc_action_net_init(tn, &act_nat_ops);
+   return tc_action_net_init(net, tn, &act_nat_ops);
 }

 static void __net_exit nat_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
index 82d258b..33c0cc5 100644
--- a/net/sched/act_pedit.c
+++ b/net/sched/act_pedit.c
@@ -488,7 +488,7 @@ static __net_init int pedit_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, pedit_net_id);

-   return tc_action_net_init(tn, &act_pedit_ops);
+   return tc_action_net_init(net, tn, &act_pedit_ops);
 }

 static void __net_exit pedit_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_police.c b/net/sched/act_police.c
index 997c34d..4db2595 100644
--- a/net/sched/act_police.c
+++ b/net/sched/act_police.c
@@ -342,7 +342,7 @@ static __net_init int police_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, police_net_id);

-   return tc_action_net_init(tn, &act_police_ops);
+   return tc_action_net_init(net, tn, &act_police_ops);
 }

 static void __net_exit police_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c
index 34ff6c3..9863531 100644
--- a/net/sched/act_sample.c
+++ b/net/sched/act_sample.c
@@ -258,7 +258,7 @@ static __net_init int sample_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, sample_net_id);

-   return tc_action_net_init(tn, &act_sample_ops);
+   return tc_action_net_init(net, tn, &act_sample_ops);
 }

 static void __net_exit sample_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c
index 658efae..b418ef6 100644
--- a/net/sched/act_simple.c
+++ b/net/sched/act_simple.c
@@ -215,7 +215,7 @@ static __net_init int simp_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, simp_net_id);

-   return tc_action_net_init(tn, &act_simp_ops);
+   return tc_action_net_init(net, tn, &act_simp_ops);
 }

 static void __net_exit simp_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c
index 7709710..a80179c 100644
--- a/net/sched/act_skbedit.c
+++ b/net/sched/act_skbedit.c
@@ -316,7 +316,7 @@ static __net_init int skbedit_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, skbedit_net_id);

-   return tc_action_net_init(tn, &act_skbedit_ops);
+   return tc_action_net_init(net, tn, &act_skbedit_ops);
 }

 static void __net_exit skbedit_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c
index 3038493..21d1952 100644
--- a/net/sched/act_skbmod.c
+++ b/net/sched/act_skbmod.c
@@ -277,7 +277,7 @@ static __net_init int skbmod_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, skbmod_net_id);

-   return tc_action_net_init(tn, &act_skbmod_ops);
+   return tc_action_net_init(net, tn, &act_skbmod_ops);
 }

 static void __net_exit skbmod_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c
index 66bfe57..43309ff 100644
--- a/net/sched/act_tunnel_key.c
+++ b/net/sched/act_tunnel_key.c
@@ -579,7 +579,7 @@ static __net_init int tunnel_key_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, tunnel_key_net_id);

-   return tc_action_net_init(tn, &act_tunnel_key_ops);
+   return tc_action_net_init(net, tn, &act_tunnel_key_ops);
 }

 static void __net_exit tunnel_key_exit_net(struct list_head *net_list)
diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c
index da993edd..41528b9 100644
--- a/net/sched/act_vlan.c
+++ b/net/sched/act_vlan.c
@@ -324,7 +324,7 @@ static __net_init int vlan_init_net(struct net *net)
 {
    struct tc_action_net *tn = net_generic(net, vlan_net_id);

-   return tc_action_net_init(tn, &act_vlan_ops);
+   return tc_action_net_init(net, tn, &act_vlan_ops);
 }

 static void __net_exit vlan_exit_net(struct list_head *net_list)

Leave a Reply

Your email address will not be published. Required fields are marked *