Commit 63f55acf authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso

netfilter: ecache: document extension area access rules

Once ct->ext gets free'd via kfree() rather than kfree_rcu we can't
access the extension area anymore without owning the conntrack.

This is a special case:

The worker is walking the pcpu dying list while holding dying list lock:
Neither ct nor ct->ext can be free'd until after the walk has completed.
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent f8615bf8
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
static DEFINE_MUTEX(nf_ct_ecache_mutex); static DEFINE_MUTEX(nf_ct_ecache_mutex);
#define ECACHE_RETRY_WAIT (HZ/10) #define ECACHE_RETRY_WAIT (HZ/10)
#define ECACHE_STACK_ALLOC (256 / sizeof(void *))
enum retry_state { enum retry_state {
STATE_CONGESTED, STATE_CONGESTED,
...@@ -39,11 +40,11 @@ enum retry_state { ...@@ -39,11 +40,11 @@ enum retry_state {
static enum retry_state ecache_work_evict_list(struct ct_pcpu *pcpu) static enum retry_state ecache_work_evict_list(struct ct_pcpu *pcpu)
{ {
struct nf_conn *refs[16]; struct nf_conn *refs[ECACHE_STACK_ALLOC];
enum retry_state ret = STATE_DONE;
struct nf_conntrack_tuple_hash *h; struct nf_conntrack_tuple_hash *h;
struct hlist_nulls_node *n; struct hlist_nulls_node *n;
unsigned int evicted = 0; unsigned int evicted = 0;
enum retry_state ret = STATE_DONE;
spin_lock(&pcpu->lock); spin_lock(&pcpu->lock);
...@@ -54,10 +55,22 @@ static enum retry_state ecache_work_evict_list(struct ct_pcpu *pcpu) ...@@ -54,10 +55,22 @@ static enum retry_state ecache_work_evict_list(struct ct_pcpu *pcpu)
if (!nf_ct_is_confirmed(ct)) if (!nf_ct_is_confirmed(ct))
continue; continue;
/* This ecache access is safe because the ct is on the
* pcpu dying list and we hold the spinlock -- the entry
* cannot be free'd until after the lock is released.
*
* This is true even if ct has a refcount of 0: the
* cpu that is about to free the entry must remove it
* from the dying list and needs the lock to do so.
*/
e = nf_ct_ecache_find(ct); e = nf_ct_ecache_find(ct);
if (!e || e->state != NFCT_ECACHE_DESTROY_FAIL) if (!e || e->state != NFCT_ECACHE_DESTROY_FAIL)
continue; continue;
/* ct is in NFCT_ECACHE_DESTROY_FAIL state, this means
* the worker owns this entry: the ct will remain valid
* until the worker puts its ct reference.
*/
if (nf_conntrack_event(IPCT_DESTROY, ct)) { if (nf_conntrack_event(IPCT_DESTROY, ct)) {
ret = STATE_CONGESTED; ret = STATE_CONGESTED;
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment