Received: by 2002:a05:6a10:af89:0:0:0:0 with SMTP id iu9csp3690385pxb; Mon, 24 Jan 2022 15:28:13 -0800 (PST) X-Google-Smtp-Source: ABdhPJw/Thopt/gElz6Y2KxpQIFH+a/UFxoYD1iojsSWLW+m9HWOXbCwccLvV6Aa/Vlvs+umpisS X-Received: by 2002:aa7:8003:0:b0:4bc:1a91:6df3 with SMTP id j3-20020aa78003000000b004bc1a916df3mr16038612pfi.26.1643066893149; Mon, 24 Jan 2022 15:28:13 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1643066893; cv=none; d=google.com; s=arc-20160816; b=V6tBM2XcWqkYFwCm7JHzUFrF9sKGzQd4rlSEbn0ft1cRH37QA0Sh8F6/wvFqbJI+Ja VwaM5ckkEJ7tSHQBNh9+dIe5wzPOxcgIjlE0hbWzw3CST0xvxFJwJ9BOY2WoxUi3umvq PqcclpEJa4iJ7SeyYri0ZucWKjDk8JP4nRjQ2/GQnlUvU6GzcIk+s2YAp9CsAIxht6Vs 8qWx6R9GBm50aP5Cmd7lNDk025Al9X+eG9hu+y77LQe6oK3bLQNZcbUQ16OrX3eG4FFm WZpL2HCakklPbV6XDUshi59fszUfNughqOQYUGs5IKW3OdhDbtPZ5h1KsOL3ch0F1cOo 5Q9A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=OEJWG44v8FafVWHdAAI/txP42kQHGzwBiE2tPYhLjNE=; b=TRCwD6nPffD31CetuOMJt/Uid8IKaneHwrvDD7vnz4YSt9vGP5BL4Ep+WhKe3Mo9rl /RhlQ2WKqDQ+KwZmu3VMABnOAJ22mWl2mNEhSICauEIRDXokVrNfRZQtP7g/dvy3MWUT 94SPXlDfHslxIEoU1KJLTsGIfMYwzs74UDzOCDzCuEpSFNShkqp9eQLHoQIKlCy6N+Tk s9PGA7gHgIa40gnzuM4Tc6ne8JHbgLuVvQsdA5ygHcAB/6g9wFZRFsQck9ufpD7BHPTH eQPsvDKAtV3hv47Dtcgqk12REkQc8JJjFf6Z8I6EWutOB79XqhNBMCBIsOFbUyWArfu2 cEqQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b="Yi/vI76V"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id y1si14573196pga.254.2022.01.24.15.28.01; Mon, 24 Jan 2022 15:28:13 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b="Yi/vI76V"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1847999AbiAXXVb (ORCPT + 99 others); Mon, 24 Jan 2022 18:21:31 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38094 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1586905AbiAXW1b (ORCPT ); Mon, 24 Jan 2022 17:27:31 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 390EBC019B20; Mon, 24 Jan 2022 12:54:39 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 8DAD460B03; Mon, 24 Jan 2022 20:54:38 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6EB40C340E5; Mon, 24 Jan 2022 20:54:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1643057678; bh=Z+D5Lb0cITYxKZnNkwCLasS5OuqE492k2Fon+rCWUp0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Yi/vI76VY1RlBmNAqQUlz/GhXYLTB5C3eDxVUkgaRoHvJDOJZuv4DCqUijGxurIzG D3c2RSu2l4/YZ4ItF/tX9c6j/w7uFnyQYNkx8JRioZPCfLunH2tFHrgk0qthU8oNF5 y+YBE9G0kprERt2g+2U796uwreNePPHNgq+s99U4= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Hans Verkuil , Mauro Carvalho Chehab Subject: [PATCH 5.16 0042/1039] media: cec: fix a deadlock situation Date: Mon, 24 Jan 2022 19:30:31 +0100 Message-Id: <20220124184126.550255993@linuxfoundation.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220124184125.121143506@linuxfoundation.org> References: <20220124184125.121143506@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Hans Verkuil commit a9e6107616bb8108aa4fc22584a05e69761a91f7 upstream. The cec_devnode struct has a lock meant to serialize access to the fields of this struct. This lock is taken during device node (un)registration and when opening or releasing a filehandle to the device node. When the last open filehandle is closed the cec adapter might be disabled by calling the adap_enable driver callback with the devnode.lock held. However, if during that callback a message or event arrives then the driver will call one of the cec_queue_event() variants in cec-adap.c, and those will take the same devnode.lock to walk the open filehandle list. This obviously causes a deadlock. This is quite easy to reproduce with the cec-gpio driver since that uses the cec-pin framework which generated lots of events and uses a kernel thread for the processing, so when adap_enable is called the thread is still running and can generate events. But I suspect that it might also happen with other drivers if an interrupt arrives signaling e.g. a received message before adap_enable had a chance to disable the interrupts. This patch adds a new mutex to serialize access to the fhs list. When adap_enable() is called the devnode.lock mutex is held, but not devnode.lock_fhs. The event functions in cec-adap.c will now use devnode.lock_fhs instead of devnode.lock, ensuring that it is safe to call those functions from the adap_enable callback. This specific issue only happens if the last open filehandle is closed and the physical address is invalid. This is not something that happens during normal operation, but it does happen when monitoring CEC traffic (e.g. cec-ctl --monitor) with an unconfigured CEC adapter. Signed-off-by: Hans Verkuil Cc: # for v5.13 and up Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/cec/core/cec-adap.c | 38 +++++++++++++++++++++----------------- drivers/media/cec/core/cec-api.c | 6 ++++++ drivers/media/cec/core/cec-core.c | 3 +++ include/media/cec.h | 11 +++++++++-- 4 files changed, 39 insertions(+), 19 deletions(-) --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -161,10 +161,10 @@ static void cec_queue_event(struct cec_a u64 ts = ktime_get_ns(); struct cec_fh *fh; - mutex_lock(&adap->devnode.lock); + mutex_lock(&adap->devnode.lock_fhs); list_for_each_entry(fh, &adap->devnode.fhs, list) cec_queue_event_fh(fh, ev, ts); - mutex_unlock(&adap->devnode.lock); + mutex_unlock(&adap->devnode.lock_fhs); } /* Notify userspace that the CEC pin changed state at the given time. */ @@ -178,11 +178,12 @@ void cec_queue_pin_cec_event(struct cec_ }; struct cec_fh *fh; - mutex_lock(&adap->devnode.lock); - list_for_each_entry(fh, &adap->devnode.fhs, list) + mutex_lock(&adap->devnode.lock_fhs); + list_for_each_entry(fh, &adap->devnode.fhs, list) { if (fh->mode_follower == CEC_MODE_MONITOR_PIN) cec_queue_event_fh(fh, &ev, ktime_to_ns(ts)); - mutex_unlock(&adap->devnode.lock); + } + mutex_unlock(&adap->devnode.lock_fhs); } EXPORT_SYMBOL_GPL(cec_queue_pin_cec_event); @@ -195,10 +196,10 @@ void cec_queue_pin_hpd_event(struct cec_ }; struct cec_fh *fh; - mutex_lock(&adap->devnode.lock); + mutex_lock(&adap->devnode.lock_fhs); list_for_each_entry(fh, &adap->devnode.fhs, list) cec_queue_event_fh(fh, &ev, ktime_to_ns(ts)); - mutex_unlock(&adap->devnode.lock); + mutex_unlock(&adap->devnode.lock_fhs); } EXPORT_SYMBOL_GPL(cec_queue_pin_hpd_event); @@ -211,10 +212,10 @@ void cec_queue_pin_5v_event(struct cec_a }; struct cec_fh *fh; - mutex_lock(&adap->devnode.lock); + mutex_lock(&adap->devnode.lock_fhs); list_for_each_entry(fh, &adap->devnode.fhs, list) cec_queue_event_fh(fh, &ev, ktime_to_ns(ts)); - mutex_unlock(&adap->devnode.lock); + mutex_unlock(&adap->devnode.lock_fhs); } EXPORT_SYMBOL_GPL(cec_queue_pin_5v_event); @@ -286,12 +287,12 @@ static void cec_queue_msg_monitor(struct u32 monitor_mode = valid_la ? CEC_MODE_MONITOR : CEC_MODE_MONITOR_ALL; - mutex_lock(&adap->devnode.lock); + mutex_lock(&adap->devnode.lock_fhs); list_for_each_entry(fh, &adap->devnode.fhs, list) { if (fh->mode_follower >= monitor_mode) cec_queue_msg_fh(fh, msg); } - mutex_unlock(&adap->devnode.lock); + mutex_unlock(&adap->devnode.lock_fhs); } /* @@ -302,12 +303,12 @@ static void cec_queue_msg_followers(stru { struct cec_fh *fh; - mutex_lock(&adap->devnode.lock); + mutex_lock(&adap->devnode.lock_fhs); list_for_each_entry(fh, &adap->devnode.fhs, list) { if (fh->mode_follower == CEC_MODE_FOLLOWER) cec_queue_msg_fh(fh, msg); } - mutex_unlock(&adap->devnode.lock); + mutex_unlock(&adap->devnode.lock_fhs); } /* Notify userspace of an adapter state change. */ @@ -1573,6 +1574,7 @@ void __cec_s_phys_addr(struct cec_adapte /* Disabling monitor all mode should always succeed */ if (adap->monitor_all_cnt) WARN_ON(call_op(adap, adap_monitor_all_enable, false)); + /* serialize adap_enable */ mutex_lock(&adap->devnode.lock); if (adap->needs_hpd || list_empty(&adap->devnode.fhs)) { WARN_ON(adap->ops->adap_enable(adap, false)); @@ -1584,14 +1586,16 @@ void __cec_s_phys_addr(struct cec_adapte return; } + /* serialize adap_enable */ mutex_lock(&adap->devnode.lock); adap->last_initiator = 0xff; adap->transmit_in_progress = false; - if ((adap->needs_hpd || list_empty(&adap->devnode.fhs)) && - adap->ops->adap_enable(adap, true)) { - mutex_unlock(&adap->devnode.lock); - return; + if (adap->needs_hpd || list_empty(&adap->devnode.fhs)) { + if (adap->ops->adap_enable(adap, true)) { + mutex_unlock(&adap->devnode.lock); + return; + } } if (adap->monitor_all_cnt && --- a/drivers/media/cec/core/cec-api.c +++ b/drivers/media/cec/core/cec-api.c @@ -586,6 +586,7 @@ static int cec_open(struct inode *inode, return err; } + /* serialize adap_enable */ mutex_lock(&devnode->lock); if (list_empty(&devnode->fhs) && !adap->needs_hpd && @@ -624,7 +625,9 @@ static int cec_open(struct inode *inode, } #endif + mutex_lock(&devnode->lock_fhs); list_add(&fh->list, &devnode->fhs); + mutex_unlock(&devnode->lock_fhs); mutex_unlock(&devnode->lock); return 0; @@ -653,8 +656,11 @@ static int cec_release(struct inode *ino cec_monitor_all_cnt_dec(adap); mutex_unlock(&adap->lock); + /* serialize adap_enable */ mutex_lock(&devnode->lock); + mutex_lock(&devnode->lock_fhs); list_del(&fh->list); + mutex_unlock(&devnode->lock_fhs); if (cec_is_registered(adap) && list_empty(&devnode->fhs) && !adap->needs_hpd && adap->phys_addr == CEC_PHYS_ADDR_INVALID) { WARN_ON(adap->ops->adap_enable(adap, false)); --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -169,8 +169,10 @@ static void cec_devnode_unregister(struc devnode->registered = false; devnode->unregistered = true; + mutex_lock(&devnode->lock_fhs); list_for_each_entry(fh, &devnode->fhs, list) wake_up_interruptible(&fh->wait); + mutex_unlock(&devnode->lock_fhs); mutex_unlock(&devnode->lock); @@ -272,6 +274,7 @@ struct cec_adapter *cec_allocate_adapter /* adap->devnode initialization */ INIT_LIST_HEAD(&adap->devnode.fhs); + mutex_init(&adap->devnode.lock_fhs); mutex_init(&adap->devnode.lock); adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name); --- a/include/media/cec.h +++ b/include/media/cec.h @@ -26,13 +26,17 @@ * @dev: cec device * @cdev: cec character device * @minor: device node minor number + * @lock: lock to serialize open/release and registration * @registered: the device was correctly registered * @unregistered: the device was unregistered + * @lock_fhs: lock to control access to @fhs * @fhs: the list of open filehandles (cec_fh) - * @lock: lock to control access to this structure * * This structure represents a cec-related device node. * + * To add or remove filehandles from @fhs the @lock must be taken first, + * followed by @lock_fhs. It is safe to access @fhs if either lock is held. + * * The @parent is a physical device. It must be set by core or device drivers * before registering the node. */ @@ -43,10 +47,13 @@ struct cec_devnode { /* device info */ int minor; + /* serialize open/release and registration */ + struct mutex lock; bool registered; bool unregistered; + /* protect access to fhs */ + struct mutex lock_fhs; struct list_head fhs; - struct mutex lock; }; struct cec_adapter;