From nobody Mon Jun 8 06:36:15 2026 Received: from mail-dy1-f175.google.com (mail-dy1-f175.google.com [74.125.82.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 756C77080D for ; Sat, 6 Jun 2026 08:10:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780733441; cv=none; b=AOOHv4TL9ALJMCH+9EobmaFvBPbBFZQ9X9zfCD4A+AWPi0aD0LOolrbKtJ0gUCNOOJkfMqPHBMSMgb3x7DgP/5d1mE9WpHuNnKET/XvPznVEF+lIPVOySFchmUMWX+FHxUK6/L4+hq+LUgxuEVBBvUilBAdbIEIv678uI8yK+aU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780733441; c=relaxed/simple; bh=ByB3H0cGis14klti9ll3DHhT2PvsZr4nKGwnHNxx8gM=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=lyls7iMuay0nMQJ4DWiAFfEYYdjBxzxuIm73OQbqubTO0f6Nnt6499NVyt8twx8B5wibLyelfk+43Yky+tmCThTUavQc4hJWJdCM3NbMYrm7xFgK7h51Bpr6j3/zIkP5D8EPDbY5sgREEL2CtUXTy87gymHC3MNva3ygEyAxPWU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=openai.com; spf=pass smtp.mailfrom=openai.com; dkim=pass (1024-bit key) header.d=openai.com header.i=@openai.com header.b=BI14mjK8; arc=none smtp.client-ip=74.125.82.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=openai.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=openai.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=openai.com header.i=@openai.com header.b="BI14mjK8" Received: by mail-dy1-f175.google.com with SMTP id 5a478bee46e88-306f36df4feso1749993eec.0 for ; Sat, 06 Jun 2026 01:10:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=openai.com; s=google; t=1780733439; x=1781338239; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=ip/LlhKCN0CqvIzA9ksjg+r41kTv5NJcJDiYOblkKno=; b=BI14mjK8ionce+/1dnX3AUjeEIQqy8gLOFS81FqUpENZhRn5Mn/oIkXP7pZRdegWDm Qcp03kAgpRlQCz/PaTNORVC4g1QeNCzkrR8wTBchA0OH92GmA5H9eLutXMiiwwMyywws vTgENvs5C2I56K9jxnpT5fPb6iMor3hEPz8VM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780733439; x=1781338239; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=ip/LlhKCN0CqvIzA9ksjg+r41kTv5NJcJDiYOblkKno=; b=gYdZhwIenDfFCcQhe+5vv4QX8frqUVOtltbodxNPnrR4GawbcoR3MtoxfWMHzKq+wu 7xj0/sJTpHGvix7mh12BAQSEqaHW6slp3e5gq3RjaIP+GZW2BcyWY6vIZYKO603tJwXg 0TdsBwZ3KOiWTfXLqLqGJ9kd8Osuc8r7arngJBQeDQ51fVk5mKjoxaSfozyyGJZNwQvQ 8Cshff4FzZnmC6B4lapSN4BcfwbxebzeZlLyvAY6UI1h4GxjV05ek1k+7nte/UZ0xm0q HFCuipbu2RCeT4PfzyBUGtp2tbSH4y6wTgKuTrPc2006QX5e+HpKxzyG0CFKknln7otj KiMw== X-Gm-Message-State: AOJu0YxOtS0JMkrz5YQ/Qr0yfbO9w031z0PZYrKAMzVBON9+fAk4ud+H WCrq0CjdPXIwrW8W9vPrJdguVgV/aSuOkiHezGHGGW/ksWs3dhQ5VRA8hzQR/cczVRN6FnJlRwP f7Z/Z83E= X-Gm-Gg: Acq92OFGuZxlMvrxDlZr3wh6kCDNDeYOw8ScsShzgw0pyCa878E+2P19Hnf7Cj01CK5 ifDkbKVexBplIWlCurItgnzHLwzb+2faXsawOFQ5peXCYO4HuyfNqWX8CHptvjlkyo9M4dE/wqD TQ3wiiNHCyI8nLtuX1XXR84WhCuLU/1EAnIpTDkC5/2hCQ46N7I3lBZ+qwaKozVl0+QaiwSLxiU +mllxnule/BVk9rPDG10ApQZ7y3Uv2wglr47YeJH9yuh9IwlFFnecpw+tJ00rxn2KkMUs7MBion k7T5zr7WWe8KDMVVTxorxmdVyH3Q5uRffPzpt0O4zzaKlGiR3rM12eJRRJ5aaKupYWBUY9ijrB1 fNZ1Hlzx1NP3wY0vGRbgzlO136AhVKF4X3SC+NvdQ8XxOb5529BbwhZX8DV1XQfaUCxQ6R+Ctbb MXCY6u9iJo/wRLel2bmE+DrglcC8pktKovuRQ0LLr77E+Xziy3WfeH/L9XxgGAY1CsRcw93NWN6 jJSdUtA/O4NwQe0sEZu5DJfoYs0dQQzoFeEn3WnnHU1 X-Received: by 2002:a05:7301:7c0e:b0:2e7:190:41d6 with SMTP id 5a478bee46e88-3077fd752c9mr3747083eec.2.1780733439394; Sat, 06 Jun 2026 01:10:39 -0700 (PDT) Received: from com-75606.node.ndb.openai.org ([104.241.0.233]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3074df102a1sm9597548eec.20.2026.06.06.01.10.38 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sat, 06 Jun 2026 01:10:39 -0700 (PDT) From: Kyle Zeng To: linux-kernel@vger.kernel.org Cc: pablo@netfilter.org, Kyle Zeng Subject: [PATCH] netfilter: x_tables: avoid leaking percpu counter pointers Date: Sat, 6 Jun 2026 01:10:31 -0700 Message-ID: <20260606081032.15283-1-kylebot@openai.com> X-Mailer: git-send-email 2.54.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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_table= s.c index 85a01cbe448a..5773366de242 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -702,14 +702,12 @@ static int copy_entries_to_user(unsigned int total_si= ze, const struct xt_entry_target *t; =20 e =3D loc_cpu_entry + off; - if (copy_to_user(userptr + off, e, sizeof(*e))) { - ret =3D -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])) !=3D 0) { + sizeof(counters[num]))) { ret =3D -EFAULT; goto free_counters; } @@ -1327,9 +1325,8 @@ static int compat_copy_entry_to_user(struct arpt_entr= y *e, void __user **dstptr, =20 origsize =3D *size; ce =3D *dstptr; - if (copy_to_user(ce, e, sizeof(struct arpt_entry)) !=3D 0 || - copy_to_user(&ce->counters, &counters[i], - sizeof(counters[i])) !=3D 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; =20 *dstptr +=3D sizeof(struct compat_arpt_entry); diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index a0ceb618966f..e6e2a71d33b5 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -832,14 +832,12 @@ copy_entries_to_user(unsigned int total_size, const struct xt_entry_target *t; =20 e =3D loc_cpu_entry + off; - if (copy_to_user(userptr + off, e, sizeof(*e))) { - ret =3D -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])) !=3D 0) { + sizeof(counters[num]))) { ret =3D -EFAULT; goto free_counters; } @@ -1228,9 +1226,8 @@ compat_copy_entry_to_user(struct ipt_entry *e, void _= _user **dstptr, =20 origsize =3D *size; ce =3D *dstptr; - if (copy_to_user(ce, e, sizeof(struct ipt_entry)) !=3D 0 || - copy_to_user(&ce->counters, &counters[i], - sizeof(counters[i])) !=3D 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; =20 *dstptr +=3D sizeof(struct compat_ipt_entry); diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_table= s.c index 24a590c87eac..77f7cf526df8 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -848,14 +848,12 @@ copy_entries_to_user(unsigned int total_size, const struct xt_entry_target *t; =20 e =3D loc_cpu_entry + off; - if (copy_to_user(userptr + off, e, sizeof(*e))) { - ret =3D -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])) !=3D 0) { + sizeof(counters[num]))) { ret =3D -EFAULT; goto free_counters; } @@ -1244,9 +1242,8 @@ compat_copy_entry_to_user(struct ip6t_entry *e, void = __user **dstptr, =20 origsize =3D *size; ce =3D *dstptr; - if (copy_to_user(ce, e, sizeof(struct ip6t_entry)) !=3D 0 || - copy_to_user(&ce->counters, &counters[i], - sizeof(counters[i])) !=3D 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; =20 *dstptr +=3D sizeof(struct compat_ip6t_entry); --=20 2.43.0