net_dim_init_irq_moder() publishes the allocated struct via
dev->irq_moder before copying the moderation profiles.  If a
kmemdup() fails, the error path frees the object but leaves
dev->irq_moder dangling.  net_dim_free_irq_moder() treats a
non-NULL irq_moder as live: it dereferences
dev->irq_moder->rx_profile / tx_profile under RTNL and frees the
object a second time.

On current mainline the only in-tree caller of
net_dim_init_irq_moder() is virtnet_probe(), and its failure path
today does "goto free", which only reaches free_netdev(); nothing
calls net_dim_free_irq_moder() there, so the dangling pointer is
never dereferenced.  The bug becomes reachable once the caller
unwinds through the existing free_irq_moder label, as virtio-net
starts doing in the follow-up fix:

    virtnet_probe()
      virtnet_init_irq_moder()
        net_dim_init_irq_moder()    /* kmemdup fails:
                                       kfree(moder),
                                       dev->irq_moder stale */
      goto free_irq_moder
      virtnet_free_irq_moder()
        net_dim_free_irq_moder()    /* non-NULL irq_moder =>
                                       rtnl_dereference(
                                         dev->irq_moder->rx_profile)
                                       use-after-free,
                                       kfree() a second time */

This patch is the prerequisite that makes such an unwind safe;
without it the follow-up would trade the virtqueue leak for a
use-after-free and a double kfree on this error path.

NULL the pointer after freeing it, so a later
net_dim_free_irq_moder() is a no-op and the device is left as if
initialization never happened.

Fixes: f750dfe825b90 ("ethtool: provide customized dim profile management")
Suggested-by: Jakub Kicinski <[email protected]>
Signed-off-by: Jianlin Shi <[email protected]>
---
 lib/dim/net_dim.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lib/dim/net_dim.c b/lib/dim/net_dim.c
index d8d4f6553559..bf59deae6a29 100644
--- a/lib/dim/net_dim.c
+++ b/lib/dim/net_dim.c
@@ -141,6 +141,7 @@ int net_dim_init_irq_moder(struct net_device *dev, u8 
profile_flags,
        kfree(rxp);
 free_moder:
        kfree(moder);
+       dev->irq_moder = NULL;
        return -ENOMEM;
 }
 EXPORT_SYMBOL(net_dim_init_irq_moder);
-- 
2.43.0


Reply via email to