From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CC8533CC303; Tue, 16 Jun 2026 18:54:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781636097; cv=none; b=deCXELfXXMu9AKasW0/1hJFHYlw6MpGr7HaUIiUykTuyroHiqhKavOSOGmkFpf/R0hZ7nOfZ9uVj1Thhw8/zliSZQOdQOdtuPciKUUsDYt01c/UyZgMyrRxPkLtQF7FKWrerLVuZlbNMm1BElJEHJ2qy3n4HP9ZmzsPyFdiyN+w= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781636097; c=relaxed/simple; bh=KKIFXSxNv1L7cJa09FoYaoz8Eu8aiWS+C9+8HvGh4sU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=C+qOUX+1gTOc0Lw9PGAWP3Bxh+Cc4nWpItZ+FOPZQTJr9IjQR3Gnjcb9VXYk3QN5bHTbReuFpboNOPzoNr3SHaEeoIPNKWE9lUE+n21qcgjj2sQcIuAxOdYq2CgNg5eCp0mHJAAKrreJ4Wj9/untApowceKzW1MJoNR6pScl78I= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=bGjhUeHI; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="bGjhUeHI" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D2C021F00A3A; Tue, 16 Jun 2026 18:54:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1781636096; bh=OxuMUzbmseXL7+z2xVIKVgCAnJHan5d6+uQ/X+aGzF8=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=bGjhUeHIOqL2Zczm4K1Vt1Uq8Xt8rzyDA358tL6dC3sLRc+lv6LSs4GCyyidf27rp cerY3Uu9SOQ9d1zAZU4m1qgMxSUWMgLcmWhxHdxn2hhyzkAAybvqWaHiBpJnT/CdoF kHNCmZ3a7Lwn5knzXbmhRpvPaUMZnNRRVYZtEtxA= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Kyle Zeng , Pablo Neira Ayuso , Sasha Levin Subject: [PATCH 5.10 175/342] netfilter: x_tables: avoid leaking percpu counter pointers Date: Tue, 16 Jun 2026 20:27:51 +0530 Message-ID: <20260616145056.357398903@linuxfoundation.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260616145048.348037099@linuxfoundation.org> References: <20260616145048.348037099@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: patches@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 5.10-stable review patch. If anyone has any objections, please let me know. ------------------ From: Kyle Zeng [ Upstream commit f7f2fbb0e893a0238dc464f8d8c0f5609bec584f ] The native and compat get-entries paths copy the fixed rule entry header from the kernelized rule blob to userspace before overwriting the entry's counter fields with a sanitized counter snapshot. On SMP kernels, entry->counters.pcnt contains the percpu allocation address used by x_tables rule counters. A caller can provide a userspace buffer that faults during the initial fixed-header copy after pcnt has been copied but before the later sanitized counter copy runs. The syscall then returns -EFAULT while leaving the raw percpu pointer in userspace. Copy only the fixed entry prefix before counters from the kernelized rule blob, then copy the sanitized counter snapshot into the counter field. Apply this ordering to the IPv4, IPv6, and ARP native and compat get-entries implementations so a fault cannot expose the internal percpu counter pointer. Fixes: 71ae0dff02d7 ("netfilter: xtables: use percpu rule counters") Signed-off-by: Kyle Zeng Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/ipv4/netfilter/arp_tables.c | 15 ++++++--------- net/ipv4/netfilter/ip_tables.c | 15 ++++++--------- net/ipv6/netfilter/ip6_tables.c | 15 ++++++--------- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index d5f3b6260da0cf..bc8a5b6eccc336 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -701,14 +701,12 @@ static int copy_entries_to_user(unsigned int total_size, const struct xt_entry_target *t; e = loc_cpu_entry + off; - if (copy_to_user(userptr + off, e, sizeof(*e))) { - ret = -EFAULT; - goto free_counters; - } - if (copy_to_user(userptr + off + if (copy_to_user(userptr + off, e, + offsetof(struct arpt_entry, counters)) || + copy_to_user(userptr + off + offsetof(struct arpt_entry, counters), &counters[num], - sizeof(counters[num])) != 0) { + sizeof(counters[num]))) { ret = -EFAULT; goto free_counters; } @@ -1326,9 +1324,8 @@ static int compat_copy_entry_to_user(struct arpt_entry *e, void __user **dstptr, origsize = *size; ce = *dstptr; - if (copy_to_user(ce, e, sizeof(struct arpt_entry)) != 0 || - copy_to_user(&ce->counters, &counters[i], - sizeof(counters[i])) != 0) + if (copy_to_user(ce, e, offsetof(struct compat_arpt_entry, counters)) || + copy_to_user(&ce->counters, &counters[i], sizeof(counters[i]))) return -EFAULT; *dstptr += sizeof(struct compat_arpt_entry); diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 22e9ff592cd75f..55798e12fb3745 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -833,14 +833,12 @@ copy_entries_to_user(unsigned int total_size, const struct xt_entry_target *t; e = loc_cpu_entry + off; - if (copy_to_user(userptr + off, e, sizeof(*e))) { - ret = -EFAULT; - goto free_counters; - } - if (copy_to_user(userptr + off + if (copy_to_user(userptr + off, e, + offsetof(struct ipt_entry, counters)) || + copy_to_user(userptr + off + offsetof(struct ipt_entry, counters), &counters[num], - sizeof(counters[num])) != 0) { + sizeof(counters[num]))) { ret = -EFAULT; goto free_counters; } @@ -1229,9 +1227,8 @@ compat_copy_entry_to_user(struct ipt_entry *e, void __user **dstptr, origsize = *size; ce = *dstptr; - if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 || - copy_to_user(&ce->counters, &counters[i], - sizeof(counters[i])) != 0) + if (copy_to_user(ce, e, offsetof(struct compat_ipt_entry, counters)) || + copy_to_user(&ce->counters, &counters[i], sizeof(counters[i]))) return -EFAULT; *dstptr += sizeof(struct compat_ipt_entry); diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index df7cd3d285e4f0..da136d25701a9e 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -850,14 +850,12 @@ copy_entries_to_user(unsigned int total_size, const struct xt_entry_target *t; e = loc_cpu_entry + off; - if (copy_to_user(userptr + off, e, sizeof(*e))) { - ret = -EFAULT; - goto free_counters; - } - if (copy_to_user(userptr + off + if (copy_to_user(userptr + off, e, + offsetof(struct ip6t_entry, counters)) || + copy_to_user(userptr + off + offsetof(struct ip6t_entry, counters), &counters[num], - sizeof(counters[num])) != 0) { + sizeof(counters[num]))) { ret = -EFAULT; goto free_counters; } @@ -1246,9 +1244,8 @@ compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr, origsize = *size; ce = *dstptr; - if (copy_to_user(ce, e, sizeof(struct ip6t_entry)) != 0 || - copy_to_user(&ce->counters, &counters[i], - sizeof(counters[i])) != 0) + if (copy_to_user(ce, e, offsetof(struct compat_ip6t_entry, counters)) || + copy_to_user(&ce->counters, &counters[i], sizeof(counters[i]))) return -EFAULT; *dstptr += sizeof(struct compat_ip6t_entry); -- 2.53.0