Video capture: validate SPA pod values to prevent OOB reads

Add bounds checks when reading SPA pod choice values in OnNodeParam
and ParseFormat. Validate that the pod value size matches the expected
type using spa_pod_type_size() and that the number of choice items
is sufficient before accessing the underlying arrays.

Bug: chromium:545349644
Change-Id: I865e4736f52be1a0dfce47827181dc072630bb57
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/495620
Commit-Queue: Jan Grulich <grulja@gmail.com>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Andreas Pehrson <apehrson@mozilla.com>
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#48344}
diff --git a/modules/video_capture/linux/pipewire_session.cc b/modules/video_capture/linux/pipewire_session.cc
index ba841a7..e17bab8 100644
--- a/modules/video_capture/linux/pipewire_session.cc
+++ b/modules/video_capture/linux/pipewire_session.cc
@@ -46,6 +46,11 @@
 namespace webrtc {
 namespace videocapturemodule {
 
+// Checks that the pod matches the expected type and is large enough to hold it.
+static bool SpaValueIsType(const spa_pod* val, uint32_t type) {
+  return val->type == type && val->size >= spa_pod_type_size(type);
+}
+
 VideoType PipeWireRawFormatToVideoType(uint32_t id) {
   switch (id) {
     case SPA_VIDEO_FORMAT_I420:
@@ -187,20 +192,21 @@
   prop = spa_pod_object_find_prop(obj, prop, SPA_FORMAT_VIDEO_framerate);
   if (prop) {
     val = spa_pod_get_values(&prop->value, &n_items, &choice);
-    if (val->type == SPA_TYPE_Fraction) {
+    if (SpaValueIsType(val, SPA_TYPE_Fraction)) {
       spa_fraction* fract;
 
       fract = static_cast<spa_fraction*>(SPA_POD_BODY(val));
 
-      if (choice == SPA_CHOICE_None) {
+      if (choice == SPA_CHOICE_None && n_items >= 1) {
         cap.maxFPS = 1.0 * fract[0].num / fract[0].denom;
-      } else if (choice == SPA_CHOICE_Enum) {
+      } else if (choice == SPA_CHOICE_Enum && n_items >= 2) {
         for (uint32_t i = 1; i < n_items; i++) {
           cap.maxFPS = std::max(
               static_cast<int32_t>(1.0 * fract[i].num / fract[i].denom),
               cap.maxFPS);
         }
-      } else if (choice == SPA_CHOICE_Range && fract[1].num > 0) {
+      } else if (choice == SPA_CHOICE_Range && n_items >= 2 &&
+                 fract[1].num > 0) {
         cap.maxFPS = 1.0 * fract[1].num / fract[1].denom;
       }
     }
@@ -211,10 +217,10 @@
     return;
 
   val = spa_pod_get_values(&prop->value, &n_items, &choice);
-  if (val->type != SPA_TYPE_Rectangle)
+  if (!SpaValueIsType(val, SPA_TYPE_Rectangle))
     return;
 
-  if (choice != SPA_CHOICE_None)
+  if (choice != SPA_CHOICE_None || n_items < 1)
     return;
 
   if (!ParseFormat(param, &cap))
@@ -269,10 +275,10 @@
       return false;
 
     val = spa_pod_get_values(&prop->value, &n_items, &choice);
-    if (val->type != SPA_TYPE_Id)
+    if (!SpaValueIsType(val, SPA_TYPE_Id))
       return false;
 
-    if (choice != SPA_CHOICE_None)
+    if (choice != SPA_CHOICE_None || n_items < 1)
       return false;
 
     id = static_cast<uint32_t*>(SPA_POD_BODY(val));