nslots is derived from the ABS_MT_SLOT maximum reported by the virtio
device. A device could report a bogus maximum (e.g. -1) making nslots <=
0, and input_mt_init_slots() can independently fail too (e.g. it rejects
slot counts over 1024). Neither case was handled here, and
input_mt_init_slots() failing took down the whole probe, losing the rest
of the input device over a problem isolated to multitouch.

Instead of failing the probe, warn and disable the ABS_MT_SLOT
capability in either case, and let the rest of the device register
normally. This recovery assumes input_mt_init_slots() is called with
flags == 0: with nonzero flags it can fail after already mutating the
input_dev (e.g. via input_set_abs_params()), which this recovery does
not account for.

Signed-off-by: Hari Mishal <[email protected]>
---
v2: per review, disable multitouch instead of failing the whole
    probe when the reported slot count is invalid or
    input_mt_init_slots() otherwise fails.

 drivers/virtio/virtio_input.c | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/drivers/virtio/virtio_input.c b/drivers/virtio/virtio_input.c
index deec24e8e682..786f5150724f 100644
--- a/drivers/virtio/virtio_input.c
+++ b/drivers/virtio/virtio_input.c
@@ -312,9 +312,25 @@ static int virtinput_probe(struct virtio_device *vdev)
 
                if (test_bit(ABS_MT_SLOT, vi->idev->absbit)) {
                        nslots = input_abs_get_max(vi->idev, ABS_MT_SLOT) + 1;
-                       err = input_mt_init_slots(vi->idev, nslots, 0);
-                       if (err)
-                               goto err_mt_init_slots;
+                       if (nslots <= 0) {
+                               dev_warn(&vdev->dev,
+                                        "invalid multitouch slot count %d, 
disabling multitouch\n",
+                                        nslots);
+                               __clear_bit(ABS_MT_SLOT, vi->idev->absbit);
+                       } else {
+                               /*
+                                * flags is 0: input_mt_init_slots() can only 
fail
+                                * before touching *idev, so the recovery below 
is
+                                * a full rollback.
+                                */
+                               err = input_mt_init_slots(vi->idev, nslots, 0);
+                               if (err) {
+                                       dev_warn(&vdev->dev,
+                                                "failed to init %d multitouch 
slots (%d), disabling multitouch\n",
+                                                nslots, err);
+                                       __clear_bit(ABS_MT_SLOT, 
vi->idev->absbit);
+                               }
+                       }
                }
        }
 
@@ -331,7 +347,6 @@ static int virtinput_probe(struct virtio_device *vdev)
        spin_lock_irqsave(&vi->lock, flags);
        vi->ready = false;
        spin_unlock_irqrestore(&vi->lock, flags);
-err_mt_init_slots:
        input_free_device(vi->idev);
 err_input_alloc:
        vdev->config->del_vqs(vdev);
-- 
2.43.0


Reply via email to