summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCong Wang <xiyou.wangcong@gmail.com>2018-08-19 12:22:13 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-09-15 09:45:26 +0200
commite54c50709019d8680ba55b520407f1ba82432683 (patch)
tree4e9843a0942a6230975c597f1997f205a10c586a
parentcd7330c06195d3ff8c7a601bac449b0e8b3f2bec (diff)
downloadlinux-stable-e54c50709019d8680ba55b520407f1ba82432683.tar.gz
linux-stable-e54c50709019d8680ba55b520407f1ba82432683.tar.bz2
linux-stable-e54c50709019d8680ba55b520407f1ba82432683.zip
act_ife: fix a potential deadlock
[ Upstream commit 5ffe57da29b3802baeddaa40909682bbb4cb4d48 ] use_all_metadata() acquires read_lock(&ife_mod_lock), then calls add_metainfo() which calls find_ife_oplist() which acquires the same lock again. Deadlock! Introduce __add_metainfo() which accepts struct tcf_meta_ops *ops as an additional parameter and let its callers to decide how to find it. For use_all_metadata(), it already has ops, no need to find it again, just call __add_metainfo() directly. And, as ife_mod_lock is only needed for find_ife_oplist(), this means we can make non-atomic allocation for populate_metalist() now. Fixes: 817e9f2c5c26 ("act_ife: acquire ife_mod_lock before reading ifeoplist") Cc: Jamal Hadi Salim <jhs@mojatatu.com> 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>
-rw-r--r--net/sched/act_ife.c34
1 files changed, 21 insertions, 13 deletions
diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c
index 408584314af3..0f81b81c1ff6 100644
--- a/net/sched/act_ife.c
+++ b/net/sched/act_ife.c
@@ -278,22 +278,16 @@ static int load_metaops_and_vet(u32 metaid, void *val, int len)
/* called when adding new meta information
*/
-static int add_metainfo(struct tcf_ife_info *ife, u32 metaid, void *metaval,
- int len, bool atomic, bool exists)
+static int __add_metainfo(const struct tcf_meta_ops *ops,
+ struct tcf_ife_info *ife, u32 metaid, void *metaval,
+ int len, bool atomic, bool exists)
{
struct tcf_meta_info *mi = NULL;
- struct tcf_meta_ops *ops = find_ife_oplist(metaid);
int ret = 0;
- if (!ops)
- return -ENOENT;
-
mi = kzalloc(sizeof(*mi), atomic ? GFP_ATOMIC : GFP_KERNEL);
- if (!mi) {
- /*put back what find_ife_oplist took */
- module_put(ops->owner);
+ if (!mi)
return -ENOMEM;
- }
mi->metaid = metaid;
mi->ops = ops;
@@ -301,7 +295,6 @@ static int add_metainfo(struct tcf_ife_info *ife, u32 metaid, void *metaval,
ret = ops->alloc(mi, metaval, atomic ? GFP_ATOMIC : GFP_KERNEL);
if (ret != 0) {
kfree(mi);
- module_put(ops->owner);
return ret;
}
}
@@ -315,6 +308,21 @@ static int add_metainfo(struct tcf_ife_info *ife, u32 metaid, void *metaval,
return ret;
}
+static int add_metainfo(struct tcf_ife_info *ife, u32 metaid, void *metaval,
+ int len, bool exists)
+{
+ const struct tcf_meta_ops *ops = find_ife_oplist(metaid);
+ int ret;
+
+ if (!ops)
+ return -ENOENT;
+ ret = __add_metainfo(ops, ife, metaid, metaval, len, false, exists);
+ if (ret)
+ /*put back what find_ife_oplist took */
+ module_put(ops->owner);
+ return ret;
+}
+
static int use_all_metadata(struct tcf_ife_info *ife, bool exists)
{
struct tcf_meta_ops *o;
@@ -323,7 +331,7 @@ static int use_all_metadata(struct tcf_ife_info *ife, bool exists)
read_lock(&ife_mod_lock);
list_for_each_entry(o, &ifeoplist, list) {
- rc = add_metainfo(ife, o->metaid, NULL, 0, true, exists);
+ rc = __add_metainfo(o, ife, o->metaid, NULL, 0, true, exists);
if (rc == 0)
installed += 1;
}
@@ -412,7 +420,7 @@ static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb,
if (rc != 0)
return rc;
- rc = add_metainfo(ife, i, val, len, false, exists);
+ rc = add_metainfo(ife, i, val, len, exists);
if (rc)
return rc;
}