From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793832847; b=lK/ZFwUJVk9BaKMNrz0MkP/dMaNetYlfBvUuRKVXotoZKYiuNGrX+eg7G7LrN1H0y+j1 xng8oBhPpHg+phHwM5h/ZqVRB6tBQE8QRMyCIO3OaIOlDZvvYJKeSsWIcOTA1ghk6hKOF 5IRP/4LUE5nPQcqfRx+NMtdqKZrb2vtuBP6JQhJiBvP198T6UUyHxPcUjhD1qJEpXeIRz 9nEeY7tjRsehP6kpC7n4AwzqBNoeEEHSN8FWP9AhDZWc6szjCqyo15A+s/uOXEQ/b+iq1 47e52aLUsvmkZZRf3jpODcljOdmdgXxu2jAhOYRAwXLR9D+/CkHBVEgDAZfH+00r6Wg== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793832847; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:MIME-Version:Content-Transfer-Encoding; bh=gSWPWYOJ9974rNwWg/7s6e5n4m5R5SJcl+WSYmUEpGk=; b=CXPprh2I/JBwfmR2cz03uotFZ0f/wos2+Sm7Zsw0BVCrhYotoGTaND7fPAcOGYZ+g5m7 KyWxXCVTCIb0zsJE3ylEkhfl9XVnqGU9uHSSB9sMT5jFPpnF48Jze4WJtpqVdWGTm0RZe oZoQoCHGgEaRUWOcAODLAIqnTbtufo6Gnq0DpwmlGbTtauu+ie5TTid+JXcp32ITHQdPn HgO1WIz56gv/kfjObV4DSt4GAJh8zph/AoXHZkspkdqDh7ZhuOaYYjV4Op/bwP1aYe4ZB pTQds/WfYsG6lFPfjM+6jHfLS+uKLLxd+2njLt9LdaucuI916mHp0asydUr6PQp5O9Q== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b="LTaWJm/D"; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA6B1606A31B for ; Wed, 4 Nov 2026 22:54:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=LTaWJm/D Received: by smtp.kernel.org (Postfix) with ESMTPSA id C96C31F000C1; Wed, 4 Nov 2026 22:54:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1793832846; bh=gSWPWYOJ9974rNwWg/7s6e5n4m5R5SJcl+WSYmUEpGk=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=LTaWJm/D9Fdz3fHX0PrU/56zIfEdl55NC5YL6IYlLhkZQrU63PKEQ+zz1+zOip82J vZOYZVAJt7AY/rAB547okuGhEbGOeq/zFvTdLozTVg4l4J7tZxTHcjVcnTC0ErQV31 4E7/pNee1ItOOHGHJU1NdJ0T+3zMWLsnjTBaWKV643T2qJnTcNOKTeWU8rnhNwMjVw Fzf28HfX1EEzvDIA5SnA+iSiQMWBqEkNmAuhqViQC3ByueBaErVGlJMk3mYRUFropg PetHxBmC91ZjGV/S6Ztep8BXAQiESlGtQs5YCxcxMIMY7nb+knpOSy368qRFGyX6E1 WDAGj1cYZ57mQ== From: Sasha Levin To: stable@vger.kernel.org Cc: Lee Jones , Ping Cheng , Jiri Kosina , Sasha Levin Subject: [PATCH 5.10.y] HID: wacom: Redesign shared sibling data lifecycle Date: Wed, 4 Nov 2026 17:54:04 -0500 Message-ID: <20261104225335.842258-1-sashal@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <2026110412-skinning-tricky-453b@gregkh> References: <2026110412-skinning-tricky-453b@gregkh> X-Mailing-List: stable@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Lee Jones [ Upstream commit 480c9cb14ae81f8e112ff83f8778b3d7b9324dc8 ] The Wacom driver coordinates state between sibling interfaces of the same physical device using a shared structure 'wacom_shared' inside 'wacom_hdev_data'. The driver kept a volatile representative pointer 'data->dev' pointing to a sibling 'hid_device' for physical path comparisons during sibling matching. This pointer management is fragile. When the representative device is disconnected, wacom_remove_shared_data() failed to clear/update 'data->dev', leading to a Use-After-Free vulnerability when subsequent sibling probes dereference the dangling 'data->dev' pointer. Resolve this issue by redesigning the sibling data lifecycle: - Eliminate the volatile 'data->dev' representative pointer completely - Redesign 'wacom_hdev_data' to store stable static copies of the required attributes upon first allocation: 'phys' path string, 'vendor', 'product' IDs and the sibling's 'device_type' - Use these static attributes for stable sibling matching in wacom_are_sibling() and wacom_get_hdev_data() This ensures sibling matching remains safe and stable even if individual siblings are dynamically added or removed. To secure the lifecycle against concurrent probe/disconnect races: - Switch kref_put() to kref_put_mutex() in wacom_remove_shared_data() to serialize refcount drops with list traversal and lookup - Modify wacom_release_shared_data() to assume the list lock is already held Also, do not accumulate the 'device_type' capability flag during subsequent sibling probes. Keeping only the first probed sibling's device_type exactly preserves the original sibling matching behavior without introducing side effects. Cc: stable@vger.kernel.org Fixes: 4492efffffeb ("Input: wacom - share pen info with touch of the same ID") Signed-off-by: Lee Jones Acked-by: Ping Cheng Tested-by: Ping Cheng Signed-off-by: Jiri Kosina [ sashal: adapted sibling checks to direct shared touch and pen pointer access instead of RCU-dereferenced local variables ] Signed-off-by: Sasha Levin --- drivers/hid/wacom_sys.c | 70 +++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 374719d28f9e5..f418efa868ee9 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -750,27 +750,47 @@ static void wacom_retrieve_hid_descriptor(struct hid_device *hdev, struct wacom_hdev_data { struct list_head list; struct kref kref; - struct hid_device *dev; + char phys[64]; + __u32 vendor; + __u32 product; + __u32 device_type; struct wacom_shared shared; }; +static bool wacom_compare_device_paths(struct hid_device *hdev_a, + const char *phys_b, char separator) +{ + const char *p1 = strrchr(hdev_a->phys, separator); + const char *p2 = strrchr(phys_b, separator); + int n1, n2; + + if (!p1 || !p2) + return false; + + n1 = p1 - hdev_a->phys; + n2 = p2 - phys_b; + + if (n1 != n2 || n1 <= 0 || n2 <= 0) + return false; + + return !strncmp(hdev_a->phys, phys_b, n1); +} + static LIST_HEAD(wacom_udev_list); static DEFINE_MUTEX(wacom_udev_list_lock); static bool wacom_are_sibling(struct hid_device *hdev, - struct hid_device *sibling) + struct wacom_hdev_data *data) { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_features *features = &wacom->wacom_wac.features; - struct wacom *sibling_wacom = hid_get_drvdata(sibling); - struct wacom_features *sibling_features = &sibling_wacom->wacom_wac.features; __u32 oVid = features->oVid ? features->oVid : hdev->vendor; __u32 oPid = features->oPid ? features->oPid : hdev->product; /* The defined oVid/oPid must match that of the sibling */ - if (features->oVid != HID_ANY_ID && sibling->vendor != oVid) + if (features->oVid != HID_ANY_ID && data->vendor != oVid) return false; - if (features->oPid != HID_ANY_ID && sibling->product != oPid) + if (features->oPid != HID_ANY_ID && data->product != oPid) return false; /* @@ -778,11 +798,11 @@ static bool wacom_are_sibling(struct hid_device *hdev, * device path, while those with different VID/PID must share * the same physical parent device path. */ - if (hdev->vendor == sibling->vendor && hdev->product == sibling->product) { - if (!hid_compare_device_paths(hdev, sibling, '/')) + if (hdev->vendor == data->vendor && hdev->product == data->product) { + if (!wacom_compare_device_paths(hdev, data->phys, '/')) return false; } else { - if (!hid_compare_device_paths(hdev, sibling, '.')) + if (!wacom_compare_device_paths(hdev, data->phys, '.')) return false; } @@ -795,7 +815,7 @@ static bool wacom_are_sibling(struct hid_device *hdev, * devices. */ if ((features->device_type & WACOM_DEVICETYPE_DIRECT) && - !(sibling_features->device_type & WACOM_DEVICETYPE_DIRECT)) + !(data->device_type & WACOM_DEVICETYPE_DIRECT)) return false; /* @@ -803,17 +823,17 @@ static bool wacom_are_sibling(struct hid_device *hdev, * devices. */ if (!(features->device_type & WACOM_DEVICETYPE_DIRECT) && - (sibling_features->device_type & WACOM_DEVICETYPE_DIRECT)) + (data->device_type & WACOM_DEVICETYPE_DIRECT)) return false; /* Pen devices may only be siblings of touch devices */ if ((features->device_type & WACOM_DEVICETYPE_PEN) && - !(sibling_features->device_type & WACOM_DEVICETYPE_TOUCH)) + !(data->device_type & WACOM_DEVICETYPE_TOUCH)) return false; /* Touch devices may only be siblings of pen devices */ if ((features->device_type & WACOM_DEVICETYPE_TOUCH) && - !(sibling_features->device_type & WACOM_DEVICETYPE_PEN)) + !(data->device_type & WACOM_DEVICETYPE_PEN)) return false; /* @@ -829,7 +849,7 @@ static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev) /* Try to find an already-probed interface from the same device */ list_for_each_entry(data, &wacom_udev_list, list) { - if (hid_compare_device_paths(hdev, data->dev, '/')) { + if (wacom_compare_device_paths(hdev, data->phys, '/')) { kref_get(&data->kref); return data; } @@ -837,7 +857,7 @@ static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev) /* Fallback to finding devices that appear to be "siblings" */ list_for_each_entry(data, &wacom_udev_list, list) { - if (wacom_are_sibling(hdev, data->dev)) { + if (wacom_are_sibling(hdev, data)) { kref_get(&data->kref); return data; } @@ -851,29 +871,28 @@ static void wacom_release_shared_data(struct kref *kref) struct wacom_hdev_data *data = container_of(kref, struct wacom_hdev_data, kref); - mutex_lock(&wacom_udev_list_lock); list_del(&data->list); - mutex_unlock(&wacom_udev_list_lock); - kfree(data); } static void wacom_remove_shared_data(void *res) { - struct wacom *wacom = res; + struct wacom *res_wacom = res; struct wacom_hdev_data *data; - struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct wacom_wac *wacom_wac = &res_wacom->wacom_wac; if (wacom_wac->shared) { data = container_of(wacom_wac->shared, struct wacom_hdev_data, shared); - if (wacom_wac->shared->touch == wacom->hdev) + if (wacom_wac->shared->touch == res_wacom->hdev) wacom_wac->shared->touch = NULL; - else if (wacom_wac->shared->pen == wacom->hdev) + else if (wacom_wac->shared->pen == res_wacom->hdev) wacom_wac->shared->pen = NULL; - kref_put(&data->kref, wacom_release_shared_data); + if (kref_put_mutex(&data->kref, wacom_release_shared_data, &wacom_udev_list_lock)) + mutex_unlock(&wacom_udev_list_lock); + wacom_wac->shared = NULL; } } @@ -896,7 +915,10 @@ static int wacom_add_shared_data(struct hid_device *hdev) } kref_init(&data->kref); - data->dev = hdev; + strscpy(data->phys, hdev->phys, sizeof(data->phys)); + data->vendor = hdev->vendor; + data->product = hdev->product; + data->device_type = wacom_wac->features.device_type; list_add_tail(&data->list, &wacom_udev_list); } -- 2.53.0