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

Commit acc738fe authored by Jan Engelhardt's avatar Jan Engelhardt Committed by Patrick McHardy
Browse files

netfilter: xtables: avoid pointer to self



Commit 78454473 (netfilter: iptables:
lock free counters) broke a number of modules whose rule data referenced
itself. A reallocation would not reestablish the correct references, so
it is best to use a separate struct that does not fall under RCU.

Signed-off-by: default avatarJan Engelhardt <jengelh@medozas.de>
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
parent 95ba434f
Loading
Loading
Loading
Loading
+5 −4
Original line number Original line Diff line number Diff line
@@ -4,6 +4,8 @@
/* timings are in milliseconds. */
/* timings are in milliseconds. */
#define XT_LIMIT_SCALE 10000
#define XT_LIMIT_SCALE 10000


struct xt_limit_priv;

/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
   seconds, or one every 59 hours. */
   seconds, or one every 59 hours. */
struct xt_rateinfo {
struct xt_rateinfo {
@@ -11,11 +13,10 @@ struct xt_rateinfo {
	u_int32_t burst;  /* Period multiplier for upper limit. */
	u_int32_t burst;  /* Period multiplier for upper limit. */


	/* Used internally by the kernel */
	/* Used internally by the kernel */
	unsigned long prev;
	unsigned long prev; /* moved to xt_limit_priv */
	u_int32_t credit;
	u_int32_t credit; /* moved to xt_limit_priv */
	u_int32_t credit_cap, cost;
	u_int32_t credit_cap, cost;


	/* Ugly, ugly fucker. */
	struct xt_limit_priv *master;
	struct xt_rateinfo *master;
};
};
#endif /*_XT_RATE_H*/
#endif /*_XT_RATE_H*/
+3 −1
Original line number Original line Diff line number Diff line
@@ -6,13 +6,15 @@ enum xt_quota_flags {
};
};
#define XT_QUOTA_MASK		0x1
#define XT_QUOTA_MASK		0x1


struct xt_quota_priv;

struct xt_quota_info {
struct xt_quota_info {
	u_int32_t		flags;
	u_int32_t		flags;
	u_int32_t		pad;
	u_int32_t		pad;


	/* Used internally by the kernel */
	/* Used internally by the kernel */
	aligned_u64		quota;
	aligned_u64		quota;
	struct xt_quota_info	*master;
	struct xt_quota_priv	*master;
};
};


#endif /* _XT_QUOTA_H */
#endif /* _XT_QUOTA_H */
+4 −3
Original line number Original line Diff line number Diff line
@@ -13,6 +13,8 @@ enum xt_statistic_flags {
};
};
#define XT_STATISTIC_MASK		0x1
#define XT_STATISTIC_MASK		0x1


struct xt_statistic_priv;

struct xt_statistic_info {
struct xt_statistic_info {
	u_int16_t			mode;
	u_int16_t			mode;
	u_int16_t			flags;
	u_int16_t			flags;
@@ -23,11 +25,10 @@ struct xt_statistic_info {
		struct {
		struct {
			u_int32_t	every;
			u_int32_t	every;
			u_int32_t	packet;
			u_int32_t	packet;
			/* Used internally by the kernel */
			u_int32_t	count; /* unused */
			u_int32_t	count;
		} nth;
		} nth;
	} u;
	} u;
	struct xt_statistic_info	*master __attribute__((aligned(8)));
	struct xt_statistic_priv *master __attribute__((aligned(8)));
};
};


#endif /* _XT_STATISTIC_H */
#endif /* _XT_STATISTIC_H */
+29 −11
Original line number Original line Diff line number Diff line
@@ -14,6 +14,11 @@
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_limit.h>
#include <linux/netfilter/xt_limit.h>


struct xt_limit_priv {
	unsigned long prev;
	uint32_t credit;
};

MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Herve Eychenne <rv@wallfire.org>");
MODULE_AUTHOR("Herve Eychenne <rv@wallfire.org>");
MODULE_DESCRIPTION("Xtables: rate-limit match");
MODULE_DESCRIPTION("Xtables: rate-limit match");
@@ -60,18 +65,18 @@ static DEFINE_SPINLOCK(limit_lock);
static bool
static bool
limit_mt(const struct sk_buff *skb, const struct xt_match_param *par)
limit_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{
{
	struct xt_rateinfo *r =
	const struct xt_rateinfo *r = par->matchinfo;
		((const struct xt_rateinfo *)par->matchinfo)->master;
	struct xt_limit_priv *priv = r->master;
	unsigned long now = jiffies;
	unsigned long now = jiffies;


	spin_lock_bh(&limit_lock);
	spin_lock_bh(&limit_lock);
	r->credit += (now - xchg(&r->prev, now)) * CREDITS_PER_JIFFY;
	priv->credit += (now - xchg(&priv->prev, now)) * CREDITS_PER_JIFFY;
	if (r->credit > r->credit_cap)
	if (priv->credit > r->credit_cap)
		r->credit = r->credit_cap;
		priv->credit = r->credit_cap;


	if (r->credit >= r->cost) {
	if (priv->credit >= r->cost) {
		/* We're not limited. */
		/* We're not limited. */
		r->credit -= r->cost;
		priv->credit -= r->cost;
		spin_unlock_bh(&limit_lock);
		spin_unlock_bh(&limit_lock);
		return true;
		return true;
	}
	}
@@ -95,6 +100,7 @@ user2credits(u_int32_t user)
static bool limit_mt_check(const struct xt_mtchk_param *par)
static bool limit_mt_check(const struct xt_mtchk_param *par)
{
{
	struct xt_rateinfo *r = par->matchinfo;
	struct xt_rateinfo *r = par->matchinfo;
	struct xt_limit_priv *priv;


	/* Check for overflow. */
	/* Check for overflow. */
	if (r->burst == 0
	if (r->burst == 0
@@ -104,19 +110,30 @@ static bool limit_mt_check(const struct xt_mtchk_param *par)
		return false;
		return false;
	}
	}


	/* For SMP, we only want to use one set of counters. */
	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
	r->master = r;
	if (priv == NULL)
		return -ENOMEM;

	/* For SMP, we only want to use one set of state. */
	r->master = priv;
	if (r->cost == 0) {
	if (r->cost == 0) {
		/* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies *
		/* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies *
		   128. */
		   128. */
		r->prev = jiffies;
		priv->prev = jiffies;
		r->credit = user2credits(r->avg * r->burst);	 /* Credits full. */
		priv->credit = user2credits(r->avg * r->burst); /* Credits full. */
		r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */
		r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */
		r->cost = user2credits(r->avg);
		r->cost = user2credits(r->avg);
	}
	}
	return true;
	return true;
}
}


static void limit_mt_destroy(const struct xt_mtdtor_param *par)
{
	const struct xt_rateinfo *info = par->matchinfo;

	kfree(info->master);
}

#ifdef CONFIG_COMPAT
#ifdef CONFIG_COMPAT
struct compat_xt_rateinfo {
struct compat_xt_rateinfo {
	u_int32_t avg;
	u_int32_t avg;
@@ -167,6 +184,7 @@ static struct xt_match limit_mt_reg __read_mostly = {
	.family           = NFPROTO_UNSPEC,
	.family           = NFPROTO_UNSPEC,
	.match            = limit_mt,
	.match            = limit_mt,
	.checkentry       = limit_mt_check,
	.checkentry       = limit_mt_check,
	.destroy          = limit_mt_destroy,
	.matchsize        = sizeof(struct xt_rateinfo),
	.matchsize        = sizeof(struct xt_rateinfo),
#ifdef CONFIG_COMPAT
#ifdef CONFIG_COMPAT
	.compatsize       = sizeof(struct compat_xt_rateinfo),
	.compatsize       = sizeof(struct compat_xt_rateinfo),
+24 −7
Original line number Original line Diff line number Diff line
@@ -9,6 +9,10 @@
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_quota.h>
#include <linux/netfilter/xt_quota.h>


struct xt_quota_priv {
	uint64_t quota;
};

MODULE_LICENSE("GPL");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sam Johnston <samj@samj.net>");
MODULE_AUTHOR("Sam Johnston <samj@samj.net>");
MODULE_DESCRIPTION("Xtables: countdown quota match");
MODULE_DESCRIPTION("Xtables: countdown quota match");
@@ -20,18 +24,20 @@ static DEFINE_SPINLOCK(quota_lock);
static bool
static bool
quota_mt(const struct sk_buff *skb, const struct xt_match_param *par)
quota_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{
{
	struct xt_quota_info *q =
	struct xt_quota_info *q = (void *)par->matchinfo;
		((const struct xt_quota_info *)par->matchinfo)->master;
	struct xt_quota_priv *priv = q->master;
	bool ret = q->flags & XT_QUOTA_INVERT;
	bool ret = q->flags & XT_QUOTA_INVERT;


	spin_lock_bh(&quota_lock);
	spin_lock_bh(&quota_lock);
	if (q->quota >= skb->len) {
	if (priv->quota >= skb->len) {
		q->quota -= skb->len;
		priv->quota -= skb->len;
		ret = !ret;
		ret = !ret;
	} else {
	} else {
		/* we do not allow even small packets from now on */
		/* we do not allow even small packets from now on */
		q->quota = 0;
		priv->quota = 0;
	}
	}
	/* Copy quota back to matchinfo so that iptables can display it */
	q->quota = priv->quota;
	spin_unlock_bh(&quota_lock);
	spin_unlock_bh(&quota_lock);


	return ret;
	return ret;
@@ -43,17 +49,28 @@ static bool quota_mt_check(const struct xt_mtchk_param *par)


	if (q->flags & ~XT_QUOTA_MASK)
	if (q->flags & ~XT_QUOTA_MASK)
		return false;
		return false;
	/* For SMP, we only want to use one set of counters. */

	q->master = q;
	q->master = kmalloc(sizeof(*q->master), GFP_KERNEL);
	if (q->master == NULL)
		return -ENOMEM;

	return true;
	return true;
}
}


static void quota_mt_destroy(const struct xt_mtdtor_param *par)
{
	const struct xt_quota_info *q = par->matchinfo;

	kfree(q->master);
}

static struct xt_match quota_mt_reg __read_mostly = {
static struct xt_match quota_mt_reg __read_mostly = {
	.name       = "quota",
	.name       = "quota",
	.revision   = 0,
	.revision   = 0,
	.family     = NFPROTO_UNSPEC,
	.family     = NFPROTO_UNSPEC,
	.match      = quota_mt,
	.match      = quota_mt,
	.checkentry = quota_mt_check,
	.checkentry = quota_mt_check,
	.destroy    = quota_mt_destroy,
	.matchsize  = sizeof(struct xt_quota_info),
	.matchsize  = sizeof(struct xt_quota_info),
	.me         = THIS_MODULE,
	.me         = THIS_MODULE,
};
};
Loading