nft_compat ops do not have static storage duration, unlike all other
expressions.
When nf_tables_expr_destroy() returns, expr->ops might have been
free'd already, so we need to store next address before calling
expression destructor.
For same reason, we can't deref match pointer after nft_xt_put().
This can be easily reproduced by adding msleep() before
nft_match_destroy() returns.
Fixes: 0ca743a55991 ("netfilter: nf_tables: add compatibility layer for x_tables")
Reported-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
                                   struct nft_rule *rule)
 {
-       struct nft_expr *expr;
+       struct nft_expr *expr, *next;
 
        /*
         * Careful: some expressions might not be initialized in case this
         */
        expr = nft_expr_first(rule);
        while (expr != nft_expr_last(rule) && expr->ops) {
+               next = nft_expr_next(expr);
                nf_tables_expr_destroy(ctx, expr);
-               expr = nft_expr_next(expr);
+               expr = next;
        }
        kfree(rule);
 }
 
                    void *info)
 {
        struct xt_match *match = expr->ops->data;
+       struct module *me = match->me;
        struct xt_mtdtor_param par;
 
        par.net = ctx->net;
                par.match->destroy(&par);
 
        if (nft_xt_put(container_of(expr->ops, struct nft_xt, ops)))
-               module_put(match->me);
+               module_put(me);
 }
 
 static void