diff mbox series

[nf] netfilter: ebtables: fix OOB read in compat_mtw_from_user

Message ID 20260520080119.12627-1-fw@strlen.de
State Accepted, archived
Headers show
Series [nf] netfilter: ebtables: fix OOB read in compat_mtw_from_user | expand

Commit Message

Florian Westphal May 20, 2026, 8:01 a.m. UTC
Luxiao Xu says:

 The function compat_mtw_from_user() converts ebtables extensions from
 32-bit user structures to kernel native structures. However, it lacks
 proper validation of the user-supplied match_size/target_size.

 When certain extensions are processed, the kernel-side translation
 logic may perform memory accesses based on the extension's expected
 size. If the user provides a size smaller than what the extension
 requires, it results in an out-of-bounds read as reported by KASAN.

 This fix introduces a check to ensure match_size is at least as large
 as the extension's required compatsize. This covers matches, watchers,
 and targets, while maintaining compatibility with standard targets.

AFAIU this is relevant for matches that need to go though
match->compat_from_user() call.  Those that use plain memcpy with the
user-provided size are ok because the caller checks that size vs the
start of the next rule entry offset (which itself is checked vs. total
size copied from userspace).

The ->compat_from_user() callbacks assume they can read compatsize bytes,
so they need this extra check.

Based on an earlier patch from Luxiao Xu.

Fixes: 81e675c227ec ("netfilter: ebtables: add CONFIG_COMPAT support")
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Signed-off-by: Luxiao Xu <rakukuip@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 @Original authors/reporters: is my understanding correct?

 net/bridge/netfilter/ebtables.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

Comments

Fernando Fernandez Mancera May 22, 2026, 9:31 a.m. UTC | #1
On 5/20/26 10:01 AM, Florian Westphal wrote:
> Luxiao Xu says:
> 
>   The function compat_mtw_from_user() converts ebtables extensions from
>   32-bit user structures to kernel native structures. However, it lacks
>   proper validation of the user-supplied match_size/target_size.
> 
>   When certain extensions are processed, the kernel-side translation
>   logic may perform memory accesses based on the extension's expected
>   size. If the user provides a size smaller than what the extension
>   requires, it results in an out-of-bounds read as reported by KASAN.
> 
>   This fix introduces a check to ensure match_size is at least as large
>   as the extension's required compatsize. This covers matches, watchers,
>   and targets, while maintaining compatibility with standard targets.
> 
> AFAIU this is relevant for matches that need to go though
> match->compat_from_user() call.  Those that use plain memcpy with the
> user-provided size are ok because the caller checks that size vs the
> start of the next rule entry offset (which itself is checked vs. total
> size copied from userspace).
> 
> The ->compat_from_user() callbacks assume they can read compatsize bytes,
> so they need this extra check.
> 
> Based on an earlier patch from Luxiao Xu.
> 
> Fixes: 81e675c227ec ("netfilter: ebtables: add CONFIG_COMPAT support")
> Reported-by: Yuan Tan <yuantan098@gmail.com>
> Reported-by: Yifan Wu <yifanwucs@gmail.com>
> Reported-by: Juefei Pu <tomapufckgml@gmail.com>
> Reported-by: Xin Liu <bird@lzu.edu.cn>
> Signed-off-by: Luxiao Xu <rakukuip@gmail.com>
> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
> Signed-off-by: Florian Westphal <fw@strlen.de>

Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de>

Thanks!
Lain Anc May 23, 2026, 1:23 p.m. UTC | #2
Florian Westphal <fw@strlen.de> 于2026年5月20日周三 16:01写道:
>
> Luxiao Xu says:
>
>  The function compat_mtw_from_user() converts ebtables extensions from
>  32-bit user structures to kernel native structures. However, it lacks
>  proper validation of the user-supplied match_size/target_size.
>
>  When certain extensions are processed, the kernel-side translation
>  logic may perform memory accesses based on the extension's expected
>  size. If the user provides a size smaller than what the extension
>  requires, it results in an out-of-bounds read as reported by KASAN.
>
>  This fix introduces a check to ensure match_size is at least as large
>  as the extension's required compatsize. This covers matches, watchers,
>  and targets, while maintaining compatibility with standard targets.
>
> AFAIU this is relevant for matches that need to go though
> match->compat_from_user() call.  Those that use plain memcpy with the
> user-provided size are ok because the caller checks that size vs the
> start of the next rule entry offset (which itself is checked vs. total
> size copied from userspace).
>
> The ->compat_from_user() callbacks assume they can read compatsize bytes,
> so they need this extra check.
>
> Based on an earlier patch from Luxiao Xu.
>
> Fixes: 81e675c227ec ("netfilter: ebtables: add CONFIG_COMPAT support")
> Reported-by: Yuan Tan <yuantan098@gmail.com>
> Reported-by: Yifan Wu <yifanwucs@gmail.com>
> Reported-by: Juefei Pu <tomapufckgml@gmail.com>
> Reported-by: Xin Liu <bird@lzu.edu.cn>
> Signed-off-by: Luxiao Xu <rakukuip@gmail.com>
> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
> Signed-off-by: Florian Westphal <fw@strlen.de>
> ---
>  @Original authors/reporters: is my understanding correct?
>

Yes, your understanding is absolutely correct. The extra check on compatsize
successfully prevents the out-of-bounds read during the translation logic.

Acked-by: Luxiao Xu <rakukuip@gmail.com>


>  net/bridge/netfilter/ebtables.c | 30 ++++++++++++++++++++++++++++++
>  1 file changed, 30 insertions(+)
>
> diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
> index b9f4daac09af..8a6a069329d2 100644
> --- a/net/bridge/netfilter/ebtables.c
> +++ b/net/bridge/netfilter/ebtables.c
> @@ -1956,6 +1956,25 @@ enum compat_mwt {
>         EBT_COMPAT_TARGET,
>  };
>
> +static bool match_size_ok(const struct xt_match *match, unsigned int match_size)
> +{
> +       u16 csize;
> +
> +       if (match->matchsize == -1) /* cannot validate ebt_among */
> +               return true;
> +
> +       csize = match->compatsize ? : match->matchsize;
> +
> +       return match_size >= csize;
> +}
> +
> +static bool tgt_size_ok(const struct xt_target *tgt, unsigned int tgt_size)
> +{
> +       u16 csize = tgt->compatsize ? : tgt->targetsize;
> +
> +       return tgt_size >= csize;
> +}
> +
>  static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
>                                 enum compat_mwt compat_mwt,
>                                 struct ebt_entries_buf_state *state,
> @@ -1981,6 +2000,11 @@ static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
>                 if (IS_ERR(match))
>                         return PTR_ERR(match);
>
> +               if (!match_size_ok(match, match_size)) {
> +                       module_put(match->me);
> +                       return -EINVAL;
> +               }
> +
>                 off = ebt_compat_match_offset(match, match_size);
>                 if (dst) {
>                         if (match->compat_from_user)
> @@ -2000,6 +2024,12 @@ static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
>                                             mwt->u.revision);
>                 if (IS_ERR(wt))
>                         return PTR_ERR(wt);
> +
> +               if (!tgt_size_ok(wt, match_size)) {
> +                       module_put(wt->me);
> +                       return -EINVAL;
> +               }
> +
>                 off = xt_compat_target_offset(wt);
>
>                 if (dst) {
> --
> 2.53.0
>
diff mbox series

Patch

diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index b9f4daac09af..8a6a069329d2 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -1956,6 +1956,25 @@  enum compat_mwt {
 	EBT_COMPAT_TARGET,
 };
 
+static bool match_size_ok(const struct xt_match *match, unsigned int match_size)
+{
+	u16 csize;
+
+	if (match->matchsize == -1) /* cannot validate ebt_among */
+		return true;
+
+	csize = match->compatsize ? : match->matchsize;
+
+	return match_size >= csize;
+}
+
+static bool tgt_size_ok(const struct xt_target *tgt, unsigned int tgt_size)
+{
+	u16 csize = tgt->compatsize ? : tgt->targetsize;
+
+	return tgt_size >= csize;
+}
+
 static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
 				enum compat_mwt compat_mwt,
 				struct ebt_entries_buf_state *state,
@@ -1981,6 +2000,11 @@  static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
 		if (IS_ERR(match))
 			return PTR_ERR(match);
 
+		if (!match_size_ok(match, match_size)) {
+			module_put(match->me);
+			return -EINVAL;
+		}
+
 		off = ebt_compat_match_offset(match, match_size);
 		if (dst) {
 			if (match->compat_from_user)
@@ -2000,6 +2024,12 @@  static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt,
 					    mwt->u.revision);
 		if (IS_ERR(wt))
 			return PTR_ERR(wt);
+
+		if (!tgt_size_ok(wt, match_size)) {
+			module_put(wt->me);
+			return -EINVAL;
+		}
+
 		off = xt_compat_target_offset(wt);
 
 		if (dst) {