Skip to content

Commit 07a756a

Browse files
mhklinuxliuw
authored andcommittedDec 9, 2024
Drivers: hv: util: Avoid accessing a ringbuffer not initialized yet
If the KVP (or VSS) daemon starts before the VMBus channel's ringbuffer is fully initialized, we can hit the panic below: hv_utils: Registering HyperV Utility Driver hv_vmbus: registering driver hv_utils ... BUG: kernel NULL pointer dereference, address: 0000000000000000 CPU: 44 UID: 0 PID: 2552 Comm: hv_kvp_daemon Tainted: G E 6.11.0-rc3+ #1 RIP: 0010:hv_pkt_iter_first+0x12/0xd0 Call Trace: ... vmbus_recvpacket hv_kvp_onchannelcallback vmbus_on_event tasklet_action_common tasklet_action handle_softirqs irq_exit_rcu sysvec_hyperv_stimer0 </IRQ> <TASK> asm_sysvec_hyperv_stimer0 ... kvp_register_done hvt_op_read vfs_read ksys_read __x64_sys_read This can happen because the KVP/VSS channel callback can be invoked even before the channel is fully opened: 1) as soon as hv_kvp_init() -> hvutil_transport_init() creates /dev/vmbus/hv_kvp, the kvp daemon can open the device file immediately and register itself to the driver by writing a message KVP_OP_REGISTER1 to the file (which is handled by kvp_on_msg() ->kvp_handle_handshake()) and reading the file for the driver's response, which is handled by hvt_op_read(), which calls hvt->on_read(), i.e. kvp_register_done(). 2) the problem with kvp_register_done() is that it can cause the channel callback to be called even before the channel is fully opened, and when the channel callback is starting to run, util_probe()-> vmbus_open() may have not initialized the ringbuffer yet, so the callback can hit the panic of NULL pointer dereference. To reproduce the panic consistently, we can add a "ssleep(10)" for KVP in __vmbus_open(), just before the first hv_ringbuffer_init(), and then we unload and reload the driver hv_utils, and run the daemon manually within the 10 seconds. Fix the panic by reordering the steps in util_probe() so the char dev entry used by the KVP or VSS daemon is not created until after vmbus_open() has completed. This reordering prevents the race condition from happening. Reported-by: Dexuan Cui <decui@microsoft.com> Fixes: e0fa3e5 ("Drivers: hv: utils: fix a race on userspace daemons registration") Cc: stable@vger.kernel.org Signed-off-by: Michael Kelley <mhklinux@outlook.com> Acked-by: Wei Liu <wei.liu@kernel.org> Link: https://lore.kernel.org/r/20241106154247.2271-3-mhklinux@outlook.com Signed-off-by: Wei Liu <wei.liu@kernel.org> Message-ID: <20241106154247.2271-3-mhklinux@outlook.com>
1 parent 96e052d commit 07a756a

File tree

5 files changed

+24
-0
lines changed

5 files changed

+24
-0
lines changed
 

‎drivers/hv/hv_kvp.c

+6
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,12 @@ hv_kvp_init(struct hv_util_service *srv)
767767
*/
768768
kvp_transaction.state = HVUTIL_DEVICE_INIT;
769769

770+
return 0;
771+
}
772+
773+
int
774+
hv_kvp_init_transport(void)
775+
{
770776
hvt = hvutil_transport_init(kvp_devname, CN_KVP_IDX, CN_KVP_VAL,
771777
kvp_on_msg, kvp_on_reset);
772778
if (!hvt)

‎drivers/hv/hv_snapshot.c

+6
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,12 @@ hv_vss_init(struct hv_util_service *srv)
389389
*/
390390
vss_transaction.state = HVUTIL_DEVICE_INIT;
391391

392+
return 0;
393+
}
394+
395+
int
396+
hv_vss_init_transport(void)
397+
{
392398
hvt = hvutil_transport_init(vss_devname, CN_VSS_IDX, CN_VSS_VAL,
393399
vss_on_msg, vss_on_reset);
394400
if (!hvt) {

‎drivers/hv/hv_util.c

+9
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ static struct hv_util_service util_heartbeat = {
141141
static struct hv_util_service util_kvp = {
142142
.util_cb = hv_kvp_onchannelcallback,
143143
.util_init = hv_kvp_init,
144+
.util_init_transport = hv_kvp_init_transport,
144145
.util_pre_suspend = hv_kvp_pre_suspend,
145146
.util_pre_resume = hv_kvp_pre_resume,
146147
.util_deinit = hv_kvp_deinit,
@@ -149,6 +150,7 @@ static struct hv_util_service util_kvp = {
149150
static struct hv_util_service util_vss = {
150151
.util_cb = hv_vss_onchannelcallback,
151152
.util_init = hv_vss_init,
153+
.util_init_transport = hv_vss_init_transport,
152154
.util_pre_suspend = hv_vss_pre_suspend,
153155
.util_pre_resume = hv_vss_pre_resume,
154156
.util_deinit = hv_vss_deinit,
@@ -611,6 +613,13 @@ static int util_probe(struct hv_device *dev,
611613
if (ret)
612614
goto error;
613615

616+
if (srv->util_init_transport) {
617+
ret = srv->util_init_transport();
618+
if (ret) {
619+
vmbus_close(dev->channel);
620+
goto error;
621+
}
622+
}
614623
return 0;
615624

616625
error:

‎drivers/hv/hyperv_vmbus.h

+2
Original file line numberDiff line numberDiff line change
@@ -370,12 +370,14 @@ void vmbus_on_event(unsigned long data);
370370
void vmbus_on_msg_dpc(unsigned long data);
371371

372372
int hv_kvp_init(struct hv_util_service *srv);
373+
int hv_kvp_init_transport(void);
373374
void hv_kvp_deinit(void);
374375
int hv_kvp_pre_suspend(void);
375376
int hv_kvp_pre_resume(void);
376377
void hv_kvp_onchannelcallback(void *context);
377378

378379
int hv_vss_init(struct hv_util_service *srv);
380+
int hv_vss_init_transport(void);
379381
void hv_vss_deinit(void);
380382
int hv_vss_pre_suspend(void);
381383
int hv_vss_pre_resume(void);

‎include/linux/hyperv.h

+1
Original file line numberDiff line numberDiff line change
@@ -1559,6 +1559,7 @@ struct hv_util_service {
15591559
void *channel;
15601560
void (*util_cb)(void *);
15611561
int (*util_init)(struct hv_util_service *);
1562+
int (*util_init_transport)(void);
15621563
void (*util_deinit)(void);
15631564
int (*util_pre_suspend)(void);
15641565
int (*util_pre_resume)(void);

0 commit comments

Comments
 (0)
Please sign in to comment.