fix CVE-2023-52578
Eric Dumazet (2): net: add atomic_long_t to net_device_stats fields net: bridge: use DEV_STATS_INC()
Felix Riemann (1): net: Fix unwanted sign extension in netdev_stats_to_stats64()
Zhengchao Shao (1): net: fix kabi check warning
include/linux/netdevice.h | 81 ++++++++++++++++++++++++++++----------- include/net/dst.h | 5 +-- net/bridge/br_forward.c | 4 +- net/bridge/br_input.c | 4 +- net/core/dev.c | 14 ++----- 5 files changed, 67 insertions(+), 41 deletions(-)
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/5104 邮件列表地址:https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/2...
FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://gitee.com/openeuler/kernel/pulls/5104 Mailing list address: https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/2...
From: Eric Dumazet edumazet@google.com
stable inclusion from stable-v5.10.163 commit 037db10e3f93f0d51ab3f52fbdaa7e67a7edd0b8 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I95ATV CVE: CVE-2023-52578
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
[ Upstream commit 6c1c5097781f563b70a81683ea6fdac21637573b ]
Long standing KCSAN issues are caused by data-race around some dev->stats changes.
Most performance critical paths already use per-cpu variables, or per-queue ones.
It is reasonable (and more correct) to use atomic operations for the slow paths.
This patch adds an union for each field of net_device_stats, so that we can convert paths that are not yet protected by a spinlock or a mutex.
netdev_stats_to_stats64() no longer has an #if BITS_PER_LONG==64
Note that the memcpy() we were using on 64bit arches had no provision to avoid load-tearing, while atomic_long_read() is providing the needed protection at no cost.
Signed-off-by: Eric Dumazet edumazet@google.com Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Sasha Levin sashal@kernel.org Signed-off-by: Zhengchao Shao shaozhengchao@huawei.com --- include/linux/netdevice.h | 58 +++++++++++++++++++++++---------------- include/net/dst.h | 5 ++-- net/core/dev.c | 14 ++-------- 3 files changed, 40 insertions(+), 37 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b18671777abf..5af9230f5741 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -167,31 +167,38 @@ static inline bool dev_xmit_complete(int rc) * (unsigned long) so they can be read and written atomically. */
+#define NET_DEV_STAT(FIELD) \ + union { \ + unsigned long FIELD; \ + atomic_long_t __##FIELD; \ + } + struct net_device_stats { - unsigned long rx_packets; - unsigned long tx_packets; - unsigned long rx_bytes; - unsigned long tx_bytes; - unsigned long rx_errors; - unsigned long tx_errors; - unsigned long rx_dropped; - unsigned long tx_dropped; - unsigned long multicast; - unsigned long collisions; - unsigned long rx_length_errors; - unsigned long rx_over_errors; - unsigned long rx_crc_errors; - unsigned long rx_frame_errors; - unsigned long rx_fifo_errors; - unsigned long rx_missed_errors; - unsigned long tx_aborted_errors; - unsigned long tx_carrier_errors; - unsigned long tx_fifo_errors; - unsigned long tx_heartbeat_errors; - unsigned long tx_window_errors; - unsigned long rx_compressed; - unsigned long tx_compressed; + NET_DEV_STAT(rx_packets); + NET_DEV_STAT(tx_packets); + NET_DEV_STAT(rx_bytes); + NET_DEV_STAT(tx_bytes); + NET_DEV_STAT(rx_errors); + NET_DEV_STAT(tx_errors); + NET_DEV_STAT(rx_dropped); + NET_DEV_STAT(tx_dropped); + NET_DEV_STAT(multicast); + NET_DEV_STAT(collisions); + NET_DEV_STAT(rx_length_errors); + NET_DEV_STAT(rx_over_errors); + NET_DEV_STAT(rx_crc_errors); + NET_DEV_STAT(rx_frame_errors); + NET_DEV_STAT(rx_fifo_errors); + NET_DEV_STAT(rx_missed_errors); + NET_DEV_STAT(tx_aborted_errors); + NET_DEV_STAT(tx_carrier_errors); + NET_DEV_STAT(tx_fifo_errors); + NET_DEV_STAT(tx_heartbeat_errors); + NET_DEV_STAT(tx_window_errors); + NET_DEV_STAT(rx_compressed); + NET_DEV_STAT(tx_compressed); }; +#undef NET_DEV_STAT
#include <linux/cache.h> @@ -5333,4 +5340,9 @@ do { \
extern struct net_device *blackhole_netdev;
+/* Note: Avoid these macros in fast path, prefer per-cpu or per-queue counters. */ +#define DEV_STATS_INC(DEV, FIELD) atomic_long_inc(&(DEV)->stats.__##FIELD) +#define DEV_STATS_ADD(DEV, FIELD, VAL) \ + atomic_long_add((VAL), &(DEV)->stats.__##FIELD) + #endif /* _LINUX_NETDEVICE_H */ diff --git a/include/net/dst.h b/include/net/dst.h index ce7f7a1d968b..088a7ce6cc74 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -360,9 +360,8 @@ static inline void __skb_tunnel_rx(struct sk_buff *skb, struct net_device *dev, static inline void skb_tunnel_rx(struct sk_buff *skb, struct net_device *dev, struct net *net) { - /* TODO : stats should be SMP safe */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; + DEV_STATS_INC(dev, rx_packets); + DEV_STATS_ADD(dev, rx_bytes, skb->len); __skb_tunnel_rx(skb, dev, net); }
diff --git a/net/core/dev.c b/net/core/dev.c index b7a4f0bac7f5..38165f9492b6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -10348,24 +10348,16 @@ void netdev_run_todo(void) void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, const struct net_device_stats *netdev_stats) { -#if BITS_PER_LONG == 64 - BUILD_BUG_ON(sizeof(*stats64) < sizeof(*netdev_stats)); - memcpy(stats64, netdev_stats, sizeof(*netdev_stats)); - /* zero out counters that only exist in rtnl_link_stats64 */ - memset((char *)stats64 + sizeof(*netdev_stats), 0, - sizeof(*stats64) - sizeof(*netdev_stats)); -#else - size_t i, n = sizeof(*netdev_stats) / sizeof(unsigned long); - const unsigned long *src = (const unsigned long *)netdev_stats; + size_t i, n = sizeof(*netdev_stats) / sizeof(atomic_long_t); + const atomic_long_t *src = (atomic_long_t *)netdev_stats; u64 *dst = (u64 *)stats64;
BUILD_BUG_ON(n > sizeof(*stats64) / sizeof(u64)); for (i = 0; i < n; i++) - dst[i] = src[i]; + dst[i] = atomic_long_read(&src[i]); /* zero out counters that only exist in rtnl_link_stats64 */ memset((char *)stats64 + n * sizeof(u64), 0, sizeof(*stats64) - n * sizeof(u64)); -#endif } EXPORT_SYMBOL(netdev_stats_to_stats64);
From: Eric Dumazet edumazet@google.com
stable inclusion from stable-v5.10.198 commit 04cc361f029c14dd067ad180525c7392334c9bfd category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I95ATV CVE: CVE-2023-52578
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
[ Upstream commit 44bdb313da57322c9b3c108eb66981c6ec6509f4 ]
syzbot/KCSAN reported data-races in br_handle_frame_finish() [1] This function can run from multiple cpus without mutual exclusion.
Adopt SMP safe DEV_STATS_INC() to update dev->stats fields.
Handles updates to dev->stats.tx_dropped while we are at it.
[1] BUG: KCSAN: data-race in br_handle_frame_finish / br_handle_frame_finish
read-write to 0xffff8881374b2178 of 8 bytes by interrupt on cpu 1: br_handle_frame_finish+0xd4f/0xef0 net/bridge/br_input.c:189 br_nf_hook_thresh+0x1ed/0x220 br_nf_pre_routing_finish_ipv6+0x50f/0x540 NF_HOOK include/linux/netfilter.h:304 [inline] br_nf_pre_routing_ipv6+0x1e3/0x2a0 net/bridge/br_netfilter_ipv6.c:178 br_nf_pre_routing+0x526/0xba0 net/bridge/br_netfilter_hooks.c:508 nf_hook_entry_hookfn include/linux/netfilter.h:144 [inline] nf_hook_bridge_pre net/bridge/br_input.c:272 [inline] br_handle_frame+0x4c9/0x940 net/bridge/br_input.c:417 __netif_receive_skb_core+0xa8a/0x21e0 net/core/dev.c:5417 __netif_receive_skb_one_core net/core/dev.c:5521 [inline] __netif_receive_skb+0x57/0x1b0 net/core/dev.c:5637 process_backlog+0x21f/0x380 net/core/dev.c:5965 __napi_poll+0x60/0x3b0 net/core/dev.c:6527 napi_poll net/core/dev.c:6594 [inline] net_rx_action+0x32b/0x750 net/core/dev.c:6727 __do_softirq+0xc1/0x265 kernel/softirq.c:553 run_ksoftirqd+0x17/0x20 kernel/softirq.c:921 smpboot_thread_fn+0x30a/0x4a0 kernel/smpboot.c:164 kthread+0x1d7/0x210 kernel/kthread.c:388 ret_from_fork+0x48/0x60 arch/x86/kernel/process.c:147 ret_from_fork_asm+0x11/0x20 arch/x86/entry/entry_64.S:304
read-write to 0xffff8881374b2178 of 8 bytes by interrupt on cpu 0: br_handle_frame_finish+0xd4f/0xef0 net/bridge/br_input.c:189 br_nf_hook_thresh+0x1ed/0x220 br_nf_pre_routing_finish_ipv6+0x50f/0x540 NF_HOOK include/linux/netfilter.h:304 [inline] br_nf_pre_routing_ipv6+0x1e3/0x2a0 net/bridge/br_netfilter_ipv6.c:178 br_nf_pre_routing+0x526/0xba0 net/bridge/br_netfilter_hooks.c:508 nf_hook_entry_hookfn include/linux/netfilter.h:144 [inline] nf_hook_bridge_pre net/bridge/br_input.c:272 [inline] br_handle_frame+0x4c9/0x940 net/bridge/br_input.c:417 __netif_receive_skb_core+0xa8a/0x21e0 net/core/dev.c:5417 __netif_receive_skb_one_core net/core/dev.c:5521 [inline] __netif_receive_skb+0x57/0x1b0 net/core/dev.c:5637 process_backlog+0x21f/0x380 net/core/dev.c:5965 __napi_poll+0x60/0x3b0 net/core/dev.c:6527 napi_poll net/core/dev.c:6594 [inline] net_rx_action+0x32b/0x750 net/core/dev.c:6727 __do_softirq+0xc1/0x265 kernel/softirq.c:553 do_softirq+0x5e/0x90 kernel/softirq.c:454 __local_bh_enable_ip+0x64/0x70 kernel/softirq.c:381 __raw_spin_unlock_bh include/linux/spinlock_api_smp.h:167 [inline] _raw_spin_unlock_bh+0x36/0x40 kernel/locking/spinlock.c:210 spin_unlock_bh include/linux/spinlock.h:396 [inline] batadv_tt_local_purge+0x1a8/0x1f0 net/batman-adv/translation-table.c:1356 batadv_tt_purge+0x2b/0x630 net/batman-adv/translation-table.c:3560 process_one_work kernel/workqueue.c:2630 [inline] process_scheduled_works+0x5b8/0xa30 kernel/workqueue.c:2703 worker_thread+0x525/0x730 kernel/workqueue.c:2784 kthread+0x1d7/0x210 kernel/kthread.c:388 ret_from_fork+0x48/0x60 arch/x86/kernel/process.c:147 ret_from_fork_asm+0x11/0x20 arch/x86/entry/entry_64.S:304
value changed: 0x00000000000d7190 -> 0x00000000000d7191
Reported by Kernel Concurrency Sanitizer on: CPU: 0 PID: 14848 Comm: kworker/u4:11 Not tainted 6.6.0-rc1-syzkaller-00236-gad8a69f361b9 #0
Fixes: 1c29fc4989bc ("[BRIDGE]: keep track of received multicast packets") Reported-by: syzbot syzkaller@googlegroups.com Signed-off-by: Eric Dumazet edumazet@google.com Cc: Roopa Prabhu roopa@nvidia.com Cc: Nikolay Aleksandrov razor@blackwall.org Cc: bridge@lists.linux-foundation.org Acked-by: Nikolay Aleksandrov razor@blackwall.org Link: https://lore.kernel.org/r/20230918091351.1356153-1-edumazet@google.com Signed-off-by: Paolo Abeni pabeni@redhat.com Signed-off-by: Sasha Levin sashal@kernel.org Signed-off-by: Zhengchao Shao shaozhengchao@huawei.com --- net/bridge/br_forward.c | 4 ++-- net/bridge/br_input.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 4610f3a13966..f2ef75c7ccc6 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -118,7 +118,7 @@ static int deliver_clone(const struct net_bridge_port *prev,
skb = skb_clone(skb, GFP_ATOMIC); if (!skb) { - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); return -ENOMEM; }
@@ -255,7 +255,7 @@ static void maybe_deliver_addr(struct net_bridge_port *p, struct sk_buff *skb,
skb = skb_copy(skb, GFP_ATOMIC); if (!skb) { - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); return; }
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index bf5bf148091f..52dd0708fd14 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -145,12 +145,12 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb if ((mdst && mdst->host_joined) || br_multicast_is_router(br)) { local_rcv = true; - br->dev->stats.multicast++; + DEV_STATS_INC(br->dev, multicast); } mcast_hit = true; } else { local_rcv = true; - br->dev->stats.multicast++; + DEV_STATS_INC(br->dev, multicast); } break; case BR_PKT_UNICAST:
From: Felix Riemann felix.riemann@sma.de
stable inclusion from stable-v5.10.169 commit e2bf52ff159db37b5b60d34069464c00a1f54fd6 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I95ATV CVE: CVE-2023-52578
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
commit 9b55d3f0a69af649c62cbc2633e6d695bb3cc583 upstream.
When converting net_device_stats to rtnl_link_stats64 sign extension is triggered on ILP32 machines as 6c1c509778 changed the previous "ulong -> u64" conversion to "long -> u64" by accessing the net_device_stats fields through a (signed) atomic_long_t.
This causes for example the received bytes counter to jump to 16EiB after having received 2^31 bytes. Casting the atomic value to "unsigned long" beforehand converting it into u64 avoids this.
Fixes: 6c1c5097781f ("net: add atomic_long_t to net_device_stats fields") Signed-off-by: Felix Riemann felix.riemann@sma.de Reviewed-by: Eric Dumazet edumazet@google.com Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Zhengchao Shao shaozhengchao@huawei.com --- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/core/dev.c b/net/core/dev.c index 38165f9492b6..73e3192d62b8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -10354,7 +10354,7 @@ void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,
BUILD_BUG_ON(n > sizeof(*stats64) / sizeof(u64)); for (i = 0; i < n; i++) - dst[i] = atomic_long_read(&src[i]); + dst[i] = (unsigned long)atomic_long_read(&src[i]); /* zero out counters that only exist in rtnl_link_stats64 */ memset((char *)stats64 + n * sizeof(u64), 0, sizeof(*stats64) - n * sizeof(u64));
Offering: HULK hulk inclusion category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/I95ATV CVE: CVE-2023-52578
--------------------------------
fix kabi check warning.
Signed-off-by: Zhengchao Shao shaozhengchao@huawei.com --- include/linux/netdevice.h | 69 ++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 23 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5af9230f5741..e2c43666be48 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -174,29 +174,52 @@ static inline bool dev_xmit_complete(int rc) }
struct net_device_stats { - NET_DEV_STAT(rx_packets); - NET_DEV_STAT(tx_packets); - NET_DEV_STAT(rx_bytes); - NET_DEV_STAT(tx_bytes); - NET_DEV_STAT(rx_errors); - NET_DEV_STAT(tx_errors); - NET_DEV_STAT(rx_dropped); - NET_DEV_STAT(tx_dropped); - NET_DEV_STAT(multicast); - NET_DEV_STAT(collisions); - NET_DEV_STAT(rx_length_errors); - NET_DEV_STAT(rx_over_errors); - NET_DEV_STAT(rx_crc_errors); - NET_DEV_STAT(rx_frame_errors); - NET_DEV_STAT(rx_fifo_errors); - NET_DEV_STAT(rx_missed_errors); - NET_DEV_STAT(tx_aborted_errors); - NET_DEV_STAT(tx_carrier_errors); - NET_DEV_STAT(tx_fifo_errors); - NET_DEV_STAT(tx_heartbeat_errors); - NET_DEV_STAT(tx_window_errors); - NET_DEV_STAT(rx_compressed); - NET_DEV_STAT(tx_compressed); + RH_KABI_REPLACE(unsigned long rx_packets, + NET_DEV_STAT(rx_packets)) + RH_KABI_REPLACE(unsigned long tx_packets, + NET_DEV_STAT(tx_packets)) + RH_KABI_REPLACE(unsigned long rx_bytes, + NET_DEV_STAT(rx_bytes)) + RH_KABI_REPLACE(unsigned long tx_bytes, + NET_DEV_STAT(tx_bytes)) + RH_KABI_REPLACE(unsigned long rx_errors, + NET_DEV_STAT(rx_errors)) + RH_KABI_REPLACE(unsigned long tx_errors, + NET_DEV_STAT(tx_errors)) + RH_KABI_REPLACE(unsigned long rx_dropped, + NET_DEV_STAT(rx_dropped)) + RH_KABI_REPLACE(unsigned long tx_dropped, + NET_DEV_STAT(tx_dropped)) + RH_KABI_REPLACE(unsigned long multicast, + NET_DEV_STAT(multicast)) + RH_KABI_REPLACE(unsigned long collisions, + NET_DEV_STAT(collisions)) + RH_KABI_REPLACE(unsigned long rx_length_errors, + NET_DEV_STAT(rx_length_errors)) + RH_KABI_REPLACE(unsigned long rx_over_errors, + NET_DEV_STAT(rx_over_errors)) + RH_KABI_REPLACE(unsigned long rx_crc_errors, + NET_DEV_STAT(rx_crc_errors)) + RH_KABI_REPLACE(unsigned long rx_frame_errors, + NET_DEV_STAT(rx_frame_errors)) + RH_KABI_REPLACE(unsigned long rx_fifo_errors, + NET_DEV_STAT(rx_fifo_errors)) + RH_KABI_REPLACE(unsigned long rx_missed_errors, + NET_DEV_STAT(rx_missed_errors)) + RH_KABI_REPLACE(unsigned long tx_aborted_errors, + NET_DEV_STAT(tx_aborted_errors)) + RH_KABI_REPLACE(unsigned long tx_carrier_errors, + NET_DEV_STAT(tx_carrier_errors)) + RH_KABI_REPLACE(unsigned long tx_fifo_errors, + NET_DEV_STAT(tx_fifo_errors)) + RH_KABI_REPLACE(unsigned long tx_heartbeat_errors, + NET_DEV_STAT(tx_heartbeat_errors)) + RH_KABI_REPLACE(unsigned long tx_window_errors, + NET_DEV_STAT(tx_window_errors)) + RH_KABI_REPLACE(unsigned long rx_compressed, + NET_DEV_STAT(rx_compressed)) + RH_KABI_REPLACE(unsigned long tx_compressed, + NET_DEV_STAT(tx_compressed)) }; #undef NET_DEV_STAT