Nvidia DeepStream - широко известный в узких кругах инструмент для инференса на нейронных сетях и другой высокопроизводительной обработки видео-потоков в реальном времени на оборудовании от Nvidia. Важнейшими компонентами, обеспечивающими эффективность применения DeepStream являются:
встроенные модули аппаратного декодирования видео NVDEC из разных форматов (H264, HEVC, AV1, MPEG-1, MPEG-2, VP8, VP9);
встроенные модули аппаратного кодирования видео NVENC в разные форматы (H264, HEVC, AV1, MPEG-1, MPEG-2, VP8, VP9).
Опять же, широко известная в узких кругах табличка позволяет оценить, что вы можете кодировать или декодировать на разных GPU от Nvidia.
Именно NVDEC и NVENC обеспечивают повышение эффективности инференса за счет того, что, после декодирования сжатых данных с помощью NVDEC, сырое видео (в оптимальном пайплайне обработки) не покидает память GPU до окончания обработки, откуда, если требуется, NVENC наоборот забирает сырые данные и передает их уже в сжатом (кодированном) виде в память CPU.
Сам пайплайн DeepStream работает на движке Gstreamer с дополнительными плагинами от Nvidia, обеспечивающими всякие хитрые операции (например, dewarp видео с fisheye в несколько плоских картинок) и взаимодействие с TensorRT через плагины инференса. Так же есть плагины для трекинга и других операций. Полный список доступен по этой ссылке.
В итоге, используя DeepStream, вы можете получить очень высокое быстродействие, несравнимое с OpenCV/PyTorch, TensorFlow, etc. не погружаясь в дебри CUDA-разработки.
Наша команда занимается разработкой и оптимизацией пайплайнов видео аналитики для работы на базе DeepStream. В текущем проекте мы обнаружили, что некоторые пайплайны виснут. Поскольку обработка аналитическая, на первом этапе мы просто реализовали в K8s keepalive и несколько раз в сутки рестартовали пайплайн, когда он переставал отвечать. Само по себе расследование того, где это происходит, представлялось затруднительным и не входило в приоритетные задачи.
Не так давно мы приступили к реализации новой функции, которая подразумевает реакцию на события с низкой задержкой, поэтому проблема снова стала актуальной. За пару дней мы смогли локализовать проблему и найти видео-фрагмент, который вызывает зависание пайплайна DeepStream в модуле NVDEC при декодировании видео в формате H264.
Таким образом, фактический злоумышленник имеет способ вызвать зависание пайплайна DeepStream с помощью формирования специального фрейма. В нашем случае злоумышленником была обычная камера видео-наблюдения, установленная на площадке, где проводилось тестирование. Мы смогли локализовать видео-фрагмент для стабильного воспроизведения проблемы. Поскольку DeepStream широко внедряется в умных городах, на предприятиях и производстве, подобный баг является критическим.
В нашей среде он воспроизводится в следующем окружении на GPU Quadro RTX 4000, Geforce RTX 2080:
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 465.19.01 Driver Version: 465.19.01 CUDA Version: 11.3 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 NVIDIA GeForce ... On | 00000000:02:00.0 Off | N/A |
| 30% 34C P5 17W / 215W | 434MiB / 7982MiB | 15% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
+-----------------------------------------------------------------------------+
Для воспроизведения бага можно воспользоваться простейшим пайплайном Gstreamer:
gst-launch-1.0 filesrc location=/big-data/video/output.mp4 ! qtdemux ! h264parse ! nvv4l2decoder ! fakesink
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
Пайплайн не завершается, зависает. Работает на картах серии Turing (на Ampere не проверяли, в нашей лабе их еще нет).
Обработка того же куска видео с помощью декодера на базе CPU (стандартный Gstreamer и Intel VAAPI) успешно отрабатывает:
gst-launch-1.0 filesrc location=/big-data/video/output.mp4 ! qtdemux ! avdec_h264 ! fakesink
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
Redistribute latency...
Redistribute latency...
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
Got EOS from element "pipeline0".
Execution ended after 0:00:00.220984700
Setting pipeline to PAUSED ...
Setting pipeline to READY ...
Setting pipeline to NULL ...
Freeing pipeline ...
При запуске пайплайна в режиме Debug, можно увидеть, что зависание происходит именно в декодере:
GST_DEBUG=5 gst-launch-1.0 filesrc location=/big-data/video/output.mp4 ! qtdemux ! h264parse ! nvv4l2decoder ! fakesink
...
0:00:00.614602755 19768 0x557836592cf0 DEBUG GST_MEMORY gstmemory.c:139:gst_memory_init: new memory 0x5578365c8470, maxsize:71 offset:0 size:64
0:00:00.614610302 19768 0x557836592cf0 DEBUG GST_PERFORMANCE gstallocator.c:465:_sysmem_copy: memcpy 64 memory 0x5578365c8390 -> 0x5578365c8470
0:00:00.614618503 19768 0x557836592cf0 DEBUG GST_BUFFER gstbuffer.c:2202:gst_buffer_add_meta: alloc metadata 0x7f0d6000daf8 (GstVideoMeta) of size 112
0:00:00.614625121 19768 0x557836592cf0 DEBUG videometa gstvideometa.c:86:gst_video_meta_transform: copy video metadata
0:00:00.614631365 19768 0x557836592cf0 DEBUG GST_MEMORY gstmemory.c:88:_gst_memory_free: free memory 0x5578365c8390
0:00:00.614640158 19768 0x557836592cf0 DEBUG GST_PADS gstpad.c:4072:gst_pad_query:<fakesink0:sink> sent query 0x7f0d6054f590 (allocation), result 0
0:00:00.614646925 19768 0x557836592cf0 DEBUG GST_PADS gstpad.c:4117:gst_pad_query:<fakesink0:sink> query failed
0:00:00.614653488 19768 0x557836592cf0 DEBUG GST_PADS gstpad.c:4238:gst_pad_peer_query:<nvv4l2decoder0:src> query failed
0:00:00.614660986 19768 0x557836592cf0 DEBUG v4l2 gstv4l2object.c:4389:gst_v4l2_object_stop:<nvv4l2decoder0:src> stopping
Видео-фрагмент, на котором воспроизводится проблема можно скачать здесь.
По факту проблемы мы обратились к нашему партнеру, который смог воспроизвести проблему на Nvidia T4 и дал еще больше данных:
BCM:: counter - 72
BCM:: counter - 73
BCM:: counter - 74
BCM:: counter - 75
BCM:: counter - 76
Redistribute latency...
Redistribute latency...
Redistribute latency...
Redistribute latency...
ERROR: from element /GstPipeline:pipeline0/GstQTDemux:qtdemux0: Internal data stream error.
Additional debug info:
qtdemux.c(6073): gst_qtdemux_loop (): /GstPipeline:pipeline0/GstQTDemux:qtdemux0:
streaming stopped, reason not-negotiated (-4)
Execution ended after 0:00:01.682024126
Setting pipeline to PAUSED ...
Setting pipeline to READY ...
Setting pipeline to NULL ...
Freeing pipeline ...
То есть, 76й фрейм вызывает сбой обработки, который на CPU вызывает завершение пайплайна, а при использовании NVDEC пайплайн зависает наглухо.
По факту на форуме Nvidia был открыт баг. Пока никаких дельных советом от Nvidia нет. Ждет или фикс или хотя бы инструкции о том, как можно сделать "антивирус", поставив его перед NVDEC, чтобы определять и отбрасывать зловредные фреймы. Как сказал архитектор нашего партнера: "главное, чтобы ошибка была не в чипе".
BeLove
Наверняка, к этой проблеме уязвимы большие cloud провайдеры для видеонаблюдения и им можно было бы в потоке скормить этот кусок. И проблема DoS становится актуальной - если в цикле постоянно выводить ноды из строя (пусть даже у них будет рестарт).
ivankudryavtsev Автор
Да, основная проблема в том, что можно уменьшать объем функционирующей части системы обработки, отправляя некорректно сформированные видео-данные. Но, с учетом того, что оно не падает, а зависает, это еще хуже, потому что тогда другие модули некоторое время должны считать что обработка еще живая, что увеличивает риск задержки реакции на важные события.