未验证 提交 11a489df 编写于 作者: O openeuler-ci-bot 提交者: Gitee

!1463 [sync] PR-1436: Fix CVE-2023-3117

Merge Pull Request from: @openeuler-sync-bot 
 

Origin pull request: 
https://gitee.com/openeuler/kernel/pulls/1436 
 
Pablo Neira Ayuso (4):
  netfilter: nf_tables: incorrect error path handling with NFT_MSG_NEWRULE
  netfilter: nf_tables: fix chain binding transaction logic
  netfilter: nf_tables: add NFT_TRANS_PREPARE_ERROR to deal with bound set/chain
  netfilter: nf_tables: unbind non-anonymous set if rule construction fails

https://gitee.com/src-openeuler/kernel/issues/I7H68N 
 
Link:https://gitee.com/openeuler/kernel/pulls/1463 

Reviewed-by: Yue Haibing <yuehaibing@huawei.com> 
Signed-off-by: Jialin Zhang <zhangjialin11@huawei.com> 
...@@ -778,6 +778,7 @@ struct nft_expr_type { ...@@ -778,6 +778,7 @@ struct nft_expr_type {
enum nft_trans_phase { enum nft_trans_phase {
NFT_TRANS_PREPARE, NFT_TRANS_PREPARE,
NFT_TRANS_PREPARE_ERROR,
NFT_TRANS_ABORT, NFT_TRANS_ABORT,
NFT_TRANS_COMMIT, NFT_TRANS_COMMIT,
NFT_TRANS_RELEASE NFT_TRANS_RELEASE
...@@ -908,7 +909,10 @@ static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule) ...@@ -908,7 +909,10 @@ static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule)
return (void *)&rule->data[rule->dlen]; return (void *)&rule->data[rule->dlen];
} }
void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule); void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule);
void nft_rule_expr_deactivate(const struct nft_ctx *ctx, struct nft_rule *rule,
enum nft_trans_phase phase);
void nf_tables_rule_destroy(const struct nft_ctx *ctx, struct nft_rule *rule);
static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext, static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext,
struct nft_regs *regs, struct nft_regs *regs,
...@@ -967,6 +971,8 @@ struct nft_chain { ...@@ -967,6 +971,8 @@ struct nft_chain {
}; };
int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain); int nft_chain_validate(const struct nft_ctx *ctx, const struct nft_chain *chain);
int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain);
enum nft_chain_types { enum nft_chain_types {
NFT_CHAIN_T_DEFAULT = 0, NFT_CHAIN_T_DEFAULT = 0,
...@@ -1003,11 +1009,17 @@ int nft_chain_validate_dependency(const struct nft_chain *chain, ...@@ -1003,11 +1009,17 @@ int nft_chain_validate_dependency(const struct nft_chain *chain,
int nft_chain_validate_hooks(const struct nft_chain *chain, int nft_chain_validate_hooks(const struct nft_chain *chain,
unsigned int hook_flags); unsigned int hook_flags);
static inline bool nft_chain_binding(const struct nft_chain *chain)
{
return chain->flags & NFT_CHAIN_BINDING;
}
static inline bool nft_chain_is_bound(struct nft_chain *chain) static inline bool nft_chain_is_bound(struct nft_chain *chain)
{ {
return (chain->flags & NFT_CHAIN_BINDING) && chain->bound; return (chain->flags & NFT_CHAIN_BINDING) && chain->bound;
} }
int nft_chain_add(struct nft_table *table, struct nft_chain *chain);
void nft_chain_del(struct nft_chain *chain); void nft_chain_del(struct nft_chain *chain);
void nf_tables_chain_destroy(struct nft_ctx *ctx); void nf_tables_chain_destroy(struct nft_ctx *ctx);
...@@ -1432,6 +1444,7 @@ struct nft_trans_rule { ...@@ -1432,6 +1444,7 @@ struct nft_trans_rule {
struct nft_rule *rule; struct nft_rule *rule;
struct nft_flow_rule *flow; struct nft_flow_rule *flow;
u32 rule_id; u32 rule_id;
bool bound;
}; };
#define nft_trans_rule(trans) \ #define nft_trans_rule(trans) \
...@@ -1440,6 +1453,8 @@ struct nft_trans_rule { ...@@ -1440,6 +1453,8 @@ struct nft_trans_rule {
(((struct nft_trans_rule *)trans->data)->flow) (((struct nft_trans_rule *)trans->data)->flow)
#define nft_trans_rule_id(trans) \ #define nft_trans_rule_id(trans) \
(((struct nft_trans_rule *)trans->data)->rule_id) (((struct nft_trans_rule *)trans->data)->rule_id)
#define nft_trans_rule_bound(trans) \
(((struct nft_trans_rule *)trans->data)->bound)
struct nft_trans_set { struct nft_trans_set {
struct nft_set *set; struct nft_set *set;
...@@ -1455,13 +1470,17 @@ struct nft_trans_set { ...@@ -1455,13 +1470,17 @@ struct nft_trans_set {
(((struct nft_trans_set *)trans->data)->bound) (((struct nft_trans_set *)trans->data)->bound)
struct nft_trans_chain { struct nft_trans_chain {
struct nft_chain *chain;
bool update; bool update;
char *name; char *name;
struct nft_stats __percpu *stats; struct nft_stats __percpu *stats;
u8 policy; u8 policy;
bool bound;
u32 chain_id; u32 chain_id;
}; };
#define nft_trans_chain(trans) \
(((struct nft_trans_chain *)trans->data)->chain)
#define nft_trans_chain_update(trans) \ #define nft_trans_chain_update(trans) \
(((struct nft_trans_chain *)trans->data)->update) (((struct nft_trans_chain *)trans->data)->update)
#define nft_trans_chain_name(trans) \ #define nft_trans_chain_name(trans) \
...@@ -1470,6 +1489,8 @@ struct nft_trans_chain { ...@@ -1470,6 +1489,8 @@ struct nft_trans_chain {
(((struct nft_trans_chain *)trans->data)->stats) (((struct nft_trans_chain *)trans->data)->stats)
#define nft_trans_chain_policy(trans) \ #define nft_trans_chain_policy(trans) \
(((struct nft_trans_chain *)trans->data)->policy) (((struct nft_trans_chain *)trans->data)->policy)
#define nft_trans_chain_bound(trans) \
(((struct nft_trans_chain *)trans->data)->bound)
#define nft_trans_chain_id(trans) \ #define nft_trans_chain_id(trans) \
(((struct nft_trans_chain *)trans->data)->chain_id) (((struct nft_trans_chain *)trans->data)->chain_id)
......
...@@ -168,7 +168,8 @@ static void nft_trans_destroy(struct nft_trans *trans) ...@@ -168,7 +168,8 @@ static void nft_trans_destroy(struct nft_trans *trans)
kfree(trans); kfree(trans);
} }
static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) static void __nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set,
bool bind)
{ {
struct net *net = ctx->net; struct net *net = ctx->net;
struct nft_trans *trans; struct nft_trans *trans;
...@@ -180,16 +181,78 @@ static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set) ...@@ -180,16 +181,78 @@ static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
switch (trans->msg_type) { switch (trans->msg_type) {
case NFT_MSG_NEWSET: case NFT_MSG_NEWSET:
if (nft_trans_set(trans) == set) if (nft_trans_set(trans) == set)
nft_trans_set_bound(trans) = true; nft_trans_set_bound(trans) = bind;
break; break;
case NFT_MSG_NEWSETELEM: case NFT_MSG_NEWSETELEM:
if (nft_trans_elem_set(trans) == set) if (nft_trans_elem_set(trans) == set)
nft_trans_elem_set_bound(trans) = true; nft_trans_elem_set_bound(trans) = bind;
break;
}
}
}
static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
{
return __nft_set_trans_bind(ctx, set, true);
}
static void nft_set_trans_unbind(const struct nft_ctx *ctx, struct nft_set *set)
{
return __nft_set_trans_bind(ctx, set, false);
}
static void __nft_chain_trans_bind(const struct nft_ctx *ctx,
struct nft_chain *chain, bool bind)
{
struct net *net = ctx->net;
struct nft_trans *trans;
if (!nft_chain_binding(chain))
return;
list_for_each_entry_reverse(trans, &net->nft.commit_list, list) {
switch (trans->msg_type) {
case NFT_MSG_NEWCHAIN:
if (nft_trans_chain(trans) == chain)
nft_trans_chain_bound(trans) = bind;
break;
case NFT_MSG_NEWRULE:
if (trans->ctx.chain == chain)
nft_trans_rule_bound(trans) = bind;
break; break;
} }
} }
} }
static void nft_chain_trans_bind(const struct nft_ctx *ctx,
struct nft_chain *chain)
{
__nft_chain_trans_bind(ctx, chain, true);
}
int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain)
{
if (!nft_chain_binding(chain))
return 0;
if (nft_chain_binding(ctx->chain))
return -EOPNOTSUPP;
if (chain->bound)
return -EBUSY;
chain->bound = true;
chain->use++;
nft_chain_trans_bind(ctx, chain);
return 0;
}
void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain)
{
__nft_chain_trans_bind(ctx, chain, false);
}
static int nft_netdev_register_hooks(struct net *net, static int nft_netdev_register_hooks(struct net *net,
struct list_head *hook_list) struct list_head *hook_list)
{ {
...@@ -313,8 +376,9 @@ static struct nft_trans *nft_trans_chain_add(struct nft_ctx *ctx, int msg_type) ...@@ -313,8 +376,9 @@ static struct nft_trans *nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
ntohl(nla_get_be32(ctx->nla[NFTA_CHAIN_ID])); ntohl(nla_get_be32(ctx->nla[NFTA_CHAIN_ID]));
} }
} }
nft_trans_chain(trans) = ctx->chain;
list_add_tail(&trans->list, &ctx->net->nft.commit_list); list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return trans; return trans;
} }
...@@ -332,8 +396,7 @@ static int nft_delchain(struct nft_ctx *ctx) ...@@ -332,8 +396,7 @@ static int nft_delchain(struct nft_ctx *ctx)
return 0; return 0;
} }
static void nft_rule_expr_activate(const struct nft_ctx *ctx, void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule)
struct nft_rule *rule)
{ {
struct nft_expr *expr; struct nft_expr *expr;
...@@ -346,9 +409,8 @@ static void nft_rule_expr_activate(const struct nft_ctx *ctx, ...@@ -346,9 +409,8 @@ static void nft_rule_expr_activate(const struct nft_ctx *ctx,
} }
} }
static void nft_rule_expr_deactivate(const struct nft_ctx *ctx, void nft_rule_expr_deactivate(const struct nft_ctx *ctx, struct nft_rule *rule,
struct nft_rule *rule, enum nft_trans_phase phase)
enum nft_trans_phase phase)
{ {
struct nft_expr *expr; struct nft_expr *expr;
...@@ -1981,7 +2043,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family, ...@@ -1981,7 +2043,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
return 0; return 0;
} }
static int nft_chain_add(struct nft_table *table, struct nft_chain *chain) int nft_chain_add(struct nft_table *table, struct nft_chain *chain)
{ {
int err; int err;
...@@ -3076,8 +3138,7 @@ static int nf_tables_getrule(struct net *net, struct sock *nlsk, ...@@ -3076,8 +3138,7 @@ static int nf_tables_getrule(struct net *net, struct sock *nlsk,
return err; return err;
} }
static void nf_tables_rule_destroy(const struct nft_ctx *ctx, void nf_tables_rule_destroy(const struct nft_ctx *ctx, struct nft_rule *rule)
struct nft_rule *rule)
{ {
struct nft_expr *expr, *next; struct nft_expr *expr, *next;
...@@ -3094,7 +3155,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx, ...@@ -3094,7 +3155,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
kfree(rule); kfree(rule);
} }
void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule) static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule)
{ {
nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE); nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE);
nf_tables_rule_destroy(ctx, rule); nf_tables_rule_destroy(ctx, rule);
...@@ -3364,7 +3425,8 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk, ...@@ -3364,7 +3425,8 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
return 0; return 0;
err2: err2:
nf_tables_rule_release(&ctx, rule); nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE_ERROR);
nf_tables_rule_destroy(&ctx, rule);
err1: err1:
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (info[i].ops) { if (info[i].ops) {
...@@ -4493,6 +4555,15 @@ void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -4493,6 +4555,15 @@ void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,
enum nft_trans_phase phase) enum nft_trans_phase phase)
{ {
switch (phase) { switch (phase) {
case NFT_TRANS_PREPARE_ERROR:
nft_set_trans_unbind(ctx, set);
if (nft_set_is_anonymous(set))
nft_deactivate_next(ctx->net, set);
else
list_del_rcu(&binding->list);
set->use--;
break;
case NFT_TRANS_PREPARE: case NFT_TRANS_PREPARE:
if (nft_set_is_anonymous(set)) if (nft_set_is_anonymous(set))
nft_deactivate_next(ctx->net, set); nft_deactivate_next(ctx->net, set);
...@@ -5489,7 +5560,6 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk, ...@@ -5489,7 +5560,6 @@ static int nf_tables_newsetelem(struct net *net, struct sock *nlsk,
void nft_data_hold(const struct nft_data *data, enum nft_data_types type) void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
{ {
struct nft_chain *chain; struct nft_chain *chain;
struct nft_rule *rule;
if (type == NFT_DATA_VERDICT) { if (type == NFT_DATA_VERDICT) {
switch (data->verdict.code) { switch (data->verdict.code) {
...@@ -5497,15 +5567,6 @@ void nft_data_hold(const struct nft_data *data, enum nft_data_types type) ...@@ -5497,15 +5567,6 @@ void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
case NFT_GOTO: case NFT_GOTO:
chain = data->verdict.chain; chain = data->verdict.chain;
chain->use++; chain->use++;
if (!nft_chain_is_bound(chain))
break;
chain->table->use++;
list_for_each_entry(rule, &chain->rules, list)
chain->use++;
nft_chain_add(chain->table, chain);
break; break;
} }
} }
...@@ -6429,6 +6490,7 @@ void nf_tables_deactivate_flowtable(const struct nft_ctx *ctx, ...@@ -6429,6 +6490,7 @@ void nf_tables_deactivate_flowtable(const struct nft_ctx *ctx,
enum nft_trans_phase phase) enum nft_trans_phase phase)
{ {
switch (phase) { switch (phase) {
case NFT_TRANS_PREPARE_ERROR:
case NFT_TRANS_PREPARE: case NFT_TRANS_PREPARE:
case NFT_TRANS_ABORT: case NFT_TRANS_ABORT:
case NFT_TRANS_RELEASE: case NFT_TRANS_RELEASE:
...@@ -8171,7 +8233,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) ...@@ -8171,7 +8233,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
kfree(nft_trans_chain_name(trans)); kfree(nft_trans_chain_name(trans));
nft_trans_destroy(trans); nft_trans_destroy(trans);
} else { } else {
if (nft_chain_is_bound(trans->ctx.chain)) { if (nft_trans_chain_bound(trans)) {
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
} }
...@@ -8188,6 +8250,10 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) ...@@ -8188,6 +8250,10 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
case NFT_MSG_NEWRULE: case NFT_MSG_NEWRULE:
if (nft_trans_rule_bound(trans)) {
nft_trans_destroy(trans);
break;
}
trans->ctx.chain->use--; trans->ctx.chain->use--;
list_del_rcu(&nft_trans_rule(trans)->list); list_del_rcu(&nft_trans_rule(trans)->list);
nft_rule_expr_deactivate(&trans->ctx, nft_rule_expr_deactivate(&trans->ctx,
...@@ -8723,22 +8789,12 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, ...@@ -8723,22 +8789,12 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
static void nft_verdict_uninit(const struct nft_data *data) static void nft_verdict_uninit(const struct nft_data *data)
{ {
struct nft_chain *chain; struct nft_chain *chain;
struct nft_rule *rule;
switch (data->verdict.code) { switch (data->verdict.code) {
case NFT_JUMP: case NFT_JUMP:
case NFT_GOTO: case NFT_GOTO:
chain = data->verdict.chain; chain = data->verdict.chain;
chain->use--; chain->use--;
if (!nft_chain_is_bound(chain))
break;
chain->table->use--;
list_for_each_entry(rule, &chain->rules, list)
chain->use--;
nft_chain_del(chain);
break; break;
} }
} }
......
...@@ -76,11 +76,9 @@ static int nft_immediate_init(const struct nft_ctx *ctx, ...@@ -76,11 +76,9 @@ static int nft_immediate_init(const struct nft_ctx *ctx,
switch (priv->data.verdict.code) { switch (priv->data.verdict.code) {
case NFT_JUMP: case NFT_JUMP:
case NFT_GOTO: case NFT_GOTO:
if (nft_chain_is_bound(chain)) { err = nf_tables_bind_chain(ctx, chain);
err = -EBUSY; if (err < 0)
goto err1; return err;
}
chain->bound = true;
break; break;
default: default:
break; break;
...@@ -98,6 +96,31 @@ static void nft_immediate_activate(const struct nft_ctx *ctx, ...@@ -98,6 +96,31 @@ static void nft_immediate_activate(const struct nft_ctx *ctx,
const struct nft_expr *expr) const struct nft_expr *expr)
{ {
const struct nft_immediate_expr *priv = nft_expr_priv(expr); const struct nft_immediate_expr *priv = nft_expr_priv(expr);
const struct nft_data *data = &priv->data;
struct nft_ctx chain_ctx;
struct nft_chain *chain;
struct nft_rule *rule;
if (priv->dreg == NFT_REG_VERDICT) {
switch (data->verdict.code) {
case NFT_JUMP:
case NFT_GOTO:
chain = data->verdict.chain;
if (!nft_chain_binding(chain))
break;
chain_ctx = *ctx;
chain_ctx.chain = chain;
list_for_each_entry(rule, &chain->rules, list)
nft_rule_expr_activate(&chain_ctx, rule);
nft_clear(ctx->net, chain);
break;
default:
break;
}
}
return nft_data_hold(&priv->data, nft_dreg_to_type(priv->dreg)); return nft_data_hold(&priv->data, nft_dreg_to_type(priv->dreg));
} }
...@@ -107,6 +130,43 @@ static void nft_immediate_deactivate(const struct nft_ctx *ctx, ...@@ -107,6 +130,43 @@ static void nft_immediate_deactivate(const struct nft_ctx *ctx,
enum nft_trans_phase phase) enum nft_trans_phase phase)
{ {
const struct nft_immediate_expr *priv = nft_expr_priv(expr); const struct nft_immediate_expr *priv = nft_expr_priv(expr);
const struct nft_data *data = &priv->data;
struct nft_ctx chain_ctx;
struct nft_chain *chain;
struct nft_rule *rule;
if (priv->dreg == NFT_REG_VERDICT) {
switch (data->verdict.code) {
case NFT_JUMP:
case NFT_GOTO:
chain = data->verdict.chain;
if (!nft_chain_binding(chain))
break;
chain_ctx = *ctx;
chain_ctx.chain = chain;
list_for_each_entry(rule, &chain->rules, list)
nft_rule_expr_deactivate(&chain_ctx, rule, phase);
switch (phase) {
case NFT_TRANS_PREPARE_ERROR:
nf_tables_unbind_chain(ctx, chain);
fallthrough;
case NFT_TRANS_PREPARE:
nft_deactivate_next(ctx->net, chain);
break;
default:
nft_chain_del(chain);
chain->bound = false;
chain->table->use--;
break;
}
break;
default:
break;
}
}
if (phase == NFT_TRANS_COMMIT) if (phase == NFT_TRANS_COMMIT)
return; return;
...@@ -131,15 +191,27 @@ static void nft_immediate_destroy(const struct nft_ctx *ctx, ...@@ -131,15 +191,27 @@ static void nft_immediate_destroy(const struct nft_ctx *ctx,
case NFT_GOTO: case NFT_GOTO:
chain = data->verdict.chain; chain = data->verdict.chain;
if (!nft_chain_is_bound(chain)) if (!nft_chain_binding(chain))
break;
/* Rule construction failed, but chain is already bound:
* let the transaction records release this chain and its rules.
*/
if (chain->bound) {
chain->use--;
break; break;
}
/* Rule has been deleted, release chain and its rules. */
chain_ctx = *ctx; chain_ctx = *ctx;
chain_ctx.chain = chain; chain_ctx.chain = chain;
list_for_each_entry_safe(rule, n, &chain->rules, list) chain->use--;
nf_tables_rule_release(&chain_ctx, rule); list_for_each_entry_safe(rule, n, &chain->rules, list) {
chain->use--;
list_del(&rule->list);
nf_tables_rule_destroy(&chain_ctx, rule);
}
nf_tables_chain_destroy(&chain_ctx); nf_tables_chain_destroy(&chain_ctx);
break; break;
default: default:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册