media: cec: avoid decrementing transmit_queue_sz if it is 0
authorHans Verkuil <hverkuil-cisco@xs4all.nl>
Sat, 7 Dec 2019 22:48:09 +0000 (23:48 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 9 Jan 2020 09:17:54 +0000 (10:17 +0100)
commit 95c29d46ab2a517e4c26d0a07300edca6768db17 upstream.

WARN if transmit_queue_sz is 0 but do not decrement it.
The CEC adapter will become unresponsive if it goes below
0 since then it thinks there are 4 billion messages in the
queue.

Obviously this should not happen, but a driver bug could
cause this.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Cc: <stable@vger.kernel.org> # for v4.12 and up
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/media/cec/cec-adap.c

index 8030485ecfd79d4c04a22b4792fa4a3fff30cd36..0d7d687aeea0ec23b0deaa2269573fcc6d36346b 100644 (file)
@@ -330,7 +330,8 @@ static void cec_data_cancel(struct cec_data *data)
        } else {
                list_del_init(&data->list);
                if (!(data->msg.tx_status & CEC_TX_STATUS_OK))
-                       data->adap->transmit_queue_sz--;
+                       if (!WARN_ON(!data->adap->transmit_queue_sz))
+                               data->adap->transmit_queue_sz--;
        }
 
        /* Mark it as an error */
@@ -377,6 +378,14 @@ static void cec_flush(struct cec_adapter *adap)
                 * need to do anything special in that case.
                 */
        }
+       /*
+        * If something went wrong and this counter isn't what it should
+        * be, then this will reset it back to 0. Warn if it is not 0,
+        * since it indicates a bug, either in this framework or in a
+        * CEC driver.
+        */
+       if (WARN_ON(adap->transmit_queue_sz))
+               adap->transmit_queue_sz = 0;
 }
 
 /*
@@ -465,7 +474,8 @@ int cec_thread_func(void *_adap)
                data = list_first_entry(&adap->transmit_queue,
                                        struct cec_data, list);
                list_del_init(&data->list);
-               adap->transmit_queue_sz--;
+               if (!WARN_ON(!data->adap->transmit_queue_sz))
+                       adap->transmit_queue_sz--;
 
                /* Make this the current transmitting message */
                adap->transmitting = data;