netfilter: nf_tables: cancel tracking for clobbered destination registers
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 14 Mar 2022 17:23:01 +0000 (18:23 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Sat, 19 Mar 2022 23:29:46 +0000 (00:29 +0100)
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>
include/net/netfilter/nf_tables.h
include/net/netfilter/nft_meta.h
net/bridge/netfilter/nft_meta_bridge.c
net/netfilter/nf_tables_api.c
net/netfilter/nft_bitwise.c
net/netfilter/nft_byteorder.c
net/netfilter/nft_meta.c
net/netfilter/nft_payload.c

index edabfb9e97ce4a61fa466b0bf47016d7a46711a6..20af9d3557b9d4af410d89f7781e66743a5ce281 100644 (file)
@@ -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;
@@ -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 */
index 2dce55c736f400ede2bb121e739be1718787b85d..246fd023dcf48fcc6ec93e92065501c553c8f482 100644 (file)
@@ -6,6 +6,7 @@
 
 struct nft_meta {
        enum nft_meta_keys      key:8;
+       u8                      len;
        union {
                u8              dreg;
                u8              sreg;
index c1ef9cc89b7829568cb108a5bb35629c322e121a..380a31ebf8402d38a6bea6c8ea601c0fa6ab0e41 100644 (file)
@@ -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);
 }
@@ -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;
index 6a10042243eb1a397dae64297f40a9fa7ec6ce5c..a97e112fe4064375fd60e7b98f40796bec93f30d 100644 (file)
@@ -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
  */
index 7b727d3ebf9df6159f9b7565f21e0465a6cd20ee..dffda6612369d35f17513c11f4c4a4d73f44db48 100644 (file)
@@ -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 &&
@@ -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;
 }
@@ -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;
        }
 
index e646e9ee4a98754136681139010341d255829564..d77609144b268f9af69a89a807ff2e63b741e83c 100644 (file)
@@ -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;
 }
index 5ab4df56c945bdaa6c06c3aca660312bff202054..482eed7c7bbfe6bfc2acfbb090cb60ece88c8e7e 100644 (file)
@@ -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);
 }
@@ -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;
@@ -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;
        }
 
@@ -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;
index 5cc06aef43452a8dcd30bb38e4dd6b0865566ed7..2e7ac007cb30fe6c02429034d102b4f2d6df9ceb 100644 (file)
@@ -216,10 +216,8 @@ 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;
        }
 
@@ -227,8 +225,7 @@ static bool nft_payload_reduce(struct nft_regs_track *track,
        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;
        }
 
@@ -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;