netfilter: nft_limit: reject configurations that cause integer overflow
[ Upstream commit c9d9eb9c53d37cdebbad56b91e40baf42d5a97aa ]
Reject bogus configs where internal token counter wraps around.
This only occurs with very very large requests, such as 17gbyte/s.
Its better to reject this rather than having incorrect ratelimit.
Fixes: d2168e849e
("netfilter: nft_limit: add per-byte limiting")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
c817f5c016
commit
bc6e242bb7
@ -58,17 +58,19 @@ static inline bool nft_limit_eval(struct nft_limit_priv *priv, u64 cost)
|
|||||||
static int nft_limit_init(struct nft_limit_priv *priv,
|
static int nft_limit_init(struct nft_limit_priv *priv,
|
||||||
const struct nlattr * const tb[], bool pkts)
|
const struct nlattr * const tb[], bool pkts)
|
||||||
{
|
{
|
||||||
|
u64 unit, tokens, rate_with_burst;
|
||||||
bool invert = false;
|
bool invert = false;
|
||||||
u64 unit, tokens;
|
|
||||||
|
|
||||||
if (tb[NFTA_LIMIT_RATE] == NULL ||
|
if (tb[NFTA_LIMIT_RATE] == NULL ||
|
||||||
tb[NFTA_LIMIT_UNIT] == NULL)
|
tb[NFTA_LIMIT_UNIT] == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
priv->rate = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE]));
|
priv->rate = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE]));
|
||||||
|
if (priv->rate == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
unit = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT]));
|
unit = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT]));
|
||||||
priv->nsecs = unit * NSEC_PER_SEC;
|
if (check_mul_overflow(unit, NSEC_PER_SEC, &priv->nsecs))
|
||||||
if (priv->rate == 0 || priv->nsecs < unit)
|
|
||||||
return -EOVERFLOW;
|
return -EOVERFLOW;
|
||||||
|
|
||||||
if (tb[NFTA_LIMIT_BURST])
|
if (tb[NFTA_LIMIT_BURST])
|
||||||
@ -77,18 +79,25 @@ static int nft_limit_init(struct nft_limit_priv *priv,
|
|||||||
if (pkts && priv->burst == 0)
|
if (pkts && priv->burst == 0)
|
||||||
priv->burst = NFT_LIMIT_PKT_BURST_DEFAULT;
|
priv->burst = NFT_LIMIT_PKT_BURST_DEFAULT;
|
||||||
|
|
||||||
if (priv->rate + priv->burst < priv->rate)
|
if (check_add_overflow(priv->rate, priv->burst, &rate_with_burst))
|
||||||
return -EOVERFLOW;
|
return -EOVERFLOW;
|
||||||
|
|
||||||
if (pkts) {
|
if (pkts) {
|
||||||
tokens = div64_u64(priv->nsecs, priv->rate) * priv->burst;
|
u64 tmp = div64_u64(priv->nsecs, priv->rate);
|
||||||
|
|
||||||
|
if (check_mul_overflow(tmp, priv->burst, &tokens))
|
||||||
|
return -EOVERFLOW;
|
||||||
} else {
|
} else {
|
||||||
|
u64 tmp;
|
||||||
|
|
||||||
/* The token bucket size limits the number of tokens can be
|
/* The token bucket size limits the number of tokens can be
|
||||||
* accumulated. tokens_max specifies the bucket size.
|
* accumulated. tokens_max specifies the bucket size.
|
||||||
* tokens_max = unit * (rate + burst) / rate.
|
* tokens_max = unit * (rate + burst) / rate.
|
||||||
*/
|
*/
|
||||||
tokens = div64_u64(priv->nsecs * (priv->rate + priv->burst),
|
if (check_mul_overflow(priv->nsecs, rate_with_burst, &tmp))
|
||||||
priv->rate);
|
return -EOVERFLOW;
|
||||||
|
|
||||||
|
tokens = div64_u64(tmp, priv->rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tb[NFTA_LIMIT_FLAGS]) {
|
if (tb[NFTA_LIMIT_FLAGS]) {
|
||||||
|
Loading…
Reference in New Issue
Block a user