Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit abbaccda authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller
Browse files

[NETFILTER]: ip_conntrack: fix invalid conntrack statistics RCU assumption



CONNTRACK_STAT_INC assumes rcu_read_lock in nf_hook_slow disables
preemption as well, making it legal to use __get_cpu_var without
disabling preemption manually. The assumption is not correct anymore
with preemptable RCU, additionally we need to protect against softirqs
when not holding ip_conntrack_lock.

Add CONNTRACK_STAT_INC_ATOMIC macro, which disables local softirqs,
and use where necessary.

Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 923f4902
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -301,6 +301,12 @@ extern unsigned int ip_conntrack_htable_size;
extern int ip_conntrack_checksum;
extern int ip_conntrack_checksum;
 
 
#define CONNTRACK_STAT_INC(count) (__get_cpu_var(ip_conntrack_stat).count++)
#define CONNTRACK_STAT_INC(count) (__get_cpu_var(ip_conntrack_stat).count++)
#define CONNTRACK_STAT_INC_ATOMIC(count)		\
do {							\
	local_bh_disable();				\
	__get_cpu_var(ip_conntrack_stat).count++;	\
	local_bh_enable();				\
} while (0)


#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS
#include <linux/notifier.h>
#include <linux/notifier.h>
+7 −7
Original line number Original line Diff line number Diff line
@@ -538,7 +538,7 @@ static int early_drop(struct list_head *chain)
	if (del_timer(&ct->timeout)) {
	if (del_timer(&ct->timeout)) {
		death_by_timeout((unsigned long)ct);
		death_by_timeout((unsigned long)ct);
		dropped = 1;
		dropped = 1;
		CONNTRACK_STAT_INC(early_drop);
		CONNTRACK_STAT_INC_ATOMIC(early_drop);
	}
	}
	ip_conntrack_put(ct);
	ip_conntrack_put(ct);
	return dropped;
	return dropped;
@@ -804,7 +804,7 @@ unsigned int ip_conntrack_in(unsigned int hooknum,


	/* Previously seen (loopback or untracked)?  Ignore. */
	/* Previously seen (loopback or untracked)?  Ignore. */
	if ((*pskb)->nfct) {
	if ((*pskb)->nfct) {
		CONNTRACK_STAT_INC(ignore);
		CONNTRACK_STAT_INC_ATOMIC(ignore);
		return NF_ACCEPT;
		return NF_ACCEPT;
	}
	}


@@ -840,20 +840,20 @@ unsigned int ip_conntrack_in(unsigned int hooknum,
	 * core what to do with the packet. */
	 * core what to do with the packet. */
	if (proto->error != NULL
	if (proto->error != NULL
	    && (ret = proto->error(*pskb, &ctinfo, hooknum)) <= 0) {
	    && (ret = proto->error(*pskb, &ctinfo, hooknum)) <= 0) {
		CONNTRACK_STAT_INC(error);
		CONNTRACK_STAT_INC_ATOMIC(error);
		CONNTRACK_STAT_INC(invalid);
		CONNTRACK_STAT_INC_ATOMIC(invalid);
		return -ret;
		return -ret;
	}
	}


	if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) {
	if (!(ct = resolve_normal_ct(*pskb, proto,&set_reply,hooknum,&ctinfo))) {
		/* Not valid part of a connection */
		/* Not valid part of a connection */
		CONNTRACK_STAT_INC(invalid);
		CONNTRACK_STAT_INC_ATOMIC(invalid);
		return NF_ACCEPT;
		return NF_ACCEPT;
	}
	}


	if (IS_ERR(ct)) {
	if (IS_ERR(ct)) {
		/* Too stressed to deal. */
		/* Too stressed to deal. */
		CONNTRACK_STAT_INC(drop);
		CONNTRACK_STAT_INC_ATOMIC(drop);
		return NF_DROP;
		return NF_DROP;
	}
	}


@@ -865,7 +865,7 @@ unsigned int ip_conntrack_in(unsigned int hooknum,
		 * the netfilter core what to do*/
		 * the netfilter core what to do*/
		nf_conntrack_put((*pskb)->nfct);
		nf_conntrack_put((*pskb)->nfct);
		(*pskb)->nfct = NULL;
		(*pskb)->nfct = NULL;
		CONNTRACK_STAT_INC(invalid);
		CONNTRACK_STAT_INC_ATOMIC(invalid);
		return -ret;
		return -ret;
	}
	}