Work around another bad JPEG header.

Add a second known bad offset in JpegDecoder and generalize the bad
header detection/patching code to deal with different offsets.

BUG=pdfium:1175

Change-Id: I65c7099e29ee6de47c19e603b2ddbe78b041b52d
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/56531
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcodec/jpeg/jpegmodule.cpp b/core/fxcodec/jpeg/jpegmodule.cpp
index 9ba2e3e..fe07cf5 100644
--- a/core/fxcodec/jpeg/jpegmodule.cpp
+++ b/core/fxcodec/jpeg/jpegmodule.cpp
@@ -20,6 +20,7 @@
 #include "core/fxge/dib/cfx_dibbase.h"
 #include "core/fxge/fx_dib.h"
 #include "third_party/base/logging.h"
+#include "third_party/base/optional.h"
 #include "third_party/base/ptr_util.h"
 
 extern "C" {
@@ -215,6 +216,8 @@
 
 namespace {
 
+constexpr size_t kKnownBadHeaderWithInvalidHeightByteOffsetStarts[] = {94, 163};
+
 class JpegDecoder final : public ScanlineDecoder {
  public:
   JpegDecoder();
@@ -248,24 +251,24 @@
   void InitDecompressSrc();
 
   // Can only be called inside a jpeg_read_header() setjmp handler.
-  bool HasKnownBadHeaderWithInvalidHeight() const;
+  bool HasKnownBadHeaderWithInvalidHeight(size_t dimension_offset) const;
 
   // Is a JPEG SOFn marker, which is defined as 0xff, 0xc[0-9a-f].
-  bool IsSofSegment(int marker_offset) const;
+  bool IsSofSegment(size_t marker_offset) const;
 
   // Patch up the in-memory JPEG header for known bad JPEGs.
-  void PatchUpKnownBadHeaderWithInvalidHeight();
+  void PatchUpKnownBadHeaderWithInvalidHeight(size_t dimension_offset);
 
   // Patch up the JPEG trailer, even if it is correct.
   void PatchUpTrailer();
 
   uint8_t* GetWritableSrcData();
 
-  // Before |kKnownBadHeaderWithInvalidHeightByteOffsetStart|.
+  // For a given invalid height byte offset in
+  // |kKnownBadHeaderWithInvalidHeightByteOffsetStarts|, the SOFn marker should
+  // be this many bytes before that.
   static const size_t kSofMarkerByteOffset = 5;
 
-  static const int kKnownBadHeaderWithInvalidHeightByteOffsetStart = 163;
-
   uint32_t m_nDefaultScaleDenom = 1;
 };
 
@@ -291,15 +294,22 @@
   m_bInited = true;
 
   if (setjmp(m_JmpBuf) == -1) {
-    bool bHasKnownBadHeader =
-        bAcceptKnownBadHeader && HasKnownBadHeaderWithInvalidHeight();
+    Optional<size_t> known_bad_header_offset;
+    if (bAcceptKnownBadHeader) {
+      for (size_t offset : kKnownBadHeaderWithInvalidHeightByteOffsetStarts) {
+        if (HasKnownBadHeaderWithInvalidHeight(offset)) {
+          known_bad_header_offset = offset;
+          break;
+        }
+      }
+    }
     jpeg_destroy_decompress(&m_Cinfo);
-    if (!bHasKnownBadHeader) {
+    if (!known_bad_header_offset.has_value()) {
       m_bInited = false;
       return false;
     }
 
-    PatchUpKnownBadHeaderWithInvalidHeight();
+    PatchUpKnownBadHeaderWithInvalidHeight(known_bad_header_offset.value());
 
     jpeg_create_decompress(&m_Cinfo);
     InitDecompressSrc();
@@ -416,7 +426,8 @@
   m_Src.next_input_byte = m_SrcSpan.data();
 }
 
-bool JpegDecoder::HasKnownBadHeaderWithInvalidHeight() const {
+bool JpegDecoder::HasKnownBadHeaderWithInvalidHeight(
+    size_t dimension_offset) const {
   // Perform lots of possibly redundant checks to make sure this has no false
   // positives.
   bool bDimensionChecks = m_Cinfo.err->msg_code == JERR_IMAGE_TOO_BIG &&
@@ -428,16 +439,13 @@
   if (!bDimensionChecks)
     return false;
 
-  if (m_SrcSpan.size() <= kKnownBadHeaderWithInvalidHeightByteOffsetStart + 3)
+  if (m_SrcSpan.size() <= dimension_offset + 3u)
     return false;
 
-  if (!IsSofSegment(kKnownBadHeaderWithInvalidHeightByteOffsetStart -
-                    kSofMarkerByteOffset)) {
+  if (!IsSofSegment(dimension_offset - kSofMarkerByteOffset))
     return false;
-  }
 
-  const uint8_t* pHeaderDimensions =
-      &m_SrcSpan[kKnownBadHeaderWithInvalidHeightByteOffsetStart];
+  const uint8_t* pHeaderDimensions = &m_SrcSpan[dimension_offset];
   uint8_t nExpectedWidthByte1 = (m_OrigWidth >> 8) & 0xff;
   uint8_t nExpectedWidthByte2 = m_OrigWidth & 0xff;
   // Height high byte, height low byte, width high byte, width low byte.
@@ -446,17 +454,16 @@
          pHeaderDimensions[3] == nExpectedWidthByte2;
 }
 
-bool JpegDecoder::IsSofSegment(int marker_offset) const {
+bool JpegDecoder::IsSofSegment(size_t marker_offset) const {
   const uint8_t* pHeaderMarker = &m_SrcSpan[marker_offset];
   return pHeaderMarker[0] == 0xff && pHeaderMarker[1] >= 0xc0 &&
          pHeaderMarker[1] <= 0xcf;
 }
 
-void JpegDecoder::PatchUpKnownBadHeaderWithInvalidHeight() {
-  ASSERT(m_SrcSpan.size() >
-         kKnownBadHeaderWithInvalidHeightByteOffsetStart + 1);
-  uint8_t* pData =
-      GetWritableSrcData() + kKnownBadHeaderWithInvalidHeightByteOffsetStart;
+void JpegDecoder::PatchUpKnownBadHeaderWithInvalidHeight(
+    size_t dimension_offset) {
+  ASSERT(m_SrcSpan.size() > dimension_offset + 1u);
+  uint8_t* pData = GetWritableSrcData() + dimension_offset;
   pData[0] = (m_OrigHeight >> 8) & 0xff;
   pData[1] = m_OrigHeight & 0xff;
 }