From: Yuqiang Sun <izaiahsun@gmail.com>
To: haiyangz@microsoft.com, wei.liu@kernel.org, decui@microsoft.com
Cc: mhklinux@outlook.com, stephen@networkplumber.org,
linmq006@gmail.com, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, stable@vger.kernel.org
Subject: [PATCH 1/2] Drivers: hv: vmbus: Keep the channel when vmbus_add_channel_kobj() fails
Date: Mon, 2 Nov 2026 17:47:18 +0800 [thread overview]
Message-ID: <20261102094719.35760-2-izaiahsun@gmail.com> (raw)
In-Reply-To: <20261102094719.35760-1-izaiahsun@gmail.com>
The channel is freed by the release of its kobject, and free_channel()
drops that reference. Since commit 8bc69f86328e ("Drivers: hv: vmbus:
Fix memory leak in vmbus_add_channel_kobj"), vmbus_add_channel_kobj()
also drops it when it fails, as kobject_init_and_add() asks for. That
frees the channel, and both callers then go on with it:
vmbus_add_channel_work() unlinks a sub-channel, unmaps and releases its
relid and calls free_channel(), a use after free and a second
kobject_put(); for a primary channel, vmbus_device_register() unwinds
through vmbus_device_release(), which removes the channel too.
The kobject is also only initialized in vmbus_add_channel_kobj(), so the
error paths before it, for instance a vmbus_device_create() failure in
vmbus_add_channel_work(), call free_channel() on a kobject that was
never initialized: kobject_put() warns and does nothing, so the channel
is leaked, and removing the attribute group from a kobject that is not
in sysfs warns as well.
Initialize the kobject when the channel is allocated, so that
free_channel() is the one place that drops its reference on every path.
vmbus_add_channel_kobj() then only adds it, and leaves the reference to
its callers when it fails. Remove the attribute group only if the
kobject was added.
Fixes: c2e5df616e1a ("vmbus: add per-channel sysfs info")
Fixes: 8bc69f86328e ("Drivers: hv: vmbus: Fix memory leak in vmbus_add_channel_kobj")
Cc: stable@vger.kernel.org
Signed-off-by: Yuqiang Sun <izaiahsun@gmail.com>
---
Notes:
The bug was found with kbound, a research static analysis prototype.
The patch, the changelog and the test setup were written with the help
of an AI coding assistant (Claude); I have reviewed them and tested them
as described in the cover letter.
drivers/hv/channel_mgmt.c | 5 +++--
drivers/hv/hyperv_vmbus.h | 1 +
drivers/hv/vmbus_drv.c | 25 ++++++++++++++++++-------
3 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index c42def04ac41..cb1d8d76928f 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -367,6 +367,7 @@ static struct vmbus_channel *alloc_channel(void)
vmbus_on_event, (unsigned long)channel);
hv_ringbuffer_pre_init(channel);
+ vmbus_init_channel_kobj(channel);
return channel;
}
@@ -663,8 +664,8 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
mutex_unlock(&vmbus_connection.channel_mutex);
cpus_read_unlock();
/*
- * Don't call free_channel(), because newchannel->kobj
- * is not initialized yet.
+ * Don't call free_channel(): newchannel was never
+ * added, and its kobject holds nothing yet.
*/
kfree(newchannel);
WARN_ON_ONCE(1);
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 33923621a5a3..7865a519dd7f 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -408,6 +408,7 @@ struct hv_device *vmbus_device_create(const guid_t *type,
int vmbus_device_register(struct hv_device *child_device_obj);
void vmbus_device_unregister(struct hv_device *device_obj);
+void vmbus_init_channel_kobj(struct vmbus_channel *channel);
int vmbus_add_channel_kobj(struct hv_device *device_obj,
struct vmbus_channel *channel);
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 4692687cdbf3..fc0f16f51266 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -2084,6 +2084,15 @@ int hv_remove_ring_sysfs(struct vmbus_channel *channel)
}
EXPORT_SYMBOL_GPL(hv_remove_ring_sysfs);
+/*
+ * vmbus_init_channel_kobj - initialize the kobject that holds the channel's
+ * lifetime; free_channel() drops its reference
+ */
+void vmbus_init_channel_kobj(struct vmbus_channel *channel)
+{
+ kobject_init(&channel->kobj, &vmbus_chan_ktype);
+}
+
/*
* vmbus_add_channel_kobj - setup a sub-directory under device/channels
*/
@@ -2094,13 +2103,14 @@ int vmbus_add_channel_kobj(struct hv_device *dev, struct vmbus_channel *channel)
u32 relid = channel->offermsg.child_relid;
int ret;
+ /*
+ * On failure the kobject keeps its reference: the callers' error
+ * paths drop it in free_channel(), which also frees the channel.
+ */
kobj->kset = dev->channels_kset;
- ret = kobject_init_and_add(kobj, &vmbus_chan_ktype, NULL,
- "%u", relid);
- if (ret) {
- kobject_put(kobj);
+ ret = kobject_add(kobj, NULL, "%u", relid);
+ if (ret)
return ret;
- }
ret = sysfs_create_group(kobj, &vmbus_chan_group);
@@ -2109,7 +2119,6 @@ int vmbus_add_channel_kobj(struct hv_device *dev, struct vmbus_channel *channel)
* The calling functions' error handling paths will cleanup the
* empty channel directory.
*/
- kobject_put(kobj);
dev_err(device, "Unable to set up channel sysfs files\n");
return ret;
}
@@ -2124,7 +2133,9 @@ int vmbus_add_channel_kobj(struct hv_device *dev, struct vmbus_channel *channel)
*/
void vmbus_remove_channel_attr_group(struct vmbus_channel *channel)
{
- sysfs_remove_group(&channel->kobj, &vmbus_chan_group);
+ /* A channel that failed before vmbus_add_channel_kobj() has none */
+ if (channel->kobj.state_in_sysfs)
+ sysfs_remove_group(&channel->kobj, &vmbus_chan_group);
}
/*
--
2.53.0
next prev parent reply other threads:[~2026-11-02 9:47 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-02 9:47 [PATCH 0/2] Drivers: hv: vmbus: Fix the error paths of channel setup Yuqiang Sun
2026-11-02 9:47 ` Yuqiang Sun [this message]
2026-11-02 9:47 ` [PATCH 2/2] Drivers: hv: vmbus: Do not remove the channel when device registration fails Yuqiang Sun
2026-11-02 10:06 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261102094719.35760-2-izaiahsun@gmail.com \
--to=izaiahsun@gmail.com \
--cc=decui@microsoft.com \
--cc=haiyangz@microsoft.com \
--cc=linmq006@gmail.com \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mhklinux@outlook.com \
--cc=stable@vger.kernel.org \
--cc=stephen@networkplumber.org \
--cc=wei.liu@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.