hwrng: virtio - prevent use-after-free after a failed restore
PATCH 1/1 hwrng: virtio - prevent use-after-free after a failed restore
remove_common() frees the driver data during freeze, but does not clear vdev->priv, which points to freed memory. If probe_common() then fails during restore, a later remove or freeze can dereference the dangling pointer.
Clear vdev->priv on the probe_common() error cleanup path and before freeing the driver data in remove_common(). Also reset the device and return early from remove_common() if vdev->priv is already NULL for a safe teardown after a failed restore.
--- drivers/char/hw_random/virtio-rng.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/char/hw_random/virtio-rng.c +7 -0
@@ -190,6 +190,7 @@ static int probe_common(struct virtio_device *vdev) err_find:ida_free (&rng_index_ida, index); err_ida: + vdev->priv =NULL ;kfree (vi);return err; }
@@ -198,6 +199,11 @@ static void remove_common(struct virtio_device *vdev) {struct virtrng_info *vi = vdev->priv; +if (!vi) { +virtio_reset_device (vdev); +return ; + } + vi->hwrng_removed = true; vi->data_avail =0 ; vi->data_idx =0 ;
@@ -207,6 +213,7 @@ static void remove_common(struct virtio_device *vdev)virtio_reset_device (vdev); vdev->config->del_vqs (vdev);ida_free (&rng_index_ida, vi->index); + vdev->priv =NULL ;kfree (vi); }
[email protected]
AI reviewThank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] virtio-rng: data race on data_avail leads to deadlock --
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] virtio-rng: data race on data_avail leads to deadlock
--
commit 2904ec0356e25704559279a6336e7fed895c58d2
Author: Thorsten Blum <[email protected]>
hwrng: virtio - prevent use-after-free after a failed restore
remove_common() frees the driver data during freeze, but does not clear vdev->priv, which points to freed memory. Clear vdev->priv on the error cleanup path and before freeing the driver data in remove_common(). Also reset the device and return early from remove_common() if vdev->priv is already NULL.
Quoted 4 lines
drivers/char/hw_random/virtio-rng.c +0
-0
[ ... ]
drivers/char/hw_random/virtio-rng.c static void remove_common(struct virtio_device *vdev) · line 208
Quoted 11 lines
drivers/char/hw_random/virtio-rng.c +5
-0@@ -198,6 +199,11 @@ static void remove_common(struct virtio_device *vdev) {struct virtrng_info *vi = vdev->priv; +if (!vi) { +virtio_reset_device (vdev); +return ; + } + vi->hwrng_removed = true; vi->data_avail =0 ;
[Severity: High]
Does this code race with a reader and cause a deadlock?
In copy_data() in drivers/char/hw_random/virtio-rng.c, vi->data_avail is read, memcpy() is called, and then size is subtracted:
drivers/char/hw_random/virtio-rng.c:copy_data() {
...
memcpy(buf, vi->data + idx, size);
vi->data_idx += size;
vi->data_avail -= size;
if (vi->data_avail == 0)
request_entropy(vi);
return size;
}If remove_common() sets vi->data_avail to 0 before the subtraction, will this cause an unsigned integer underflow, leaving vi->data_avail as a huge value?
Since vi->data_avail != 0, copy_data() would skip calling request_entropy(). virtio_read() would then consume the single completion from remove_common().
Seeing a huge data_avail instead of 0, virtio_read() would continue looping and eventually block forever in wait_for_completion_killable() because no pending entropy request will trigger a new completion:
drivers/char/hw_random/virtio-rng.c:virtio_read() {
...
while (size != 0) {
/* data_avail is 0 but a request is pending */
ret = wait_for_completion_killable(&vi->have_data);
...
}Could this cause hwrng_unregister() in remove_common() to block forever waiting for the reader to drop its reference, deadlocking device removal?
Quoted 2 lines
drivers/char/hw_random/virtio-rng.c +0
-0vi->data_idx =0 ;
complete(&vi->have_data);
[ ... ]
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Hi Thorsten,
Quoted 31 lines · Thorsten Blum
remove_common()frees the driver data during freeze, but does not clearvdev->priv, which points to freed memory. Ifprobe_common()then fails during restore, a later remove or freeze can dereference the dangling pointer.Clear
vdev->privon theprobe_common()error cleanup path and before freeing the driver data inremove_common(). Also reset the device and return early fromremove_common()ifvdev->privis already NULL for a safe teardown after a failed restore.Fixes 08e53fbdb85c ("virtio-rng: support multiple virtio-rng devices")Signed-off-by Thorsten Blum <[email protected]>--- drivers/char/hw_random/virtio-rng.c | 7 +++++++ 1 file changed, 7 insertions(+)drivers/char/hw_random/virtio-rng.c +1
-0@@ -190,6 +190,7 @@ static int probe_common(struct virtio_device *vdev) err_find:ida_free (&rng_index_ida, index); err_ida: + vdev->priv =NULL ;kfree (vi);return err; }
drivers/char/hw_random/virtio-rng.c static void remove_common(struct virtio_device *vdev) · line 206
Quoted 8 lines
drivers/char/hw_random/virtio-rng.c +5
-0@@ -198,6 +199,11 @@ static void remove_common(struct virtio_device *vdev) {struct virtrng_info *vi = vdev->priv; +if (!vi) { +virtio_reset_device (vdev); +return ; + } +
The virtio_reset_device() is always called before setting vdev->priv to NULL. So the virtio device is already quiesced, no need to do early return. Isn't it?
drivers/char/hw_random/virtio-rng.c static void remove_common(struct virtio_device *vdev) · line 219
Quoted 12 lines
drivers/char/hw_random/virtio-rng.c +1
-0vi->hwrng_removed = true; vi->data_avail =0 ; vi->data_idx =0 ;@@ -207,6 +213,7 @@ static void remove_common(struct virtio_device *vdev)virtio_reset_device (vdev); vdev->config->del_vqs (vdev);ida_free (&rng_index_ida, vi->index); + vdev->priv =NULL ;kfree (vi); }
The rest of the hunks are fine.
Thanks,
PrasannaKumar
Hi PrasannaKumar,
Quoted 37 lines · PrasannaKumar Muralidharan
Hi Thorsten,
Quoted 34 lines · Thorsten Blum
remove_common()frees the driver data during freeze, but does not clearvdev->priv, which points to freed memory. Ifprobe_common()then fails during restore, a later remove or freeze can dereference the dangling pointer.Clear
vdev->privon theprobe_common()error cleanup path and before freeing the driver data inremove_common(). Also reset the device and return early fromremove_common()ifvdev->privis already NULL for a safe teardown after a failed restore.Fixes 08e53fbdb85c ("virtio-rng: support multiple virtio-rng devices")Signed-off-by Thorsten Blum <[email protected]>--- drivers/char/hw_random/virtio-rng.c | 7 +++++++ 1 file changed, 7 insertions(+)drivers/char/hw_random/virtio-rng.c +1
-0@@ -190,6 +190,7 @@ static int probe_common(struct virtio_device *vdev) err_find:ida_free (&rng_index_ida, index); err_ida: + vdev->priv =NULL ;kfree (vi);return err; }@@ -198,6 +199,11 @@ static void remove_common(struct virtio_device *vdev) {struct virtrng_info *vi = vdev->priv;
Quoted 9 lines
drivers/char/hw_random/virtio-rng.c static void remove_common(struct virtio_device *vdev) · line 206
Quoted 5 lines
drivers/char/hw_random/virtio-rng.c +5
-0+if (!vi) { +virtio_reset_device (vdev); +return ; + } +The
virtio_reset_device()is always called before settingvdev->privto NULL. So the virtio device is already quiesced, no need to do early return. Isn't it?
The check is still needed.
For example, during hibernation or suspend, .freeze (virtrng_freeze() -> remove_common()) frees vi and sets vdev->priv to NULL.
On resume, .restore (virtrng_restore()) calls probe_common(), which could fail and leave vdev->priv NULL. A later .freeze or .remove calls remove_common() again, which would dereference NULL without the check.
Best regards,
Thorsten
PrasannaKumar Muralidharan
Reviewed-byQuoted 51 lines · Thorsten Blum
Hi PrasannaKumar,
Quoted 37 lines · PrasannaKumar Muralidharan
Hi Thorsten,
Quoted 34 lines · Thorsten Blum
remove_common()frees the driver data during freeze, but does not clearvdev->priv, which points to freed memory. Ifprobe_common()then fails during restore, a later remove or freeze can dereference the dangling pointer.Clear
vdev->privon theprobe_common()error cleanup path and before freeing the driver data inremove_common(). Also reset the device and return early fromremove_common()ifvdev->privis already NULL for a safe teardown after a failed restore.Fixes 08e53fbdb85c ("virtio-rng: support multiple virtio-rng devices")Signed-off-by Thorsten Blum <[email protected]>--- drivers/char/hw_random/virtio-rng.c | 7 +++++++ 1 file changed, 7 insertions(+)drivers/char/hw_random/virtio-rng.c +1
-0@@ -190,6 +190,7 @@ static int probe_common(struct virtio_device *vdev) err_find:ida_free (&rng_index_ida, index); err_ida: + vdev->priv =NULL ;kfree (vi);return err; }@@ -198,6 +199,11 @@ static void remove_common(struct virtio_device *vdev) {struct virtrng_info *vi = vdev->priv;Quoted 9 lines
drivers/char/hw_random/virtio-rng.c static void remove_common(struct virtio_device *vdev) · line 206
Quoted 5 lines
drivers/char/hw_random/virtio-rng.c +5
-0+if (!vi) { +virtio_reset_device (vdev); +return ; + } +The
virtio_reset_device()is always called before settingvdev->privto NULL. So the virtio device is already quiesced, no need to do early return. Isn't it?
Quoted 8 lines
The check is still needed.
For example, during hibernation or suspend, .freeze (
virtrng_freeze()->remove_common()) frees vi and setsvdev->privto NULL.On resume, .restore (
virtrng_restore()) callsprobe_common(), which could fail and leavevdev->privNULL. A later .freeze or .remove callsremove_common()again, which would dereference NULL without the check.
Yes, it makes sense.
Quoted 2 lines
Best regards,
Thorsten
Change looks good to me.
Feel free to add:
Thanks,
PrasannaKumar
Herbert Xu
maintainerQuoted 10 lines · Thorsten Blum
remove_common()frees the driver data during freeze, but does not clearvdev->priv, which points to freed memory. Ifprobe_common()then fails during restore, a later remove or freeze can dereference the dangling pointer.Clear
vdev->privon theprobe_common()error cleanup path and before freeing the driver data inremove_common(). Also reset the device and return early fromremove_common()ifvdev->privis already NULL for a safe teardown after a failed restore.
Quoted 6 lines
Fixes 08e53fbdb85c ("virtio-rng: support multiple virtio-rng devices")Signed-off-by Thorsten Blum <[email protected]>--- drivers/char/hw_random/virtio-rng.c | 7 +++++++ 1 file changed, 7 insertions(+)
Please respond to this comment:
https://sashiko.dev/#/patchset/20261016103640.4989-2-blum%40kernel.org
Thanks,
Signature · 3 lines
Email: Herbert Xu <[email protected]> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
Hi Herbert,
Quoted 18 lines · Herbert Xu
Quoted 10 lines · Thorsten Blum
remove_common()frees the driver data during freeze, but does not clearvdev->priv, which points to freed memory. Ifprobe_common()then fails during restore, a later remove or freeze can dereference the dangling pointer.Clear
vdev->privon theprobe_common()error cleanup path and before freeing the driver data inremove_common(). Also reset the device and return early fromremove_common()ifvdev->privis already NULL for a safe teardown after a failed restore.Quoted 6 lines
Fixes 08e53fbdb85c ("virtio-rng: support multiple virtio-rng devices")Signed-off-by Thorsten Blum <[email protected]>--- drivers/char/hw_random/virtio-rng.c | 7 +++++++ 1 file changed, 7 insertions(+)
Quoted 3 lines
Please respond to this comment:
https://sashiko.dev/#/patchset/20261016103640.4989-2-blum%40kernel.org
I think the issue raised by Sashiko is valid, but also pre-existing and could therefore be fixed in a separate follow-up patch.
Thanks,
Thorsten
Herbert Xu
maintainerQuoted 10 lines · Thorsten Blum
remove_common()frees the driver data during freeze, but does not clearvdev->priv, which points to freed memory. Ifprobe_common()then fails during restore, a later remove or freeze can dereference the dangling pointer.Clear
vdev->privon theprobe_common()error cleanup path and before freeing the driver data inremove_common(). Also reset the device and return early fromremove_common()ifvdev->privis already NULL for a safe teardown after a failed restore.
Quoted 6 lines
Fixes 08e53fbdb85c ("virtio-rng: support multiple virtio-rng devices")Signed-off-by Thorsten Blum <[email protected]>--- drivers/char/hw_random/virtio-rng.c | 7 +++++++ 1 file changed, 7 insertions(+)
Patch applied. Thanks.
Signature · 3 lines
Email: Herbert Xu <[email protected]> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt