Commit 690bf643 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter fixes for net:

1) Harden set element field checks to avoid out-of-bound memory access,
   this patch also fixes the type of issue described in 7e6bc1f6
   ("netfilter: nf_tables: stricter validation of element data") in a
   broader way.

2) Patches to restrict the chain, set, and rule id lookup in the
   transaction to the corresponding top-level table, patches from
   Thadeu Lima de Souza Cascardo.

3) Fix incorrect comment in ip6t_LOG.h

4) nft_data_init() performs upfront validation of the expected data.
   struct nft_data_desc is used to describe the expected data to be
   received from userspace. The .size field represents the maximum size
   that can be stored, for bound checks. Then, .len is an input/output field
   which stores the expected length as input (this is optional, to restrict
   the checks), as output it stores the real length received from userspace
   (if it was not specified as input). This patch comes in response to
   7e6bc1f6 ("netfilter: nf_tables: stricter validation of element data")
   to address this type of issue in a more generic way by avoid opencoded
   data validation. Next patch requires this as a dependency.

5) Disallow jump to implicit chain from set element, this configuration
   is invalid. Only allow jump to chain via immediate expression is
   supported at this stage.

6) Fix possible null-pointer derefence in the error path of table updates,
   if memory allocation of the transaction fails. From Florian Westphal.

* git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  netfilter: nf_tables: fix null deref due to zeroed list head
  netfilter: nf_tables: disallow jump to implicit chain from set element
  netfilter: nf_tables: upfront validation of data via nft_data_init()
  netfilter: ip6t_LOG: Fix a typo in a comment
  netfilter: nf_tables: do not allow RULE_ID to refer to another chain
  netfilter: nf_tables: do not allow CHAIN_ID to refer to another table
  netfilter: nf_tables: do not allow SET_ID to refer to another table
  netfilter: nf_tables: validate variable length element extension
====================

Link: https://lore.kernel.org/r/20220809220532.130240-1-pablo@netfilter.org/Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents bc3c8fe3 58007785
......@@ -221,13 +221,18 @@ struct nft_ctx {
bool report;
};
enum nft_data_desc_flags {
NFT_DATA_DESC_SETELEM = (1 << 0),
};
struct nft_data_desc {
enum nft_data_types type;
unsigned int size;
unsigned int len;
unsigned int flags;
};
int nft_data_init(const struct nft_ctx *ctx,
struct nft_data *data, unsigned int size,
int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
struct nft_data_desc *desc, const struct nlattr *nla);
void nft_data_hold(const struct nft_data *data, enum nft_data_types type);
void nft_data_release(const struct nft_data *data, enum nft_data_types type);
......@@ -651,6 +656,7 @@ extern const struct nft_set_ext_type nft_set_ext_types[];
struct nft_set_ext_tmpl {
u16 len;
u8 offset[NFT_SET_EXT_NUM];
u8 ext_len[NFT_SET_EXT_NUM];
};
/**
......@@ -680,7 +686,8 @@ static inline int nft_set_ext_add_length(struct nft_set_ext_tmpl *tmpl, u8 id,
return -EINVAL;
tmpl->offset[id] = tmpl->len;
tmpl->len += nft_set_ext_types[id].len + len;
tmpl->ext_len[id] = nft_set_ext_types[id].len + len;
tmpl->len += tmpl->ext_len[id];
return 0;
}
......
......@@ -17,4 +17,4 @@ struct ip6t_log_info {
char prefix[30];
};
#endif /*_IPT_LOG_H*/
#endif /* _IP6T_LOG_H */
This diff is collapsed.
......@@ -93,7 +93,16 @@ static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = {
static int nft_bitwise_init_bool(struct nft_bitwise *priv,
const struct nlattr *const tb[])
{
struct nft_data_desc mask, xor;
struct nft_data_desc mask = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->mask),
.len = priv->len,
};
struct nft_data_desc xor = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->xor),
.len = priv->len,
};
int err;
if (tb[NFTA_BITWISE_DATA])
......@@ -103,37 +112,30 @@ static int nft_bitwise_init_bool(struct nft_bitwise *priv,
!tb[NFTA_BITWISE_XOR])
return -EINVAL;
err = nft_data_init(NULL, &priv->mask, sizeof(priv->mask), &mask,
tb[NFTA_BITWISE_MASK]);
err = nft_data_init(NULL, &priv->mask, &mask, tb[NFTA_BITWISE_MASK]);
if (err < 0)
return err;
if (mask.type != NFT_DATA_VALUE || mask.len != priv->len) {
err = -EINVAL;
goto err_mask_release;
}
err = nft_data_init(NULL, &priv->xor, sizeof(priv->xor), &xor,
tb[NFTA_BITWISE_XOR]);
err = nft_data_init(NULL, &priv->xor, &xor, tb[NFTA_BITWISE_XOR]);
if (err < 0)
goto err_mask_release;
if (xor.type != NFT_DATA_VALUE || xor.len != priv->len) {
err = -EINVAL;
goto err_xor_release;
}
goto err_xor_err;
return 0;
err_xor_release:
nft_data_release(&priv->xor, xor.type);
err_mask_release:
err_xor_err:
nft_data_release(&priv->mask, mask.type);
return err;
}
static int nft_bitwise_init_shift(struct nft_bitwise *priv,
const struct nlattr *const tb[])
{
struct nft_data_desc d;
struct nft_data_desc desc = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->data),
.len = sizeof(u32),
};
int err;
if (tb[NFTA_BITWISE_MASK] ||
......@@ -143,13 +145,12 @@ static int nft_bitwise_init_shift(struct nft_bitwise *priv,
if (!tb[NFTA_BITWISE_DATA])
return -EINVAL;
err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &d,
tb[NFTA_BITWISE_DATA]);
err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_BITWISE_DATA]);
if (err < 0)
return err;
if (d.type != NFT_DATA_VALUE || d.len != sizeof(u32) ||
priv->data.data[0] >= BITS_PER_TYPE(u32)) {
nft_data_release(&priv->data, d.type);
if (priv->data.data[0] >= BITS_PER_TYPE(u32)) {
nft_data_release(&priv->data, desc.type);
return -EINVAL;
}
......@@ -339,22 +340,21 @@ static const struct nft_expr_ops nft_bitwise_ops = {
static int
nft_bitwise_extract_u32_data(const struct nlattr * const tb, u32 *out)
{
struct nft_data_desc desc;
struct nft_data data;
int err = 0;
struct nft_data_desc desc = {
.type = NFT_DATA_VALUE,
.size = sizeof(data),
.len = sizeof(u32),
};
int err;
err = nft_data_init(NULL, &data, sizeof(data), &desc, tb);
err = nft_data_init(NULL, &data, &desc, tb);
if (err < 0)
return err;
if (desc.type != NFT_DATA_VALUE || desc.len != sizeof(u32)) {
err = -EINVAL;
goto err;
}
*out = data.data[0];
err:
nft_data_release(&data, desc.type);
return err;
return 0;
}
static int nft_bitwise_fast_init(const struct nft_ctx *ctx,
......
......@@ -73,20 +73,16 @@ static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_cmp_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc;
struct nft_data_desc desc = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->data),
};
int err;
err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc,
tb[NFTA_CMP_DATA]);
err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]);
if (err < 0)
return err;
if (desc.type != NFT_DATA_VALUE) {
err = -EINVAL;
nft_data_release(&priv->data, desc.type);
return err;
}
err = nft_parse_register_load(tb[NFTA_CMP_SREG], &priv->sreg, desc.len);
if (err < 0)
return err;
......@@ -214,12 +210,14 @@ static int nft_cmp_fast_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
{
struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc;
struct nft_data data;
struct nft_data_desc desc = {
.type = NFT_DATA_VALUE,
.size = sizeof(data),
};
int err;
err = nft_data_init(NULL, &data, sizeof(data), &desc,
tb[NFTA_CMP_DATA]);
err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
if (err < 0)
return err;
......@@ -313,11 +311,13 @@ static int nft_cmp16_fast_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
{
struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc;
struct nft_data_desc desc = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->data),
};
int err;
err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc,
tb[NFTA_CMP_DATA]);
err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]);
if (err < 0)
return err;
......@@ -380,8 +380,11 @@ const struct nft_expr_ops nft_cmp16_fast_ops = {
static const struct nft_expr_ops *
nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
{
struct nft_data_desc desc;
struct nft_data data;
struct nft_data_desc desc = {
.type = NFT_DATA_VALUE,
.size = sizeof(data),
};
enum nft_cmp_ops op;
u8 sreg;
int err;
......@@ -404,14 +407,10 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
return ERR_PTR(-EINVAL);
}
err = nft_data_init(NULL, &data, sizeof(data), &desc,
tb[NFTA_CMP_DATA]);
err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
if (err < 0)
return ERR_PTR(err);
if (desc.type != NFT_DATA_VALUE)
goto err1;
sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
if (op == NFT_CMP_EQ || op == NFT_CMP_NEQ) {
......@@ -423,9 +422,6 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
return &nft_cmp16_fast_ops;
}
return &nft_cmp_ops;
err1:
nft_data_release(&data, desc.type);
return ERR_PTR(-EINVAL);
}
struct nft_expr_type nft_cmp_type __read_mostly = {
......
......@@ -60,7 +60,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
&regs->data[priv->sreg_key], NULL,
&regs->data[priv->sreg_data],
timeout, 0, GFP_ATOMIC);
if (elem == NULL)
if (IS_ERR(elem))
goto err1;
ext = nft_set_elem_ext(set, elem);
......
......@@ -29,20 +29,36 @@ static const struct nla_policy nft_immediate_policy[NFTA_IMMEDIATE_MAX + 1] = {
[NFTA_IMMEDIATE_DATA] = { .type = NLA_NESTED },
};
static enum nft_data_types nft_reg_to_type(const struct nlattr *nla)
{
enum nft_data_types type;
u8 reg;
reg = ntohl(nla_get_be32(nla));
if (reg == NFT_REG_VERDICT)
type = NFT_DATA_VERDICT;
else
type = NFT_DATA_VALUE;
return type;
}
static int nft_immediate_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_immediate_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc;
struct nft_data_desc desc = {
.size = sizeof(priv->data),
};
int err;
if (tb[NFTA_IMMEDIATE_DREG] == NULL ||
tb[NFTA_IMMEDIATE_DATA] == NULL)
return -EINVAL;
err = nft_data_init(ctx, &priv->data, sizeof(priv->data), &desc,
tb[NFTA_IMMEDIATE_DATA]);
desc.type = nft_reg_to_type(tb[NFTA_IMMEDIATE_DREG]);
err = nft_data_init(ctx, &priv->data, &desc, tb[NFTA_IMMEDIATE_DATA]);
if (err < 0)
return err;
......
......@@ -51,7 +51,14 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr
const struct nlattr * const tb[])
{
struct nft_range_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc_from, desc_to;
struct nft_data_desc desc_from = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->data_from),
};
struct nft_data_desc desc_to = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->data_to),
};
int err;
u32 op;
......@@ -61,26 +68,16 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr
!tb[NFTA_RANGE_TO_DATA])
return -EINVAL;
err = nft_data_init(NULL, &priv->data_from, sizeof(priv->data_from),
&desc_from, tb[NFTA_RANGE_FROM_DATA]);
err = nft_data_init(NULL, &priv->data_from, &desc_from,
tb[NFTA_RANGE_FROM_DATA]);
if (err < 0)
return err;
if (desc_from.type != NFT_DATA_VALUE) {
err = -EINVAL;
goto err1;
}
err = nft_data_init(NULL, &priv->data_to, sizeof(priv->data_to),
&desc_to, tb[NFTA_RANGE_TO_DATA]);
err = nft_data_init(NULL, &priv->data_to, &desc_to,
tb[NFTA_RANGE_TO_DATA]);
if (err < 0)
goto err1;
if (desc_to.type != NFT_DATA_VALUE) {
err = -EINVAL;
goto err2;
}
if (desc_from.len != desc_to.len) {
err = -EINVAL;
goto err2;
......
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