From nobody Thu Apr 16 09:12:25 2026 Received: from mail-lj1-f178.google.com (mail-lj1-f178.google.com [209.85.208.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1DBE1175A8C for ; Sat, 28 Feb 2026 22:36:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772318205; cv=none; b=K7gYmkFHRhwl1+gSH6zg1uDQ1KmdtnlHuhA/SN/CXmRp9LEqTdltt1PAlIcxLWdI46YwRTon4Gcf1+WSFHvFLHu9meBT9CwdChi1uwd2UN5t6TojarjmLQ/V4rPROLeYPcbKLLlLeN3JfEYxKPyFjYmjs5saB1tyWF4x9Gwz/6I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772318205; c=relaxed/simple; bh=1L+kj9UJcELj2WH7mBt0bzlwtqGMf+P3GMx4NyrAiKk=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=Iz7MVh92I6vsGCXNwoCRlOZhea88zdZQzmFhUC3olyXn2ihuXKcTWDMYbm2E9iaM3xtBOunGV7/A4FJem7QdwUeEo2s/XI7gSWuz72tsXGK7pI6iz8KSe6JdtdhmWpNHIgmcGJNc9fBFq7+ECI0pOwRniBtcj00Wa4E37p/ywvA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=RVwLwtYw; arc=none smtp.client-ip=209.85.208.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="RVwLwtYw" Received: by mail-lj1-f178.google.com with SMTP id 38308e7fff4ca-389ee8efedeso45213801fa.3 for ; Sat, 28 Feb 2026 14:36:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772318201; x=1772923001; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=rYNJvGhKy9IG7jqDBgfRP5oiX2ACXOEUkxj/oyltGZk=; b=RVwLwtYwluvKw+shu3zDLqRvawEumy8wH9uwpnwg32wWVAK2bSL2ImR9bnlUSYgRua qsWyZrTy+xeMqchWNRFCRL3IxfFoBb0JvXZHVhxw4SjwOdrKDqgo+Nul4nfcq7l5cavi /6U/ovz6oO9rX7mv+idqv5TErOJgy7esswRXS6EHfLrAjQ9h0Muz31cRO+be4iKxWi4x YP2bZzCxNoi6C4+8r7eWR42UFLtnBe6Kfm3B2QmDI0jRtZodaCUA2X27dhqpN1TJl98E XhYMa7qPf5pJvFvq8sVnNBGTDqqxKFxPuFDV1dt5jfe0P/uKOqv+YWMVTVoU/3x0Q1He aX5Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772318201; x=1772923001; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=rYNJvGhKy9IG7jqDBgfRP5oiX2ACXOEUkxj/oyltGZk=; b=suaBl1A20ZgOJWK0+E7MUJJpEtfvVtApPIGWUfKaqhUZGOehFJAYRKNpKoyevhE8zY zofZiM0uhrVwi2NhLZ/9BoG3idH6l4E8neHTkjcw1eGEmLj3cZJyEXMLZmlgGYZmdwwF DJcoSh/xT6c5nT0qVAoMn5zhq1DMJlD7xoq+tXBitJJ2lireEYQ8V8HLhUR07MtDJgxV pLvCZnE8qpWetR8+RiBG2WkB0SEHXb7L4KVm7eSe1lQiW1mt4Ao1gjwLQhYFYTih4tg8 y4Mv8bhuWLEt6/OKkZR1Uwiqx+vB5xLctnYWHvNlIgkcUeN9nEVd0uVIsnhIAtGPxngD AtPQ== X-Forwarded-Encrypted: i=1; AJvYcCUE4JgW+85mKv+SgxFq3yiH3OIyVjuuqcNQAeiVAiIckHRS4mJyb8VFYWaExrEXf7T4R++S7/LgyQoNOGc=@vger.kernel.org X-Gm-Message-State: AOJu0YwohFKseAJXMacE5PRiNQjA+8KznZSEJHsXhzfArWFiUTyIH/oX /Z7qP2qqeT4t6oAo05SJLQ91cSuG5Ishzax1Fri5ByQ8SVuQWV5TWVEV X-Gm-Gg: ATEYQzyuHqFmi3qmBXh2lBx97zw+Qt1FccikvdDsVrpxpldOgW2GMZd9Jjdyypv8biJ /WHqhcFAW8aIJMn232ewwu51maD2E7VcjTYP4eSn4LyHkcC5CaiyHFs6DJVSQ/KLbkzlR9NgnGo cYRhwEUMASVgkq30VrI11AvI1Sd6xfGoHfNI77gKASMHsKUGDiSM1efq7sUr0PNPNFeKN8gh6t1 5rKpWzlAe4HiT1bPZws3h8YEdIjhaho6BPj6cYjeeoBBl4FxUpVD0vu34g4oCtR2ofw4a/r225x ULS97B7oCw4XIhaWhI+izoErFfAWVINyZMd8zRA6LW6R18aeec7d4zKoRvdpikv+zeJc826tAo8 5NKv5I8Ti5LXk6FZvYSVo0nkaN38GWGjfpaW7EN/vQMg1r5LcrFpyuNdnY/opKyfJKq9K24Pig6 4j0k3oQatlY8FidKC9DbV1zT/tAUBw++t7Pg== X-Received: by 2002:a2e:9b19:0:b0:385:9b50:91a8 with SMTP id 38308e7fff4ca-389ff13b8a2mr32846831fa.15.1772318200745; Sat, 28 Feb 2026 14:36:40 -0800 (PST) Received: from localhost ([188.234.148.119]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-389f2ffdfdasm20580881fa.29.2026.02.28.14.36.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Feb 2026 14:36:39 -0800 (PST) From: Mikhail Gavrilov To: dmitry.torokhov@gmail.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Mikhail Gavrilov Subject: [PATCH] Input: uinput - fix circular locking dependency with ff-core Date: Sun, 1 Mar 2026 03:36:28 +0500 Message-ID: <20260228223628.472208-1-mikhail.v.gavrilov@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A lockdep circular locking dependency warning can be triggered reproducibly when using a force-feedback gamepad with uinput (for example, playing ELDEN RING under Wine with a Flydigi Vader 5 controller): ff->mutex -> udev->mutex -> input_mutex -> dev->mutex -> ff->mutex The cycle is caused by four lock acquisition paths: 1. ff upload: input_ff_upload() holds ff->mutex and calls uinput_dev_upload_effect() -> uinput_request_submit() -> uinput_request_send(), which acquires udev->mutex. 2. device create: uinput_ioctl_handler() holds udev->mutex and calls uinput_create_device() -> input_register_device(), which acquires input_mutex. 3. device register: input_register_device() holds input_mutex and calls kbd_connect() -> input_register_handle(), which acquires dev->mutex. 4. evdev release: evdev_release() calls input_flush_device() under dev->mutex, which calls input_ff_flush() acquiring ff->mutex. Fix this by replacing udev->mutex with the existing udev->requests_lock spinlock in uinput_request_send(). The function only needs to atomically check device state and queue an input event into the ring buffer via uinput_dev_event() -- both operations are safe under a spinlock (ktime_get_ts64() and wake_up_interruptible() do not sleep). This breaks the ff->mutex -> udev->mutex link since a spinlock is a leaf in the lock ordering and cannot form cycles with mutexes. To keep state transitions visible to uinput_request_send(), protect writes to udev->state in uinput_create_device() and uinput_destroy_device() with the same spinlock. Additionally, move init_completion(&request->done) from uinput_request_send() to uinput_request_submit() before uinput_request_reserve_slot(). Once the slot is allocated, uinput_flush_requests() may call complete() on it at any time from the destroy path, so the completion must be initialised before the request becomes visible. Lock ordering after the fix: ff->mutex -> requests_lock (spinlock, leaf) udev->mutex -> requests_lock (spinlock, leaf) udev->mutex -> input_mutex -> dev->mutex -> ff->mutex (no back-edge) Link: https://lore.kernel.org/all/CABXGCsMoxag+kEwHhb7KqhuyxfmGGd0P=3DtHZyb= 1uKE0pLr8Hkg@mail.gmail.com/ Signed-off-by: Mikhail Gavrilov --- drivers/input/misc/uinput.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index e589060db280..dcb855234049 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -146,19 +146,15 @@ static void uinput_request_release_slot(struct uinput= _device *udev, static int uinput_request_send(struct uinput_device *udev, struct uinput_request *request) { - int retval; + int retval =3D 0; =20 - retval =3D mutex_lock_interruptible(&udev->mutex); - if (retval) - return retval; + spin_lock(&udev->requests_lock); =20 if (udev->state !=3D UIST_CREATED) { retval =3D -ENODEV; goto out; } =20 - init_completion(&request->done); - /* * Tell our userspace application about this new request * by queueing an input event. @@ -166,7 +162,7 @@ static int uinput_request_send(struct uinput_device *ud= ev, uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id); =20 out: - mutex_unlock(&udev->mutex); + spin_unlock(&udev->requests_lock); return retval; } =20 @@ -175,6 +171,13 @@ static int uinput_request_submit(struct uinput_device = *udev, { int retval; =20 + /* + * Initialize completion before allocating the request slot. + * Once the slot is allocated, uinput_flush_requests() may + * complete it at any time, so it must be initialized first. + */ + init_completion(&request->done); + retval =3D uinput_request_reserve_slot(udev, request); if (retval) return retval; @@ -289,7 +292,14 @@ static void uinput_destroy_device(struct uinput_device= *udev) struct input_dev *dev =3D udev->dev; enum uinput_state old_state =3D udev->state; =20 + /* + * Update state under requests_lock so that concurrent + * uinput_request_send() sees the state change before we + * flush pending requests and tear down the device. + */ + spin_lock(&udev->requests_lock); udev->state =3D UIST_NEW_DEVICE; + spin_unlock(&udev->requests_lock); =20 if (dev) { name =3D dev->name; @@ -366,7 +376,9 @@ static int uinput_create_device(struct uinput_device *u= dev) if (error) goto fail2; =20 + spin_lock(&udev->requests_lock); udev->state =3D UIST_CREATED; + spin_unlock(&udev->requests_lock); =20 return 0; =20 --=20 2.53.0