In __vringh_iov(), the F_INDIRECT branch descends into the indirect
table and continues before the descriptor accounting runs. A top-level
indirect descriptor is therefore never charged to count. If such a
descriptor sets NEXT to point back at itself, returning from the
indirect table resumes at the same top-level descriptor, which is again
not counted, so the walk never makes forward progress.

Because count stays flat and indirect_count is reset to 0 on every
return to the top-level table, neither bound in the loop check trips.
A guest can spin the vringh worker at 100% CPU inside __vringh_iov(),
an uninterruptible host DoS.

Move the descriptor accounting above the indirect switch so that a
top-level indirect descriptor is charged one top-level traversal step
before the walk descends into its table, bringing this re-entry under
the existing vrh->vring.num bound. Each top-level descriptor is still
charged at most one step and indirect_count still bounds a single
table, so legitimate chains (including the multiple-indirect case in
tools/virtio/vringh_test.c) stay within vring.num, while a cyclic
indirect descriptor is now rejected with -ELOOP.

Fixes: dbd29e075228 ("vringh: Fix loop descriptors check in the indirect cases")
Cc: Xie Yongji <[email protected]>
Cc: [email protected]
Assisted-by: Hawkeye:GLM-5.3-flash
Assisted-by: Qoder:Qwen3.8-Max
Signed-off-by: Fang Xieyan <[email protected]>
---
 drivers/vhost/vringh.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/drivers/vhost/vringh.c b/drivers/vhost/vringh.c
index 9066f9f..f672e11 100644
--- a/drivers/vhost/vringh.c
+++ b/drivers/vhost/vringh.c
@@ -333,6 +333,17 @@ __vringh_iov(struct vringh *vrh, u16 i,
                if (unlikely(err))
                        goto fail;
 
+               if (up_next == -1)
+                       count++;
+               else
+                       indirect_count++;
+
+               if (count > vrh->vring.num || indirect_count > desc_max) {
+                       vringh_bad("Descriptor loop in %p", descs);
+                       err = -ELOOP;
+                       goto fail;
+               }
+
                if (unlikely(desc.flags &
                             cpu_to_vringh16(vrh, VRING_DESC_F_INDIRECT))) {
                        u64 a = vringh64_to_cpu(vrh, desc.addr);
@@ -358,17 +369,6 @@ __vringh_iov(struct vringh *vrh, u16 i,
                        continue;
                }
 
-               if (up_next == -1)
-                       count++;
-               else
-                       indirect_count++;
-
-               if (count > vrh->vring.num || indirect_count > desc_max) {
-                       vringh_bad("Descriptor loop in %p", descs);
-                       err = -ELOOP;
-                       goto fail;
-               }
-
                if (desc.flags & cpu_to_vringh16(vrh, VRING_DESC_F_WRITE))
                        iov = wiov;
                else {
-- 
2.50.1


Reply via email to