NACK: [SRU][F][PATCH 1/3] media: cec: abort if the current transmit was canceled
Jacob Martin
jacob.martin at canonical.com
Mon Jan 13 15:48:53 UTC 2025
On 1/13/25 9:26 AM, Magali Lemes wrote:
> On 10/01/2025 18:19, Jacob Martin wrote:
>> From: Hans Verkuil <hverkuil-cisco at xs4all.nl>
>>
>> If a transmit-in-progress was canceled, then, once the transmit
>> is done, mark it as aborted and refrain from retrying the transmit.
>>
>> To signal this situation the new transmit_in_progress_aborted field is
>> set to true.
>>
>> The old implementation would just set adap->transmitting to NULL and
>> set adap->transmit_in_progress to false, but on the hardware level
>> the transmit was still ongoing. However, the framework would think
>> the transmit was aborted, and if a new transmit was issued, then
>> it could overwrite the HW buffer containing the old transmit with the
>> new transmit, leading to garbled data on the CEC bus.
>>
>> Signed-off-by: Hans Verkuil <hverkuil-cisco at xs4all.nl>
>> Signed-off-by: Mauro Carvalho Chehab <mchehab at kernel.org>
>> (backported from commit 590a8e564c6eff7e77a84e728612f1269e3c0685)
>> [jacobmartin: initialize transmit_in_progress_aborted in directly in
>> __cec_s_phys_addr as cec_activate_cnt_{inc,dec} from 3813c932ed ("media:
>> cec: call enable_adap on s_log_addrs") are not present.]
>> CVE-2024-23848
>> Signed-off-by: Jacob Martin <jacob.martin at canonical.com>
>> ---
>> drivers/media/cec/cec-adap.c | 13 ++++++++++---
>> include/media/cec.h | 1 +
>> 2 files changed, 11 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c
>> index 94ddaca496c9..cda48e58557c 100644
>> --- a/drivers/media/cec/cec-adap.c
>> +++ b/drivers/media/cec/cec-adap.c
>> @@ -418,7 +418,7 @@ static void cec_flush(struct cec_adapter *adap)
>> cec_data_cancel(data, CEC_TX_STATUS_ABORTED);
>> }
>> if (adap->transmitting)
>> - cec_data_cancel(adap->transmitting, CEC_TX_STATUS_ABORTED);
>> + adap->transmit_in_progress_aborted = true;
>> /* Cancel the pending timeout work. */
>> list_for_each_entry_safe(data, n, &adap->wait_queue, list) {
>> @@ -569,6 +569,7 @@ int cec_thread_func(void *_adap)
>> if (data->attempts == 0)
>> data->attempts = attempts;
>> + adap->transmit_in_progress_aborted = false;
>> /* Tell the adapter to transmit, cancel on error */
>> if (adap->ops->adap_transmit(adap, data->attempts,
>> signal_free_time, &data->msg))
>> @@ -596,6 +597,8 @@ void cec_transmit_done_ts(struct cec_adapter
>> *adap, u8 status,
>> struct cec_msg *msg;
>> unsigned int attempts_made = arb_lost_cnt + nack_cnt +
>> low_drive_cnt + error_cnt;
>> + bool done = status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK);
>> + bool aborted = adap->transmit_in_progress_aborted;
>> dprintk(2, "%s: status 0x%02x\n", __func__, status);
>> if (attempts_made < 1)
>> @@ -616,6 +619,7 @@ void cec_transmit_done_ts(struct cec_adapter
>> *adap, u8 status,
>> goto wake_thread;
>> }
>> adap->transmit_in_progress = false;
>> + adap->transmit_in_progress_aborted = false;
>> msg = &data->msg;
>> @@ -636,8 +640,7 @@ void cec_transmit_done_ts(struct cec_adapter
>> *adap, u8 status,
>> * the hardware didn't signal that it retried itself (by setting
>> * CEC_TX_STATUS_MAX_RETRIES), then we will retry ourselves.
>> */
>> - if (data->attempts > attempts_made &&
>> - !(status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK))) {
>> + if (!aborted && data->attempts > attempts_made && !done) {
>> /* Retry this message */
>> data->attempts -= attempts_made;
>> if (msg->timeout)
>> @@ -652,6 +655,8 @@ void cec_transmit_done_ts(struct cec_adapter
>> *adap, u8 status,
>> goto wake_thread;
>> }
>> + if (aborted && !done)
>> + status |= CEC_TX_STATUS_ABORTED;
>> data->attempts = 0;
>> /* Always set CEC_TX_STATUS_MAX_RETRIES on error */
>> @@ -1571,6 +1576,7 @@ void __cec_s_phys_addr(struct cec_adapter *adap,
>> u16 phys_addr, bool block)
>> if (adap->needs_hpd || list_empty(&adap->devnode.fhs)) {
>> WARN_ON(adap->ops->adap_enable(adap, false));
>> adap->transmit_in_progress = false;
>> + adap->transmit_in_progress_aborted = false;
>> wake_up_interruptible(&adap->kthread_waitq);
>> }
>> mutex_unlock(&adap->devnode.lock);
>> @@ -1581,6 +1587,7 @@ void __cec_s_phys_addr(struct cec_adapter *adap,
>> u16 phys_addr, bool block)
>> mutex_lock(&adap->devnode.lock);
>> adap->last_initiator = 0xff;
>> adap->transmit_in_progress = false;
>> + adap->transmit_in_progress_aborted = false;
>
> Why was the block below
> ```
> if (adap->transmitting)
> cec_data_cancel(adap->transmitting, CEC_TX_STATUS_ABORTED);
> ```
> from the original upstream commit dropped?
>
It shouldn't have been! Thanks, I'll send a v2.
>
>> if ((adap->needs_hpd || list_empty(&adap->devnode.fhs)) &&
>> adap->ops->adap_enable(adap, true)) {
>> diff --git a/include/media/cec.h b/include/media/cec.h
>> index 4d59387bc61b..06f88f126828 100644
>> --- a/include/media/cec.h
>> +++ b/include/media/cec.h
>> @@ -187,6 +187,7 @@ struct cec_adapter {
>> struct list_head wait_queue;
>> struct cec_data *transmitting;
>> bool transmit_in_progress;
>> + bool transmit_in_progress_aborted;
>> struct task_struct *kthread_config;
>> struct completion config_completion;
>
More information about the kernel-team
mailing list