]> granicus.if.org Git - ipset/commitdiff
RCU safe comment extension handling
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Sun, 29 Mar 2015 10:13:53 +0000 (12:13 +0200)
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Sun, 29 Mar 2015 14:55:20 +0000 (16:55 +0200)
Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
kernel/include/linux/netfilter/ipset/ip_set.h
kernel/include/linux/netfilter/ipset/ip_set_comment.h
kernel/net/netfilter/ipset/ip_set_bitmap_gen.h
kernel/net/netfilter/ipset/ip_set_list_set.c

index 5c1c6a5c08cae69d8221b71762ca55c5ff085277..88949043641dd3cf0b1fe9c3f54f6d662a144d52 100644 (file)
@@ -109,8 +109,13 @@ struct ip_set_counter {
        atomic64_t packets;
 };
 
+struct ip_set_comment_rcu {
+       struct rcu_head rcu;
+       char str[0];
+};
+
 struct ip_set_comment {
-       char *str;
+       struct ip_set_comment_rcu __rcu *c;
 };
 
 struct ip_set_skbinfo {
index 21217ea008d79611051c17eb7aa0900c3f3e6663..42dce2bd6bdf7c2b982cda65fb6b0b8aa62331dd 100644 (file)
@@ -20,37 +20,43 @@ static inline void
 ip_set_init_comment(struct ip_set_comment *comment,
                    const struct ip_set_ext *ext)
 {
+       struct ip_set_comment_rcu *c = rcu_dereference_protected(comment->c, 1);
        size_t len = ext->comment ? strlen(ext->comment) : 0;
 
-       if (unlikely(comment->str)) {
-               kfree(comment->str);
-               comment->str = NULL;
+       if (unlikely(c)) {
+               kfree_rcu(c, rcu);
+               rcu_assign_pointer(comment->c, NULL);
        }
        if (!len)
                return;
        if (unlikely(len > IPSET_MAX_COMMENT_SIZE))
                len = IPSET_MAX_COMMENT_SIZE;
-       comment->str = kzalloc(len + 1, GFP_ATOMIC);
-       if (unlikely(!comment->str))
+       c = kzalloc(sizeof(*c) + len + 1, GFP_ATOMIC);
+       if (unlikely(!c))
                return;
-       strlcpy(comment->str, ext->comment, len + 1);
+       strlcpy(c->str, ext->comment, len + 1);
+       rcu_assign_pointer(comment->c, c);
 }
 
 static inline int
 ip_set_put_comment(struct sk_buff *skb, struct ip_set_comment *comment)
 {
-       if (!comment->str)
+       struct ip_set_comment_rcu *c = rcu_dereference_bh(comment->c);
+
+       if (!c)
                return 0;
-       return nla_put_string(skb, IPSET_ATTR_COMMENT, comment->str);
+       return nla_put_string(skb, IPSET_ATTR_COMMENT, c->str);
 }
 
 static inline void
 ip_set_comment_free(struct ip_set_comment *comment)
 {
-       if (unlikely(!comment->str))
+       struct ip_set_comment_rcu *c = rcu_dereference_bh(comment->c);
+
+       if (unlikely(!c))
                return;
-       kfree(comment->str);
-       comment->str = NULL;
+       kfree_rcu(c, rcu);
+       rcu_assign_pointer(comment->c, NULL);
 }
 
 #endif
index dba65b499c68e7edc9f7f4d6e94a53085d250992..381a011327f088dcf1246c4976d95840919ced03 100644 (file)
@@ -203,10 +203,13 @@ mtype_list(const struct ip_set *set,
        struct nlattr *adt, *nested;
        void *x;
        u32 id, first = cb->args[IPSET_CB_ARG0];
+       int ret = 0;
 
        adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
        if (!adt)
                return -EMSGSIZE;
+       /* Extensions may be replaced */
+       rcu_read_lock();
        for (; cb->args[IPSET_CB_ARG0] < map->elements;
             cb->args[IPSET_CB_ARG0]++) {
                id = cb->args[IPSET_CB_ARG0];
@@ -222,7 +225,8 @@ mtype_list(const struct ip_set *set,
                if (!nested) {
                        if (id == first) {
                                nla_nest_cancel(skb, adt);
-                               return -EMSGSIZE;
+                               ret = -EMSGSIZE;
+                               goto out;
                        }
 
                        goto nla_put_failure;
@@ -239,16 +243,18 @@ mtype_list(const struct ip_set *set,
        /* Set listing finished */
        cb->args[IPSET_CB_ARG0] = 0;
 
-       return 0;
+       goto out;
 
 nla_put_failure:
        nla_nest_cancel(skb, nested);
        if (unlikely(id == first)) {
                cb->args[IPSET_CB_ARG0] = 0;
-               return -EMSGSIZE;
+               ret = -EMSGSIZE;
        }
        ipset_nest_end(skb, adt);
-       return 0;
+out:
+       rcu_read_unlock();
+       return ret;
 }
 
 static void
index 8edadb5a7b45650fefeec42a88198af98f8df7c8..0b88000d4b669fe5741022f746345f32989511bb 100644 (file)
@@ -478,6 +478,7 @@ list_set_list(const struct ip_set *set,
        struct nlattr *atd, *nested;
        u32 i = 0, first = cb->args[IPSET_CB_ARG0];
        struct set_elem *e;
+       int ret = 0;
 
        atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
        if (!atd)
@@ -488,6 +489,7 @@ list_set_list(const struct ip_set *set,
                i++;
        }
 
+       rcu_read_lock();
        list_for_each_entry_from(e, &map->members, list) {
                i++;
                if (SET_WITH_TIMEOUT(set) &&
@@ -497,7 +499,8 @@ list_set_list(const struct ip_set *set,
                if (!nested) {
                        if (i == first) {
                                nla_nest_cancel(skb, atd);
-                               return -EMSGSIZE;
+                               ret = -EMSGSIZE;
+                               goto out;
                        }
                        goto nla_put_failure;
                }
@@ -512,17 +515,19 @@ list_set_list(const struct ip_set *set,
        ipset_nest_end(skb, atd);
        /* Set listing finished */
        cb->args[IPSET_CB_ARG0] = 0;
-       return 0;
+       goto out;
 
 nla_put_failure:
        nla_nest_cancel(skb, nested);
        if (unlikely(i == first)) {
                cb->args[IPSET_CB_ARG0] = 0;
-               return -EMSGSIZE;
+               ret = -EMSGSIZE;
        }
        cb->args[IPSET_CB_ARG0] = i - 1;
        ipset_nest_end(skb, atd);
-       return 0;
+out:
+       rcu_read_unlock();
+       return ret;
 }
 
 static bool