aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2015-10-05 16:51:01 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2015-10-12 17:04:34 +0200
commitae2d708ed8fb8fa713fdd7eacea5df40bae6315b (patch)
tree20ea2799e0bcc69377bd4eeee66f530cb0afae5e /include
parentnetfilter: xt_CT: don't put back reference to timeout policy object (diff)
downloadlinux-dev-ae2d708ed8fb8fa713fdd7eacea5df40bae6315b.tar.xz
linux-dev-ae2d708ed8fb8fa713fdd7eacea5df40bae6315b.zip
netfilter: conntrack: fix crash on timeout object removal
The object and module refcounts are updated for each conntrack template, however, if we delete the iptables rules and we flush the timeout database, we may end up with invalid references to timeout object that are just gone. Resolve this problem by setting the timeout reference to NULL when the custom timeout entry is removed from our base. This patch requires some RCU trickery to ensure safe pointer handling. This handling is similar to what we already do with conntrack helpers, the idea is to avoid bumping the timeout object reference counter from the packet path to avoid the cost of atomic ops. Reported-by: Stephen Hemminger <stephen@networkplumber.org> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'include')
-rw-r--r--include/net/netfilter/nf_conntrack_timeout.h25
1 files changed, 19 insertions, 6 deletions
diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h
index 62308713dd7f..f72be38860a7 100644
--- a/include/net/netfilter/nf_conntrack_timeout.h
+++ b/include/net/netfilter/nf_conntrack_timeout.h
@@ -20,10 +20,20 @@ struct ctnl_timeout {
};
struct nf_conn_timeout {
- struct ctnl_timeout *timeout;
+ struct ctnl_timeout __rcu *timeout;
};
-#define NF_CT_TIMEOUT_EXT_DATA(__t) (unsigned int *) &((__t)->timeout->data)
+static inline unsigned int *
+nf_ct_timeout_data(struct nf_conn_timeout *t)
+{
+ struct ctnl_timeout *timeout;
+
+ timeout = rcu_dereference(t->timeout);
+ if (timeout == NULL)
+ return NULL;
+
+ return (unsigned int *)timeout->data;
+}
static inline
struct nf_conn_timeout *nf_ct_timeout_find(const struct nf_conn *ct)
@@ -47,7 +57,7 @@ struct nf_conn_timeout *nf_ct_timeout_ext_add(struct nf_conn *ct,
if (timeout_ext == NULL)
return NULL;
- timeout_ext->timeout = timeout;
+ rcu_assign_pointer(timeout_ext->timeout, timeout);
return timeout_ext;
#else
@@ -64,10 +74,13 @@ nf_ct_timeout_lookup(struct net *net, struct nf_conn *ct,
unsigned int *timeouts;
timeout_ext = nf_ct_timeout_find(ct);
- if (timeout_ext)
- timeouts = NF_CT_TIMEOUT_EXT_DATA(timeout_ext);
- else
+ if (timeout_ext) {
+ timeouts = nf_ct_timeout_data(timeout_ext);
+ if (unlikely(!timeouts))
+ timeouts = l4proto->get_timeouts(net);
+ } else {
timeouts = l4proto->get_timeouts(net);
+ }
return timeouts;
#else