Only validate indexes for paletted BMPs

Validates palette indexes only for BMP images with indexed color depths
(1, 4, or 8 bits); 24-bit and 32-bit images use direct color.

Fixed: pdfium:1168
Change-Id: I4f2a30f8ad5e32ef07e82f18e8323ee92bd78481
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/98670
Commit-Queue: K. Moon <kmoon@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxcodec/bmp/cfx_bmpdecompressor.cpp b/core/fxcodec/bmp/cfx_bmpdecompressor.cpp
index bc2b2e8..6af1ecf 100644
--- a/core/fxcodec/bmp/cfx_bmpdecompressor.cpp
+++ b/core/fxcodec/bmp/cfx_bmpdecompressor.cpp
@@ -377,16 +377,31 @@
     SaveDecodingStatus(DecodeStatus::kData);
     switch (bit_counts_) {
       case 1: {
-        for (uint32_t col = 0; col < width_; ++col)
-          out_row_buffer_[idx++] =
+        for (uint32_t col = 0; col < width_; ++col) {
+          uint8_t index =
               dest_buf[col >> 3] & (0x80 >> (col % 8)) ? 0x01 : 0x00;
+          if (!ValidateColorIndex(index))
+            return BmpDecoder::Status::kFail;
+          out_row_buffer_[idx++] = index;
+        }
         break;
       }
       case 4: {
         for (uint32_t col = 0; col < width_; ++col) {
-          out_row_buffer_[idx++] = (col & 0x01)
-                                       ? (dest_buf[col >> 1] & 0x0F)
+          uint8_t index = (col & 0x01) ? (dest_buf[col >> 1] & 0x0F)
                                        : ((dest_buf[col >> 1] & 0xF0) >> 4);
+          if (!ValidateColorIndex(index))
+            return BmpDecoder::Status::kFail;
+          out_row_buffer_[idx++] = index;
+        }
+        break;
+      }
+      case 8: {
+        for (uint32_t col = 0; col < width_; ++col) {
+          uint8_t index = dest_buf[col];
+          if (!ValidateColorIndex(index))
+            return BmpDecoder::Status::kFail;
+          out_row_buffer_[idx++] = index;
         }
         break;
       }
@@ -421,19 +436,15 @@
         }
         break;
       }
-      case 8:
       case 24:
       case 32:
+        // TODO(crbug.com/pdfium/1901): Apply bitfields.
         uint8_t* dest_buf_data = dest_buf.data();
         std::copy(dest_buf_data, dest_buf_data + src_row_bytes_,
                   out_row_buffer_.begin());
         idx += src_row_bytes_;
         break;
     }
-    for (uint8_t byte : out_row_buffer_) {
-      if (!ValidateColorIndex(byte))
-        return BmpDecoder::Status::kFail;
-    }
     ReadNextScanline();
   }
   SaveDecodingStatus(DecodeStatus::kTail);
diff --git a/core/fxcodec/progressive_decoder_unittest.cpp b/core/fxcodec/progressive_decoder_unittest.cpp
index ed5c4de..33c20fa 100644
--- a/core/fxcodec/progressive_decoder_unittest.cpp
+++ b/core/fxcodec/progressive_decoder_unittest.cpp
@@ -109,6 +109,70 @@
   status = DecodeToBitmap(decoder, bitmap);
   EXPECT_EQ(FXCODEC_STATUS::kError, status);
 }
+
+TEST(ProgressiveDecoder, Direct24Bmp) {
+  static constexpr uint8_t kInput[] = {
+      0x42, 0x4d, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00,
+      0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+      0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+      0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0x00};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(source, FXCODEC_IMAGE_BMP, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(1, decoder.GetWidth());
+  ASSERT_EQ(1, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+  EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00));
+}
+
+TEST(ProgressiveDecoder, Direct32Bmp) {
+  static constexpr uint8_t kInput[] = {
+      0x42, 0x4d, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00,
+      0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+      0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+      0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0xff};
+
+  ProgressiveDecoder decoder;
+
+  auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
+  CFX_DIBAttribute attr;
+  FXCODEC_STATUS status =
+      decoder.LoadImageInfo(source, FXCODEC_IMAGE_BMP, &attr, true);
+  ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
+
+  ASSERT_EQ(1, decoder.GetWidth());
+  ASSERT_EQ(1, decoder.GetHeight());
+
+  auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
+  bitmap->Create(decoder.GetWidth(), decoder.GetHeight(), FXDIB_Format::kRgb);
+
+  size_t frames;
+  std::tie(status, frames) = decoder.GetFrames();
+  ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
+  ASSERT_EQ(1u, frames);
+
+  status = DecodeToBitmap(decoder, bitmap);
+  EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
+  EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00));
+}
 #endif  // PDF_ENABLE_XFA_BMP
 
 #ifdef PDF_ENABLE_XFA_GIF