net: sched: act_sample: fix psample group handling on overwrite [Linux 4.14.143]

This Linux kernel change "net: sched: act_sample: fix psample group handling on overwrite" is included in the Linux 4.14.143 release. This change is authored by Vlad Buslov <vladbu [at] mellanox.com> on Tue Aug 27 21:49:38 2019 +0300. The commit for this change in Linux stable tree is 47cd2e1 (patch) which is from upstream commit dbf47a2. 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 dbf47a2.

net: sched: act_sample: fix psample group handling on overwrite

[ Upstream commit dbf47a2a094edf58983265e323ca4bdcdb58b5ee ]

Action sample doesn't properly handle psample_group pointer in overwrite
case. Following issues need to be fixed:

- In tcf_sample_init() function RCU_INIT_POINTER() is used to set
  s->psample_group, even though we neither setting the pointer to NULL, nor
  preventing concurrent readers from accessing the pointer in some way.
  Use rcu_swap_protected() instead to safely reset the pointer.

- Old value of s->psample_group is not released or deallocated in any way,
  which results resource leak. Use psample_group_put() on non-NULL value
  obtained with rcu_swap_protected().

- The function psample_group_put() that released reference to struct
  psample_group pointed by rcu-pointer s->psample_group doesn't respect rcu
  grace period when deallocating it. Extend struct psample_group with rcu
  head and use kfree_rcu when freeing it.

Fixes: 5c5670fae430 ("net/sched: Introduce sample tc action")
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

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

 include/net/psample.h  | 1 +
 net/psample/psample.c  | 2 +-
 net/sched/act_sample.c | 5 ++++-
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/include/net/psample.h b/include/net/psample.h
index 9b80f81..94cb37a 100644
--- a/include/net/psample.h
+++ b/include/net/psample.h
@@ -12,6 +12,7 @@ struct psample_group {
    u32 group_num;
    u32 refcount;
    u32 seq;
+   struct rcu_head rcu;
 };

 struct psample_group *psample_group_get(struct net *net, u32 group_num);
diff --git a/net/psample/psample.c b/net/psample/psample.c
index 64f9562..4cea3532 100644
--- a/net/psample/psample.c
+++ b/net/psample/psample.c
@@ -156,7 +156,7 @@ static void psample_group_destroy(struct psample_group *group)
 {
    psample_group_notify(group, PSAMPLE_CMD_DEL_GROUP);
    list_del(&group->list);
-   kfree(group);
+   kfree_rcu(group, rcu);
 }

 static struct psample_group *
diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c
index 12748a0..489db10 100644
--- a/net/sched/act_sample.c
+++ b/net/sched/act_sample.c
@@ -92,13 +92,16 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla,
            tcf_idr_release(*a, bind);
        return -ENOMEM;
    }
-   RCU_INIT_POINTER(s->psample_group, psample_group);
+   rcu_swap_protected(s->psample_group, psample_group,
+              lockdep_is_held(&s->tcf_lock));

    if (tb[TCA_SAMPLE_TRUNC_SIZE]) {
        s->truncate = true;
        s->trunc_size = nla_get_u32(tb[TCA_SAMPLE_TRUNC_SIZE]);
    }

+   if (psample_group)
+       psample_group_put(psample_group);
    if (ret == ACT_P_CREATED)
        tcf_idr_insert(tn, *a);
    return ret;

Leave a Reply

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