Skip to content

Commit 34cc9e5

Browse files
committed
netfilter: nf_tables: cancel tracking for clobbered destination registers
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>
1 parent b2d3065 commit 34cc9e5

File tree

8 files changed

+95
-28
lines changed

8 files changed

+95
-28
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ struct nft_regs_track {
126126
struct {
127127
const struct nft_expr *selector;
128128
const struct nft_expr *bitwise;
129+
u8 num_reg;
129130
} regs[NFT_REG32_NUM];
130131

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

1645+
void nft_reg_track_update(struct nft_regs_track *track,
1646+
const struct nft_expr *expr, u8 dreg, u8 len);
1647+
void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len);
1648+
void __nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg);
1649+
1650+
static inline bool nft_reg_track_cmp(struct nft_regs_track *track,
1651+
const struct nft_expr *expr, u8 dreg)
1652+
{
1653+
return track->regs[dreg].selector &&
1654+
track->regs[dreg].selector->ops == expr->ops &&
1655+
track->regs[dreg].num_reg == 0;
1656+
}
1657+
16441658
#endif /* _NET_NF_TABLES_H */

include/net/netfilter/nft_meta.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
struct nft_meta {
88
enum nft_meta_keys key:8;
9+
u8 len;
910
union {
1011
u8 dreg;
1112
u8 sreg;

net/bridge/netfilter/nft_meta_bridge.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ static int nft_meta_bridge_get_init(const struct nft_ctx *ctx,
8787
return nft_meta_get_init(ctx, expr, tb);
8888
}
8989

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

115-
track->regs[i].selector = NULL;
116-
track->regs[i].bitwise = NULL;
116+
__nft_reg_track_cancel(track, i);
117117
}
118118

119119
return false;

net/netfilter/nf_tables_api.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,58 @@ static int nft_delflowtable(struct nft_ctx *ctx,
550550
return err;
551551
}
552552

553+
static void __nft_reg_track_clobber(struct nft_regs_track *track, u8 dreg)
554+
{
555+
int i;
556+
557+
for (i = track->regs[dreg].num_reg; i > 0; i--)
558+
__nft_reg_track_cancel(track, dreg - i);
559+
}
560+
561+
static void __nft_reg_track_update(struct nft_regs_track *track,
562+
const struct nft_expr *expr,
563+
u8 dreg, u8 num_reg)
564+
{
565+
track->regs[dreg].selector = expr;
566+
track->regs[dreg].bitwise = NULL;
567+
track->regs[dreg].num_reg = num_reg;
568+
}
569+
570+
void nft_reg_track_update(struct nft_regs_track *track,
571+
const struct nft_expr *expr, u8 dreg, u8 len)
572+
{
573+
unsigned int regcount;
574+
int i;
575+
576+
__nft_reg_track_clobber(track, dreg);
577+
578+
regcount = DIV_ROUND_UP(len, NFT_REG32_SIZE);
579+
for (i = 0; i < regcount; i++, dreg++)
580+
__nft_reg_track_update(track, expr, dreg, i);
581+
}
582+
EXPORT_SYMBOL_GPL(nft_reg_track_update);
583+
584+
void nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg, u8 len)
585+
{
586+
unsigned int regcount;
587+
int i;
588+
589+
__nft_reg_track_clobber(track, dreg);
590+
591+
regcount = DIV_ROUND_UP(len, NFT_REG32_SIZE);
592+
for (i = 0; i < regcount; i++, dreg++)
593+
__nft_reg_track_cancel(track, dreg);
594+
}
595+
EXPORT_SYMBOL_GPL(nft_reg_track_cancel);
596+
597+
void __nft_reg_track_cancel(struct nft_regs_track *track, u8 dreg)
598+
{
599+
track->regs[dreg].selector = NULL;
600+
track->regs[dreg].bitwise = NULL;
601+
track->regs[dreg].num_reg = 0;
602+
}
603+
EXPORT_SYMBOL_GPL(__nft_reg_track_cancel);
604+
553605
/*
554606
* Tables
555607
*/

net/netfilter/nft_bitwise.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -283,12 +283,16 @@ static bool nft_bitwise_reduce(struct nft_regs_track *track,
283283
{
284284
const struct nft_bitwise *priv = nft_expr_priv(expr);
285285
const struct nft_bitwise *bitwise;
286+
unsigned int regcount;
287+
u8 dreg;
288+
int i;
286289

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

290293
bitwise = nft_expr_priv(expr);
291294
if (track->regs[priv->sreg].selector == track->regs[priv->dreg].selector &&
295+
track->regs[priv->sreg].num_reg == 0 &&
292296
track->regs[priv->dreg].bitwise &&
293297
track->regs[priv->dreg].bitwise->ops == expr->ops &&
294298
priv->sreg == bitwise->sreg &&
@@ -302,17 +306,21 @@ static bool nft_bitwise_reduce(struct nft_regs_track *track,
302306
return true;
303307
}
304308

305-
if (track->regs[priv->sreg].bitwise) {
306-
track->regs[priv->dreg].selector = NULL;
307-
track->regs[priv->dreg].bitwise = NULL;
309+
if (track->regs[priv->sreg].bitwise ||
310+
track->regs[priv->sreg].num_reg != 0) {
311+
nft_reg_track_cancel(track, priv->dreg, priv->len);
308312
return false;
309313
}
310314

311315
if (priv->sreg != priv->dreg) {
312-
track->regs[priv->dreg].selector =
313-
track->regs[priv->sreg].selector;
316+
nft_reg_track_update(track, track->regs[priv->sreg].selector,
317+
priv->dreg, priv->len);
314318
}
315-
track->regs[priv->dreg].bitwise = expr;
319+
320+
dreg = priv->dreg;
321+
regcount = DIV_ROUND_UP(priv->len, NFT_REG32_SIZE);
322+
for (i = 0; i < regcount; i++, dreg++)
323+
track->regs[priv->dreg].bitwise = expr;
316324

317325
return false;
318326
}
@@ -447,8 +455,7 @@ static bool nft_bitwise_fast_reduce(struct nft_regs_track *track,
447455
}
448456

449457
if (track->regs[priv->sreg].bitwise) {
450-
track->regs[priv->dreg].selector = NULL;
451-
track->regs[priv->dreg].bitwise = NULL;
458+
nft_reg_track_cancel(track, priv->dreg, NFT_REG32_SIZE);
452459
return false;
453460
}
454461

net/netfilter/nft_byteorder.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,7 @@ static bool nft_byteorder_reduce(struct nft_regs_track *track,
172172
{
173173
struct nft_byteorder *priv = nft_expr_priv(expr);
174174

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

178177
return false;
179178
}

net/netfilter/nft_meta.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
539539
return -EOPNOTSUPP;
540540
}
541541

542+
priv->len = len;
542543
return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg,
543544
NULL, NFT_DATA_VALUE, len);
544545
}
@@ -664,6 +665,7 @@ int nft_meta_set_init(const struct nft_ctx *ctx,
664665
return -EOPNOTSUPP;
665666
}
666667

668+
priv->len = len;
667669
err = nft_parse_register_load(tb[NFTA_META_SREG], &priv->sreg, len);
668670
if (err < 0)
669671
return err;
@@ -756,18 +758,15 @@ static bool nft_meta_get_reduce(struct nft_regs_track *track,
756758
const struct nft_meta *priv = nft_expr_priv(expr);
757759
const struct nft_meta *meta;
758760

759-
if (!track->regs[priv->dreg].selector ||
760-
track->regs[priv->dreg].selector->ops != expr->ops) {
761-
track->regs[priv->dreg].selector = expr;
762-
track->regs[priv->dreg].bitwise = NULL;
761+
if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
762+
nft_reg_track_update(track, expr, priv->dreg, priv->len);
763763
return false;
764764
}
765765

766766
meta = nft_expr_priv(track->regs[priv->dreg].selector);
767767
if (priv->key != meta->key ||
768768
priv->dreg != meta->dreg) {
769-
track->regs[priv->dreg].selector = expr;
770-
track->regs[priv->dreg].bitwise = NULL;
769+
nft_reg_track_update(track, expr, priv->dreg, priv->len);
771770
return false;
772771
}
773772

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

803-
track->regs[i].selector = NULL;
804-
track->regs[i].bitwise = NULL;
802+
__nft_reg_track_cancel(track, i);
805803
}
806804

807805
return false;

net/netfilter/nft_payload.c

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -216,19 +216,16 @@ static bool nft_payload_reduce(struct nft_regs_track *track,
216216
const struct nft_payload *priv = nft_expr_priv(expr);
217217
const struct nft_payload *payload;
218218

219-
if (!track->regs[priv->dreg].selector ||
220-
track->regs[priv->dreg].selector->ops != expr->ops) {
221-
track->regs[priv->dreg].selector = expr;
222-
track->regs[priv->dreg].bitwise = NULL;
219+
if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
220+
nft_reg_track_update(track, expr, priv->dreg, priv->len);
223221
return false;
224222
}
225223

226224
payload = nft_expr_priv(track->regs[priv->dreg].selector);
227225
if (priv->base != payload->base ||
228226
priv->offset != payload->offset ||
229227
priv->len != payload->len) {
230-
track->regs[priv->dreg].selector = expr;
231-
track->regs[priv->dreg].bitwise = NULL;
228+
nft_reg_track_update(track, expr, priv->dreg, priv->len);
232229
return false;
233230
}
234231

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

818-
track->regs[i].selector = NULL;
819-
track->regs[i].bitwise = NULL;
815+
__nft_reg_track_cancel(track, i);
820816
}
821817

822818
return false;

0 commit comments

Comments
 (0)