Skip to content

Commit

Permalink
netfilter: nf_tables: cancel tracking for clobbered destination regis…
Browse files Browse the repository at this point in the history
…ters

Output of expressions might be larger than one single register, this might
clobber existing data. Reset tracking for all destination registers that
required to store the expression output.

This patch adds three new helper functions:

- nft_reg_track_update: cancel previous register tracking and update it.
- nft_reg_track_cancel: cancel any previous register tracking info.
- __nft_reg_track_cancel: cancel only one single register tracking info.

Partial register clobbering detection is also supported by checking the
.num_reg field which describes the number of register that are used.

This patch updates the following expressions:

- meta_bridge
- bitwise
- byteorder
- meta
- payload

to use these helper functions.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
Pablo Neira Ayuso committed Mar 19, 2022
1 parent b2d3065 commit 34cc9e5
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 28 deletions.
14 changes: 14 additions & 0 deletions include/net/netfilter/nf_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ struct nft_regs_track {
struct {
const struct nft_expr *selector;
const struct nft_expr *bitwise;
u8 num_reg;
} regs[NFT_REG32_NUM];

const struct nft_expr *cur;
Expand Down Expand Up @@ -1641,4 +1642,17 @@ static inline bool nft_reduce_is_readonly(const struct nft_expr *expr)
return expr->ops->reduce == NFT_REDUCE_READONLY;
}

void nft_reg_track_update(struct nft_regs_track *track,
const struct nft_expr *expr, u8 dreg, u8 len);
void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len);
void __nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg);

static inline bool nft_reg_track_cmp(struct nft_regs_track *track,
const struct nft_expr *expr, u8 dreg)
{
return track->regs[dreg].selector &&
track->regs[dreg].selector->ops == expr->ops &&
track->regs[dreg].num_reg == 0;
}

#endif /* _NET_NF_TABLES_H */
1 change: 1 addition & 0 deletions include/net/netfilter/nft_meta.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

struct nft_meta {
enum nft_meta_keys key:8;
u8 len;
union {
u8 dreg;
u8 sreg;
Expand Down
4 changes: 2 additions & 2 deletions net/bridge/netfilter/nft_meta_bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ static int nft_meta_bridge_get_init(const struct nft_ctx *ctx,
return nft_meta_get_init(ctx, expr, tb);
}

priv->len = len;
return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg,
NULL, NFT_DATA_VALUE, len);
}
Expand All @@ -112,8 +113,7 @@ static bool nft_meta_bridge_set_reduce(struct nft_regs_track *track,
if (track->regs[i].selector->ops != &nft_meta_bridge_get_ops)
continue;

track->regs[i].selector = NULL;
track->regs[i].bitwise = NULL;
__nft_reg_track_cancel(track, i);
}

return false;
Expand Down
52 changes: 52 additions & 0 deletions net/netfilter/nf_tables_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,58 @@ static int nft_delflowtable(struct nft_ctx *ctx,
return err;
}

static void __nft_reg_track_clobber(struct nft_regs_track *track, u8 dreg)
{
int i;

for (i = track->regs[dreg].num_reg; i > 0; i--)
__nft_reg_track_cancel(track, dreg - i);
}

static void __nft_reg_track_update(struct nft_regs_track *track,
const struct nft_expr *expr,
u8 dreg, u8 num_reg)
{
track->regs[dreg].selector = expr;
track->regs[dreg].bitwise = NULL;
track->regs[dreg].num_reg = num_reg;
}

void nft_reg_track_update(struct nft_regs_track *track,
const struct nft_expr *expr, u8 dreg, u8 len)
{
unsigned int regcount;
int i;

__nft_reg_track_clobber(track, dreg);

regcount = DIV_ROUND_UP(len, NFT_REG32_SIZE);
for (i = 0; i < regcount; i++, dreg++)
__nft_reg_track_update(track, expr, dreg, i);
}
EXPORT_SYMBOL_GPL(nft_reg_track_update);

void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len)
{
unsigned int regcount;
int i;

__nft_reg_track_clobber(track, dreg);

regcount = DIV_ROUND_UP(len, NFT_REG32_SIZE);
for (i = 0; i < regcount; i++, dreg++)
__nft_reg_track_cancel(track, dreg);
}
EXPORT_SYMBOL_GPL(nft_reg_track_cancel);

void __nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg)
{
track->regs[dreg].selector = NULL;
track->regs[dreg].bitwise = NULL;
track->regs[dreg].num_reg = 0;
}
EXPORT_SYMBOL_GPL(__nft_reg_track_cancel);

/*
* Tables
*/
Expand Down
23 changes: 15 additions & 8 deletions net/netfilter/nft_bitwise.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,16 @@ static bool nft_bitwise_reduce(struct nft_regs_track *track,
{
const struct nft_bitwise *priv = nft_expr_priv(expr);
const struct nft_bitwise *bitwise;
unsigned int regcount;
u8 dreg;
int i;

if (!track->regs[priv->sreg].selector)
return false;

bitwise = nft_expr_priv(expr);
if (track->regs[priv->sreg].selector == track->regs[priv->dreg].selector &&
track->regs[priv->sreg].num_reg == 0 &&
track->regs[priv->dreg].bitwise &&
track->regs[priv->dreg].bitwise->ops == expr->ops &&
priv->sreg == bitwise->sreg &&
Expand All @@ -302,17 +306,21 @@ static bool nft_bitwise_reduce(struct nft_regs_track *track,
return true;
}

if (track->regs[priv->sreg].bitwise) {
track->regs[priv->dreg].selector = NULL;
track->regs[priv->dreg].bitwise = NULL;
if (track->regs[priv->sreg].bitwise ||
track->regs[priv->sreg].num_reg != 0) {
nft_reg_track_cancel(track, priv->dreg, priv->len);
return false;
}

if (priv->sreg != priv->dreg) {
track->regs[priv->dreg].selector =
track->regs[priv->sreg].selector;
nft_reg_track_update(track, track->regs[priv->sreg].selector,
priv->dreg, priv->len);
}
track->regs[priv->dreg].bitwise = expr;

dreg = priv->dreg;
regcount = DIV_ROUND_UP(priv->len, NFT_REG32_SIZE);
for (i = 0; i < regcount; i++, dreg++)
track->regs[priv->dreg].bitwise = expr;

return false;
}
Expand Down Expand Up @@ -447,8 +455,7 @@ static bool nft_bitwise_fast_reduce(struct nft_regs_track *track,
}

if (track->regs[priv->sreg].bitwise) {
track->regs[priv->dreg].selector = NULL;
track->regs[priv->dreg].bitwise = NULL;
nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE);
return false;
}

Expand Down
3 changes: 1 addition & 2 deletions net/netfilter/nft_byteorder.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,7 @@ static bool nft_byteorder_reduce(struct nft_regs_track *track,
{
struct nft_byteorder *priv = nft_expr_priv(expr);

track->regs[priv->dreg].selector = NULL;
track->regs[priv->dreg].bitwise = NULL;
nft_reg_track_cancel(track, priv->dreg, priv->len);

return false;
}
Expand Down
14 changes: 6 additions & 8 deletions net/netfilter/nft_meta.c
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
return -EOPNOTSUPP;
}

priv->len = len;
return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg,
NULL, NFT_DATA_VALUE, len);
}
Expand Down Expand Up @@ -664,6 +665,7 @@ int nft_meta_set_init(const struct nft_ctx *ctx,
return -EOPNOTSUPP;
}

priv->len = len;
err = nft_parse_register_load(tb[NFTA_META_SREG], &priv->sreg, len);
if (err < 0)
return err;
Expand Down Expand Up @@ -756,18 +758,15 @@ static bool nft_meta_get_reduce(struct nft_regs_track *track,
const struct nft_meta *priv = nft_expr_priv(expr);
const struct nft_meta *meta;

if (!track->regs[priv->dreg].selector ||
track->regs[priv->dreg].selector->ops != expr->ops) {
track->regs[priv->dreg].selector = expr;
track->regs[priv->dreg].bitwise = NULL;
if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}

meta = nft_expr_priv(track->regs[priv->dreg].selector);
if (priv->key != meta->key ||
priv->dreg != meta->dreg) {
track->regs[priv->dreg].selector = expr;
track->regs[priv->dreg].bitwise = NULL;
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}

Expand Down Expand Up @@ -800,8 +799,7 @@ static bool nft_meta_set_reduce(struct nft_regs_track *track,
if (track->regs[i].selector->ops != &nft_meta_get_ops)
continue;

track->regs[i].selector = NULL;
track->regs[i].bitwise = NULL;
__nft_reg_track_cancel(track, i);
}

return false;
Expand Down
12 changes: 4 additions & 8 deletions net/netfilter/nft_payload.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,19 +216,16 @@ static bool nft_payload_reduce(struct nft_regs_track *track,
const struct nft_payload *priv = nft_expr_priv(expr);
const struct nft_payload *payload;

if (!track->regs[priv->dreg].selector ||
track->regs[priv->dreg].selector->ops != expr->ops) {
track->regs[priv->dreg].selector = expr;
track->regs[priv->dreg].bitwise = NULL;
if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}

payload = nft_expr_priv(track->regs[priv->dreg].selector);
if (priv->base != payload->base ||
priv->offset != payload->offset ||
priv->len != payload->len) {
track->regs[priv->dreg].selector = expr;
track->regs[priv->dreg].bitwise = NULL;
nft_reg_track_update(track, expr, priv->dreg, priv->len);
return false;
}

Expand Down Expand Up @@ -815,8 +812,7 @@ static bool nft_payload_set_reduce(struct nft_regs_track *track,
track->regs[i].selector->ops != &nft_payload_fast_ops)
continue;

track->regs[i].selector = NULL;
track->regs[i].bitwise = NULL;
__nft_reg_track_cancel(track, i);
}

return false;
Expand Down

0 comments on commit 34cc9e5

Please sign in to comment.