The level of struct nft_ctx is updated by nf_tables_check_loops().
That is used to validate jumpstack depth.
But jumpstack validation routine doesn't update and validate recursively.
So, in some cases, chain depth can be bigger than the NFT_JUMP_STACK_SIZE.

After this patch, The jumpstack validation routine is located in
the nft_chain_validate().
When new rules or new set elements are added,
the nft_table_validate() is called by the nf_tables_newrule
and the nf_tables_newsetelem.
The nft_table_validate() calls the nft_chain_validate()
that visit all their children chains recursively.
So it can update depth of chain certainly.

Reproducer:
   %cat ./test.sh
   #!/bin/bash
   nft add table ip filter
   nft add chain ip filter input { type filter hook input priority 0\; }
   for ((i=0;i<20;i++)); do
        nft add chain ip filter a$i
   done

   nft add rule ip filter input jump a1

   for ((i=0;i<10;i++)); do
        nft add rule ip filter a$i jump a$((i+1))
   done

   for ((i=11;i<19;i++)); do
        nft add rule ip filter a$i jump a$((i+1))
   done

   nft add rule ip filter a10 jump a11

Result:
[  168.803743] kernel BUG at net/netfilter/nf_tables_core.c:186!
[  168.810881] invalid opcode: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN PTI
[  168.812091] Modules linked in: nf_tables nfnetlink ip_tables x_tables
[  168.812091] CPU: 0 PID: 8 Comm: ksoftirqd/0 Not tainted 4.17.0-rc7+ #186
[  168.812091] RIP: 0010:nft_do_chain+0x9fe/0xf50 [nf_tables]
[  168.812091] RSP: 0000:ffff88011a5475b0 EFLAGS: 00010212
[  168.812091] RAX: 00000000fffffffd RBX: ffff88011a5476a0 RCX: 0000000000000000
[  168.812091] RDX: 0000000000000010 RSI: ffff880111d69bd8 RDI: ffff88011a5476b0
[  168.812091] RBP: ffff88011a547870 R08: ffffed00234a8ed6 R09: ffffed00234a8ed5
[  168.812091] R10: ffff88011a5476af R11: ffffed00234a8ed6 R12: ffff88011a5478b8
[  168.881353] R13: ffff880111d69be0 R14: ffff880111d69bc0 R15: dffffc0000000000
[  168.887792] FS:  0000000000000000(0000) GS:ffff88011b600000(0000) 
knlGS:0000000000000000
[  168.887792] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  168.905313] CR2: 00007ffd58ee6148 CR3: 0000000067416000 CR4: 00000000001006f0
[  168.917438] Call Trace:
[  168.917438]  ? __save_stack_trace+0x73/0xd0
[  168.922459]  ? __nft_trace_packet+0x1a0/0x1a0 [nf_tables]
[  168.922459]  ? save_stack+0x92/0xa0
[  168.922459]  ? ip_rcv+0x802/0xe10
[  168.922459]  ? sched_clock_cpu+0x144/0x180
[  168.922459]  ? sched_clock_local+0xe2/0x150
[  168.922459]  ? __lock_acquire+0xcea/0x4ed0
[  168.922459]  ? sched_clock_cpu+0x144/0x180
[  168.922459]  ? debug_check_no_locks_freed+0x280/0x280
[  168.922459]  ? nft_do_chain_ipv4+0x16f/0x1e0 [nf_tables]
[  168.922459]  nft_do_chain_ipv4+0x16f/0x1e0 [nf_tables]
[  168.922459]  ? nft_do_chain_arp+0xa0/0xa0 [nf_tables]
[  168.922459]  ? lock_acquire+0x193/0x380
[  168.922459]  ? lock_acquire+0x193/0x380
[  168.922459]  ? ip_local_deliver+0x1c6/0x3c0
[  168.922459]  nf_hook_slow+0xae/0x170
[  168.922459]  ip_local_deliver+0x293/0x3c0
[  168.922459]  ? ip_call_ra_chain+0x490/0x490
[  168.922459]  ? ip_rcv_finish+0x1910/0x1910
[  168.922459]  ip_rcv+0x802/0xe10
[ ... ]


Signed-off-by: Taehee Yoo <ap420...@gmail.com>
---
 include/net/netfilter/nf_tables.h |  4 ++--
 net/netfilter/nf_tables_api.c     | 10 +++-------
 net/netfilter/nft_immediate.c     |  3 +++
 net/netfilter/nft_lookup.c        | 13 +++++++++++--
 4 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/include/net/netfilter/nf_tables.h 
b/include/net/netfilter/nf_tables.h
index 08c005c..a7d6476 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -150,6 +150,7 @@ static inline void nft_data_debug(const struct nft_data 
*data)
  *     @portid: netlink portID of the original message
  *     @seq: netlink sequence number
  *     @family: protocol family
+ *     @level: depth of the chains
  *     @report: notify via unicast netlink message
  */
 struct nft_ctx {
@@ -160,6 +161,7 @@ struct nft_ctx {
        u32                             portid;
        u32                             seq;
        u8                              family;
+       u8                              level;
        bool                            report;
 };
 
@@ -865,7 +867,6 @@ enum nft_chain_flags {
  *     @table: table that this chain belongs to
  *     @handle: chain handle
  *     @use: number of jump references to this chain
- *     @level: length of longest path to this chain
  *     @flags: bitmask of enum nft_chain_flags
  *     @name: name of the chain
  */
@@ -878,7 +879,6 @@ struct nft_chain {
        struct nft_table                *table;
        u64                             handle;
        u32                             use;
-       u16                             level;
        u8                              flags:6,
                                        genmask:2;
        char                            *name;
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index ca4c4d9..b7d8c25 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -2384,6 +2384,9 @@ int nft_chain_validate(const struct nft_ctx *ctx, const 
struct nft_chain *chain)
        struct nft_rule *rule;
        int err;
 
+       if (ctx->level == NFT_JUMP_STACK_SIZE)
+               return -EMLINK;
+
        list_for_each_entry(rule, &chain->rules, list) {
                if (!nft_is_active_next(ctx->net, rule))
                        continue;
@@ -6826,13 +6829,6 @@ int nft_validate_register_store(const struct nft_ctx 
*ctx,
                        err = nf_tables_check_loops(ctx, data->verdict.chain);
                        if (err < 0)
                                return err;
-
-                       if (ctx->chain->level + 1 >
-                           data->verdict.chain->level) {
-                               if (ctx->chain->level + 1 == 
NFT_JUMP_STACK_SIZE)
-                                       return -EMLINK;
-                               data->verdict.chain->level = ctx->chain->level 
+ 1;
-                       }
                }
 
                return 0;
diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c
index 15adf8c..9f0542e 100644
--- a/net/netfilter/nft_immediate.c
+++ b/net/netfilter/nft_immediate.c
@@ -99,6 +99,7 @@ static int nft_immediate_validate(const struct nft_ctx *ctx,
 {
        const struct nft_immediate_expr *priv = nft_expr_priv(expr);
        const struct nft_data *data;
+       struct nft_ctx *pctx = (struct nft_ctx *)ctx;
        int err;
 
        if (priv->dreg != NFT_REG_VERDICT)
@@ -109,9 +110,11 @@ static int nft_immediate_validate(const struct nft_ctx 
*ctx,
        switch (data->verdict.code) {
        case NFT_JUMP:
        case NFT_GOTO:
+               pctx->level++;
                err = nft_chain_validate(ctx, data->verdict.chain);
                if (err < 0)
                        return err;
+               pctx->level--;
                break;
        default:
                break;
diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c
index 42e6fad..af13093 100644
--- a/net/netfilter/nft_lookup.c
+++ b/net/netfilter/nft_lookup.c
@@ -156,6 +156,8 @@ static int nft_lookup_validate_setelem(const struct nft_ctx 
*ctx,
 {
        const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
        const struct nft_data *data;
+       struct nft_ctx *pctx = (struct nft_ctx *)ctx;
+       int err;
 
        if (nft_set_ext_exists(ext, NFT_SET_EXT_FLAGS) &&
            *nft_set_ext_flags(ext) & NFT_SET_ELEM_INTERVAL_END)
@@ -165,10 +167,17 @@ static int nft_lookup_validate_setelem(const struct 
nft_ctx *ctx,
        switch (data->verdict.code) {
        case NFT_JUMP:
        case NFT_GOTO:
-               return nft_chain_validate(ctx, data->verdict.chain);
+               pctx->level++;
+               err = nft_chain_validate(ctx, data->verdict.chain);
+               if (err < 0)
+                       return err;
+               pctx->level--;
+               break;
        default:
-               return 0;
+               break;
        }
+
+       return 0;
 }
 
 static int nft_lookup_validate(const struct nft_ctx *ctx,
-- 
2.9.3

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to