Commit 9d73b42b authored by David S. Miller's avatar David S. Miller

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

Pablo Neira Ayuso says:

====================
Netfilter/IPVS fixes for net

The following patchset contains Netfilter/IPVS fixes for your net tree,
they are:

1) Don't truncate ethernet protocol type to u8 in nft_compat, from
   Arturo Borrero.

2) Fix several problems in the addition/deletion of elements in nf_tables.

3) Fix module refcount leak in ip_vs_sync, from Julian Anastasov.

4) Fix a race condition in the abort path in the nf_tables transaction
   infrastructure. Basically aborted rules can show up as active rules
   until changes are unrolled, oneliner from Patrick McHardy.

5) Check for overflows in the data area of the rule, also from Patrick.

6) Fix off-by-one in the per-rule user data size field. This introduces
   a new nft_userdata structure that is placed at the beginning of the
   user data area that contains the length to save some bits from the
   rule and we only need one bit to indicate its presence, from Patrick.

7) Fix rule replacement error path, the replaced rule is deleted on
   error instead of leaving it in place. This has been fixed by relying
   on the abort path to undo the incomplete replacement.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 3e32e733 59900e0a
...@@ -119,6 +119,22 @@ int nft_validate_data_load(const struct nft_ctx *ctx, enum nft_registers reg, ...@@ -119,6 +119,22 @@ int nft_validate_data_load(const struct nft_ctx *ctx, enum nft_registers reg,
const struct nft_data *data, const struct nft_data *data,
enum nft_data_types type); enum nft_data_types type);
/**
* struct nft_userdata - user defined data associated with an object
*
* @len: length of the data
* @data: content
*
* The presence of user data is indicated in an object specific fashion,
* so a length of zero can't occur and the value "len" indicates data
* of length len + 1.
*/
struct nft_userdata {
u8 len;
unsigned char data[0];
};
/** /**
* struct nft_set_elem - generic representation of set elements * struct nft_set_elem - generic representation of set elements
* *
...@@ -380,7 +396,7 @@ static inline void *nft_expr_priv(const struct nft_expr *expr) ...@@ -380,7 +396,7 @@ static inline void *nft_expr_priv(const struct nft_expr *expr)
* @handle: rule handle * @handle: rule handle
* @genmask: generation mask * @genmask: generation mask
* @dlen: length of expression data * @dlen: length of expression data
* @ulen: length of user data (used for comments) * @udata: user data is appended to the rule
* @data: expression data * @data: expression data
*/ */
struct nft_rule { struct nft_rule {
...@@ -388,7 +404,7 @@ struct nft_rule { ...@@ -388,7 +404,7 @@ struct nft_rule {
u64 handle:42, u64 handle:42,
genmask:2, genmask:2,
dlen:12, dlen:12,
ulen:8; udata:1;
unsigned char data[] unsigned char data[]
__attribute__((aligned(__alignof__(struct nft_expr)))); __attribute__((aligned(__alignof__(struct nft_expr))));
}; };
...@@ -476,7 +492,7 @@ static inline struct nft_expr *nft_expr_last(const struct nft_rule *rule) ...@@ -476,7 +492,7 @@ static inline struct nft_expr *nft_expr_last(const struct nft_rule *rule)
return (struct nft_expr *)&rule->data[rule->dlen]; return (struct nft_expr *)&rule->data[rule->dlen];
} }
static inline void *nft_userdata(const struct nft_rule *rule) static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule)
{ {
return (void *)&rule->data[rule->dlen]; return (void *)&rule->data[rule->dlen];
} }
......
...@@ -896,6 +896,8 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param, ...@@ -896,6 +896,8 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
IP_VS_DBG(2, "BACKUP, add new conn. failed\n"); IP_VS_DBG(2, "BACKUP, add new conn. failed\n");
return; return;
} }
if (!(flags & IP_VS_CONN_F_TEMPLATE))
kfree(param->pe_data);
} }
if (opt) if (opt)
...@@ -1169,6 +1171,7 @@ static inline int ip_vs_proc_sync_conn(struct net *net, __u8 *p, __u8 *msg_end) ...@@ -1169,6 +1171,7 @@ static inline int ip_vs_proc_sync_conn(struct net *net, __u8 *p, __u8 *msg_end)
(opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL) (opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL)
); );
#endif #endif
ip_vs_pe_put(param.pe);
return 0; return 0;
/* Error exit */ /* Error exit */
out: out:
......
...@@ -227,7 +227,7 @@ nft_rule_deactivate_next(struct net *net, struct nft_rule *rule) ...@@ -227,7 +227,7 @@ nft_rule_deactivate_next(struct net *net, struct nft_rule *rule)
static inline void nft_rule_clear(struct net *net, struct nft_rule *rule) static inline void nft_rule_clear(struct net *net, struct nft_rule *rule)
{ {
rule->genmask = 0; rule->genmask &= ~(1 << gencursor_next(net));
} }
static int static int
...@@ -1711,9 +1711,12 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net, ...@@ -1711,9 +1711,12 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net,
} }
nla_nest_end(skb, list); nla_nest_end(skb, list);
if (rule->ulen && if (rule->udata) {
nla_put(skb, NFTA_RULE_USERDATA, rule->ulen, nft_userdata(rule))) struct nft_userdata *udata = nft_userdata(rule);
goto nla_put_failure; if (nla_put(skb, NFTA_RULE_USERDATA, udata->len + 1,
udata->data) < 0)
goto nla_put_failure;
}
nlmsg_end(skb, nlh); nlmsg_end(skb, nlh);
return 0; return 0;
...@@ -1896,11 +1899,12 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, ...@@ -1896,11 +1899,12 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
struct nft_table *table; struct nft_table *table;
struct nft_chain *chain; struct nft_chain *chain;
struct nft_rule *rule, *old_rule = NULL; struct nft_rule *rule, *old_rule = NULL;
struct nft_userdata *udata;
struct nft_trans *trans = NULL; struct nft_trans *trans = NULL;
struct nft_expr *expr; struct nft_expr *expr;
struct nft_ctx ctx; struct nft_ctx ctx;
struct nlattr *tmp; struct nlattr *tmp;
unsigned int size, i, n, ulen = 0; unsigned int size, i, n, ulen = 0, usize = 0;
int err, rem; int err, rem;
bool create; bool create;
u64 handle, pos_handle; u64 handle, pos_handle;
...@@ -1968,12 +1972,19 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, ...@@ -1968,12 +1972,19 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
n++; n++;
} }
} }
/* Check for overflow of dlen field */
err = -EFBIG;
if (size >= 1 << 12)
goto err1;
if (nla[NFTA_RULE_USERDATA]) if (nla[NFTA_RULE_USERDATA]) {
ulen = nla_len(nla[NFTA_RULE_USERDATA]); ulen = nla_len(nla[NFTA_RULE_USERDATA]);
if (ulen > 0)
usize = sizeof(struct nft_userdata) + ulen;
}
err = -ENOMEM; err = -ENOMEM;
rule = kzalloc(sizeof(*rule) + size + ulen, GFP_KERNEL); rule = kzalloc(sizeof(*rule) + size + usize, GFP_KERNEL);
if (rule == NULL) if (rule == NULL)
goto err1; goto err1;
...@@ -1981,10 +1992,13 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, ...@@ -1981,10 +1992,13 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
rule->handle = handle; rule->handle = handle;
rule->dlen = size; rule->dlen = size;
rule->ulen = ulen; rule->udata = ulen ? 1 : 0;
if (ulen) if (ulen) {
nla_memcpy(nft_userdata(rule), nla[NFTA_RULE_USERDATA], ulen); udata = nft_userdata(rule);
udata->len = ulen - 1;
nla_memcpy(udata->data, nla[NFTA_RULE_USERDATA], ulen);
}
expr = nft_expr_first(rule); expr = nft_expr_first(rule);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
...@@ -2031,12 +2045,6 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, ...@@ -2031,12 +2045,6 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
err3: err3:
list_del_rcu(&rule->list); list_del_rcu(&rule->list);
if (trans) {
list_del_rcu(&nft_trans_rule(trans)->list);
nft_rule_clear(net, nft_trans_rule(trans));
nft_trans_destroy(trans);
chain->use++;
}
err2: err2:
nf_tables_rule_destroy(&ctx, rule); nf_tables_rule_destroy(&ctx, rule);
err1: err1:
...@@ -3612,12 +3620,11 @@ static int nf_tables_commit(struct sk_buff *skb) ...@@ -3612,12 +3620,11 @@ static int nf_tables_commit(struct sk_buff *skb)
&te->elem, &te->elem,
NFT_MSG_DELSETELEM, 0); NFT_MSG_DELSETELEM, 0);
te->set->ops->get(te->set, &te->elem); te->set->ops->get(te->set, &te->elem);
te->set->ops->remove(te->set, &te->elem);
nft_data_uninit(&te->elem.key, NFT_DATA_VALUE); nft_data_uninit(&te->elem.key, NFT_DATA_VALUE);
if (te->elem.flags & NFT_SET_MAP) { if (te->set->flags & NFT_SET_MAP &&
nft_data_uninit(&te->elem.data, !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END))
te->set->dtype); nft_data_uninit(&te->elem.data, te->set->dtype);
} te->set->ops->remove(te->set, &te->elem);
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
} }
...@@ -3658,7 +3665,7 @@ static int nf_tables_abort(struct sk_buff *skb) ...@@ -3658,7 +3665,7 @@ static int nf_tables_abort(struct sk_buff *skb)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct nft_trans *trans, *next; struct nft_trans *trans, *next;
struct nft_set *set; struct nft_trans_elem *te;
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
switch (trans->msg_type) { switch (trans->msg_type) {
...@@ -3719,9 +3726,13 @@ static int nf_tables_abort(struct sk_buff *skb) ...@@ -3719,9 +3726,13 @@ static int nf_tables_abort(struct sk_buff *skb)
break; break;
case NFT_MSG_NEWSETELEM: case NFT_MSG_NEWSETELEM:
nft_trans_elem_set(trans)->nelems--; nft_trans_elem_set(trans)->nelems--;
set = nft_trans_elem_set(trans); te = (struct nft_trans_elem *)trans->data;
set->ops->get(set, &nft_trans_elem(trans)); te->set->ops->get(te->set, &te->elem);
set->ops->remove(set, &nft_trans_elem(trans)); nft_data_uninit(&te->elem.key, NFT_DATA_VALUE);
if (te->set->flags & NFT_SET_MAP &&
!(te->elem.flags & NFT_SET_ELEM_INTERVAL_END))
nft_data_uninit(&te->elem.data, te->set->dtype);
te->set->ops->remove(te->set, &te->elem);
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
case NFT_MSG_DELSETELEM: case NFT_MSG_DELSETELEM:
......
...@@ -123,7 +123,7 @@ static void ...@@ -123,7 +123,7 @@ static void
nft_target_set_tgchk_param(struct xt_tgchk_param *par, nft_target_set_tgchk_param(struct xt_tgchk_param *par,
const struct nft_ctx *ctx, const struct nft_ctx *ctx,
struct xt_target *target, void *info, struct xt_target *target, void *info,
union nft_entry *entry, u8 proto, bool inv) union nft_entry *entry, u16 proto, bool inv)
{ {
par->net = ctx->net; par->net = ctx->net;
par->table = ctx->table->name; par->table = ctx->table->name;
...@@ -137,7 +137,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par, ...@@ -137,7 +137,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par,
entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0; entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0;
break; break;
case NFPROTO_BRIDGE: case NFPROTO_BRIDGE:
entry->ebt.ethproto = proto; entry->ebt.ethproto = (__force __be16)proto;
entry->ebt.invflags = inv ? EBT_IPROTO : 0; entry->ebt.invflags = inv ? EBT_IPROTO : 0;
break; break;
} }
...@@ -171,7 +171,7 @@ static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1] ...@@ -171,7 +171,7 @@ static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1]
[NFTA_RULE_COMPAT_FLAGS] = { .type = NLA_U32 }, [NFTA_RULE_COMPAT_FLAGS] = { .type = NLA_U32 },
}; };
static int nft_parse_compat(const struct nlattr *attr, u8 *proto, bool *inv) static int nft_parse_compat(const struct nlattr *attr, u16 *proto, bool *inv)
{ {
struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1]; struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1];
u32 flags; u32 flags;
...@@ -203,7 +203,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr, ...@@ -203,7 +203,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
struct xt_target *target = expr->ops->data; struct xt_target *target = expr->ops->data;
struct xt_tgchk_param par; struct xt_tgchk_param par;
size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO])); size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO]));
u8 proto = 0; u16 proto = 0;
bool inv = false; bool inv = false;
union nft_entry e = {}; union nft_entry e = {};
int ret; int ret;
...@@ -334,7 +334,7 @@ static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX + 1] = { ...@@ -334,7 +334,7 @@ static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX + 1] = {
static void static void
nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,
struct xt_match *match, void *info, struct xt_match *match, void *info,
union nft_entry *entry, u8 proto, bool inv) union nft_entry *entry, u16 proto, bool inv)
{ {
par->net = ctx->net; par->net = ctx->net;
par->table = ctx->table->name; par->table = ctx->table->name;
...@@ -348,7 +348,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, ...@@ -348,7 +348,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,
entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0; entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0;
break; break;
case NFPROTO_BRIDGE: case NFPROTO_BRIDGE:
entry->ebt.ethproto = proto; entry->ebt.ethproto = (__force __be16)proto;
entry->ebt.invflags = inv ? EBT_IPROTO : 0; entry->ebt.invflags = inv ? EBT_IPROTO : 0;
break; break;
} }
...@@ -385,7 +385,7 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, ...@@ -385,7 +385,7 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
struct xt_match *match = expr->ops->data; struct xt_match *match = expr->ops->data;
struct xt_mtchk_param par; struct xt_mtchk_param par;
size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO])); size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO]));
u8 proto = 0; u16 proto = 0;
bool inv = false; bool inv = false;
union nft_entry e = {}; union nft_entry e = {};
int ret; int ret;
......
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