CVE-2024-38586
Heiner Kallweit (5): r8169: remove unneeded memory barrier in rtl_tx r8169: use READ_ONCE in rtl_tx_slots_avail r8169: improve rtl_tx r8169: improve rtl8169_start_xmit r8169: remove nr_frags argument from rtl_tx_slots_avail
Ken Milmore (1): r8169: Fix possible ring buffer corruption on fragmented Tx packets.
drivers/net/ethernet/realtek/r8169_main.c | 44 ++++++++++------------- 1 file changed, 19 insertions(+), 25 deletions(-)
From: Heiner Kallweit hkallweit1@gmail.com
mainline inclusion from mainline-v5.11-rc1 commit 3a689e34973e8717cd57991c6fcf527dc56062b5 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA6SF4 CVE: CVE-2024-38586
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
---------------------------
tp->dirty_tx isn't changed outside rtl_tx(). Therefore I see no need to guarantee a specific order of reading tp->dirty_tx and tp->cur_tx. Having said that we can remove the memory barrier. In addition use READ_ONCE() when reading tp->cur_tx because it can change in parallel to rtl_tx().
Signed-off-by: Heiner Kallweit hkallweit1@gmail.com Link: https://lore.kernel.org/r/2264563a-fa9e-11b0-2c42-31bc6b8e2790@gmail.com Signed-off-by: Jakub Kicinski kuba@kernel.org Signed-off-by: Liu Jian liujian56@huawei.com --- drivers/net/ethernet/realtek/r8169_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 5f42debd0de9..1545e5addd2c 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -4478,9 +4478,8 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp, unsigned int dirty_tx, tx_left, bytes_compl = 0, pkts_compl = 0;
dirty_tx = tp->dirty_tx; - smp_rmb();
- for (tx_left = tp->cur_tx - dirty_tx; tx_left > 0; tx_left--) { + for (tx_left = READ_ONCE(tp->cur_tx) - dirty_tx; tx_left; tx_left--) { unsigned int entry = dirty_tx % NUM_TX_DESC; struct sk_buff *skb = tp->tx_skb[entry].skb; u32 status;
From: Heiner Kallweit hkallweit1@gmail.com
mainline inclusion from mainline-v5.11-rc1 commit 95f3c5458dfa5856bb110e31d156e00d894d0134 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA6SF4 CVE: CVE-2024-38586
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
---------------------------
tp->dirty_tx and tp->cur_tx may be changed by a racing rtl_tx() or rtl8169_start_xmit(). Use READ_ONCE() to annotate the races and ensure that the compiler doesn't use cached values.
Signed-off-by: Heiner Kallweit hkallweit1@gmail.com Link: https://lore.kernel.org/r/5676fee3-f6b4-84f2-eba5-c64949a371ad@gmail.com Signed-off-by: Jakub Kicinski kuba@kernel.org Signed-off-by: Liu Jian liujian56@huawei.com --- drivers/net/ethernet/realtek/r8169_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 1545e5addd2c..1142b4679cc2 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -4256,7 +4256,8 @@ static bool rtl8169_tso_csum_v2(struct rtl8169_private *tp, static bool rtl_tx_slots_avail(struct rtl8169_private *tp, unsigned int nr_frags) { - unsigned int slots_avail = tp->dirty_tx + NUM_TX_DESC - tp->cur_tx; + unsigned int slots_avail = READ_ONCE(tp->dirty_tx) + NUM_TX_DESC + - READ_ONCE(tp->cur_tx);
/* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */ return slots_avail > nr_frags;
From: Heiner Kallweit hkallweit1@gmail.com
mainline inclusion from mainline-v5.11-rc1 commit ca1ab89cd2d654661f559bd83ad9fc7323cb6c86 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA6SF4 CVE: CVE-2024-38586
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
---------------------------
We can simplify the for() condition and eliminate variable tx_left. The change also considers that tp->cur_tx may be incremented by a racing rtl8169_start_xmit(). In addition replace the write to tp->dirty_tx and the following smp_mb() with an equivalent call to smp_store_mb(). This implicitly adds a WRITE_ONCE() to the write.
Signed-off-by: Heiner Kallweit hkallweit1@gmail.com Link: https://lore.kernel.org/r/c2e19e5e-3d3f-d663-af32-13c3374f5def@gmail.com Signed-off-by: Jakub Kicinski kuba@kernel.org
Conflicts: drivers/net/ethernet/realtek/r8169_main.c [We did not backport f1d5470594ff.] Signed-off-by: Liu Jian liujian56@huawei.com --- drivers/net/ethernet/realtek/r8169_main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 1142b4679cc2..a396e52a90c6 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -4476,11 +4476,11 @@ static void rtl8169_pcierr_interrupt(struct net_device *dev) static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp, int budget) { - unsigned int dirty_tx, tx_left, bytes_compl = 0, pkts_compl = 0; + unsigned int dirty_tx, bytes_compl = 0, pkts_compl = 0;
dirty_tx = tp->dirty_tx;
- for (tx_left = READ_ONCE(tp->cur_tx) - dirty_tx; tx_left; tx_left--) { + while (READ_ONCE(tp->cur_tx) != dirty_tx) { unsigned int entry = dirty_tx % NUM_TX_DESC; struct sk_buff *skb = tp->tx_skb[entry].skb; u32 status; @@ -4504,7 +4504,6 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
rtl_inc_priv_stats(&tp->tx_stats, pkts_compl, bytes_compl);
- tp->dirty_tx = dirty_tx; /* Sync with rtl8169_start_xmit: * - publish dirty_tx ring index (write barrier) * - refresh cur_tx ring index and queue status (read barrier) @@ -4512,7 +4511,7 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp, * a racing xmit thread can only have a right view of the * ring status. */ - smp_mb(); + smp_store_mb(tp->dirty_tx, dirty_tx); if (netif_queue_stopped(dev) && rtl_tx_slots_avail(tp, MAX_SKB_FRAGS)) { netif_wake_queue(dev);
From: Heiner Kallweit hkallweit1@gmail.com
mainline inclusion from mainline-v5.11-rc1 commit 41294e6a434d4f19e957c55b275ea0324f275009 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA6SF4 CVE: CVE-2024-38586
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
---------------------------
Improve the following in rtl8169_start_xmit: - tp->cur_tx can be accessed in parallel by rtl_tx(), therefore annotate the race by using WRITE_ONCE - avoid checking stop_queue a second time by moving the doorbell check - netif_stop_queue() uses atomic operation set_bit() that includes a full memory barrier on some platforms, therefore use smp_mb__after_atomic to avoid overhead
Signed-off-by: Heiner Kallweit hkallweit1@gmail.com Link: https://lore.kernel.org/r/80085451-3eaf-507a-c7c0-08d607c46fbc@gmail.com Signed-off-by: Jakub Kicinski kuba@kernel.org Signed-off-by: Liu Jian liujian56@huawei.com --- drivers/net/ethernet/realtek/r8169_main.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index a396e52a90c6..88a571b3e2e8 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -4338,7 +4338,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, /* rtl_tx needs to see descriptor changes before updated tp->cur_tx */ smp_wmb();
- tp->cur_tx += frags + 1; + WRITE_ONCE(tp->cur_tx, tp->cur_tx + frags + 1);
stop_queue = !rtl_tx_slots_avail(tp, MAX_SKB_FRAGS); if (unlikely(stop_queue)) { @@ -4347,13 +4347,6 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, */ smp_wmb(); netif_stop_queue(dev); - door_bell = true; - } - - if (door_bell) - rtl8169_doorbell(tp); - - if (unlikely(stop_queue)) { /* Sync with rtl_tx: * - publish queue status and cur_tx ring index (write barrier) * - refresh dirty_tx ring index (read barrier). @@ -4361,11 +4354,15 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, * status and forget to wake up queue, a racing rtl_tx thread * can't. */ - smp_mb(); + smp_mb__after_atomic(); if (rtl_tx_slots_avail(tp, MAX_SKB_FRAGS)) netif_start_queue(dev); + door_bell = true; }
+ if (door_bell) + rtl8169_doorbell(tp); + return NETDEV_TX_OK;
err_dma_1:
From: Heiner Kallweit hkallweit1@gmail.com
mainline inclusion from mainline-v5.11-rc1 commit 83c317d7b36bb3858cf1cb86d2635ec3f3bd6ea3 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA6SF4 CVE: CVE-2024-38586
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
---------------------------
The only time when nr_frags isn't SKB_MAX_FRAGS is when entering rtl8169_start_xmit(). However we can use SKB_MAX_FRAGS also here because when queue isn't stopped there should always be room for MAX_SKB_FRAGS + 1 descriptors.
Signed-off-by: Heiner Kallweit hkallweit1@gmail.com Link: https://lore.kernel.org/r/3d1f2ad7-31d5-2cac-4f4a-394f8a3cab63@gmail.com Signed-off-by: Jakub Kicinski kuba@kernel.org Signed-off-by: Liu Jian liujian56@huawei.com --- drivers/net/ethernet/realtek/r8169_main.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 88a571b3e2e8..06a034ef52e1 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -4253,14 +4253,13 @@ static bool rtl8169_tso_csum_v2(struct rtl8169_private *tp, return true; }
-static bool rtl_tx_slots_avail(struct rtl8169_private *tp, - unsigned int nr_frags) +static bool rtl_tx_slots_avail(struct rtl8169_private *tp) { unsigned int slots_avail = READ_ONCE(tp->dirty_tx) + NUM_TX_DESC - READ_ONCE(tp->cur_tx);
/* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */ - return slots_avail > nr_frags; + return slots_avail > MAX_SKB_FRAGS; }
/* Versions RTL8102e and from RTL8168c onwards support csum_v2 */ @@ -4295,7 +4294,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
txd_first = tp->TxDescArray + entry;
- if (unlikely(!rtl_tx_slots_avail(tp, frags))) { + if (unlikely(!rtl_tx_slots_avail(tp))) { if (net_ratelimit()) netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); goto err_stop_0; @@ -4340,7 +4339,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
WRITE_ONCE(tp->cur_tx, tp->cur_tx + frags + 1);
- stop_queue = !rtl_tx_slots_avail(tp, MAX_SKB_FRAGS); + stop_queue = !rtl_tx_slots_avail(tp); if (unlikely(stop_queue)) { /* Avoid wrongly optimistic queue wake-up: rtl_tx thread must * not miss a ring update when it notices a stopped queue. @@ -4355,7 +4354,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, * can't. */ smp_mb__after_atomic(); - if (rtl_tx_slots_avail(tp, MAX_SKB_FRAGS)) + if (rtl_tx_slots_avail(tp)) netif_start_queue(dev); door_bell = true; } @@ -4509,10 +4508,8 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp, * ring status. */ smp_store_mb(tp->dirty_tx, dirty_tx); - if (netif_queue_stopped(dev) && - rtl_tx_slots_avail(tp, MAX_SKB_FRAGS)) { + if (netif_queue_stopped(dev) && rtl_tx_slots_avail(tp)) netif_wake_queue(dev); - } /* * 8168 hack: TxPoll requests are lost when the Tx packets are * too close. Let's kick an extra TxPoll request when a burst
From: Ken Milmore ken.milmore@gmail.com
mainline inclusion from mainline-v6.10-rc1 commit c71e3a5cffd5309d7f84444df03d5b72600cc417 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA6SF4 CVE: CVE-2024-38586
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
---------------------------
An issue was found on the RTL8125b when transmitting small fragmented packets, whereby invalid entries were inserted into the transmit ring buffer, subsequently leading to calls to dma_unmap_single() with a null address.
This was caused by rtl8169_start_xmit() not noticing changes to nr_frags which may occur when small packets are padded (to work around hardware quirks) in rtl8169_tso_csum_v2().
To fix this, postpone inspecting nr_frags until after any padding has been applied.
Fixes: 9020845fb5d6 ("r8169: improve rtl8169_start_xmit") Cc: stable@vger.kernel.org Signed-off-by: Ken Milmore ken.milmore@gmail.com Reviewed-by: Heiner Kallweit hkallweit1@gmail.com Link: https://lore.kernel.org/r/27ead18b-c23d-4f49-a020-1fc482c5ac95@gmail.com Signed-off-by: Paolo Abeni pabeni@redhat.com
Conflicts: drivers/net/ethernet/realtek/r8169_main.c [We did not backport bd4bdeb4f2902.] Signed-off-by: Liu Jian liujian56@huawei.com --- drivers/net/ethernet/realtek/r8169_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 06a034ef52e1..8c6d76e75b53 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -4285,11 +4285,11 @@ static void rtl8169_doorbell(struct rtl8169_private *tp) static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev) { - unsigned int frags = skb_shinfo(skb)->nr_frags; struct rtl8169_private *tp = netdev_priv(dev); unsigned int entry = tp->cur_tx % NUM_TX_DESC; struct TxDesc *txd_first, *txd_last; bool stop_queue, door_bell; + unsigned int frags; u32 opts[2];
txd_first = tp->TxDescArray + entry; @@ -4315,6 +4315,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, entry, false))) goto err_dma_0;
+ frags = skb_shinfo(skb)->nr_frags; if (frags) { if (rtl8169_xmit_frags(tp, skb, opts, entry)) goto err_dma_1;
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/9657 邮件列表地址:https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/T...
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/9657 Mailing list address: https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/T...