All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH iptables] nft: fix out-of-bounds read listing an old-revision match
@ 2026-07-17 18:59 Omkhar Arasaratnam
  2026-07-31 11:37 ` Pablo Neira Ayuso
  0 siblings, 1 reply; 2+ messages in thread
From: Omkhar Arasaratnam @ 2026-07-17 18:59 UTC (permalink / raw)
  To: netfilter-devel@vger.kernel.org
  Cc: phil@nwl.cc, fw@strlen.de, pablo@netfilter.org

nft_parse_match() sizes the xt_entry_match buffer from the wire blob
length reported by the kernel:

        m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len);
        memcpy(&m->data, mt_info, mt_len);

but selects the print/save/compare extension purely by name via
xtables_find_match(), which returns the highest supported revision. When
the kernel stored an older, smaller revision of the match, mt_len is
smaller than the resolved extension's userspacesize, and the print
callback -- and compare_matches(), which memcmp()s userspacesize bytes
-- read past the mt_len-sized allocation.

For example a conntrack match stored as xt_conntrack_mtinfo1 (152 bytes)
is printed by the rev3 callback conntrack_dump(), which reads
xt_conntrack_mtinfo3 fields (info->origsrc_port_high at offset 154), two
bytes past the buffer. Listing such a ruleset with iptables-nft -L reads
out of bounds.

Size the allocation to the resolved extension when its blob is larger,
just as nft_create_match() and __nft_create_target() already do, so the
print and compare paths stay in bounds. Matches whose stored blob is at
least as large as the extension are unaffected.

Fixes: cdc78b1d6bd7 ("nft: convert rule into a command state structure")
Signed-off-by: Omkhar Arasaratnam <omkhar@linkedin.com>
---
 iptables/nft-ruleparse.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/iptables/nft-ruleparse.c b/iptables/nft-ruleparse.c
index 26a605cf..2c5aa9ce 100644
--- a/iptables/nft-ruleparse.c
+++ b/iptables/nft-ruleparse.c
@@ -638,7 +638,10 @@ static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
                return;
        }

-       m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len);
+       /* Kernel blob may be smaller than the resolved extension (older
+        * revision); size to the extension so print/compare stay in bounds. */
+       m = xtables_calloc(1, sizeof(struct xt_entry_match) +
+                             (mt_len < match->size ? match->size : mt_len));
        memcpy(&m->data, mt_info, mt_len);
        m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
        m->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
--
2.34.1

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH iptables] nft: fix out-of-bounds read listing an old-revision match
  2026-07-17 18:59 [PATCH iptables] nft: fix out-of-bounds read listing an old-revision match Omkhar Arasaratnam
@ 2026-07-31 11:37 ` Pablo Neira Ayuso
  0 siblings, 0 replies; 2+ messages in thread
From: Pablo Neira Ayuso @ 2026-07-31 11:37 UTC (permalink / raw)
  To: Omkhar Arasaratnam
  Cc: netfilter-devel@vger.kernel.org, phil@nwl.cc, fw@strlen.de

On Fri, Jul 17, 2026 at 06:59:27PM +0000, Omkhar Arasaratnam wrote:
> nft_parse_match() sizes the xt_entry_match buffer from the wire blob
> length reported by the kernel:
> 
>         m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len);
>         memcpy(&m->data, mt_info, mt_len);
> 
> but selects the print/save/compare extension purely by name via
> xtables_find_match(), which returns the highest supported revision. When
> the kernel stored an older, smaller revision of the match, mt_len is
> smaller than the resolved extension's userspacesize, and the print
> callback -- and compare_matches(), which memcmp()s userspacesize bytes
> -- read past the mt_len-sized allocation.
> 
> For example a conntrack match stored as xt_conntrack_mtinfo1 (152 bytes)
> is printed by the rev3 callback conntrack_dump(), which reads
> xt_conntrack_mtinfo3 fields (info->origsrc_port_high at offset 154), two
> bytes past the buffer. Listing such a ruleset with iptables-nft -L reads
> out of bounds.

Is this a hypothetical crash? How can this happen in practise? These
revisions are very old, you would need to pick a very old iptables
version which possibly does not supports nftables to trigger this?

> Size the allocation to the resolved extension when its blob is larger,
> just as nft_create_match() and __nft_create_target() already do, so the
> print and compare paths stay in bounds. Matches whose stored blob is at
> least as large as the extension are unaffected.
>
> Fixes: cdc78b1d6bd7 ("nft: convert rule into a command state structure")
> Signed-off-by: Omkhar Arasaratnam <omkhar@linkedin.com>
> ---
>  iptables/nft-ruleparse.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/iptables/nft-ruleparse.c b/iptables/nft-ruleparse.c
> index 26a605cf..2c5aa9ce 100644
> --- a/iptables/nft-ruleparse.c
> +++ b/iptables/nft-ruleparse.c
> @@ -638,7 +638,10 @@ static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
>                 return;
>         }
> 
> -       m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len);
> +       /* Kernel blob may be smaller than the resolved extension (older
> +        * revision); size to the extension so print/compare stay in bounds. */
> +       m = xtables_calloc(1, sizeof(struct xt_entry_match) +
> +                             (mt_len < match->size ? match->size : mt_len));
>         memcpy(&m->data, mt_info, mt_len);
>         m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
>         m->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
> --
> 2.34.1

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-07-31 11:37 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-17 18:59 [PATCH iptables] nft: fix out-of-bounds read listing an old-revision match Omkhar Arasaratnam
2026-07-31 11:37 ` Pablo Neira Ayuso

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.