Avoid integer underflow in SkiaState::AdjustClip().

When the current `m_clipIndex` is larger than the given `limit` index,
SkiaState::AdjustClip() looks through the index range below the current
`m_clipIndex` until it finds a save command to restore. If no save
command is found, it reaches an assertion failure due to the index being
negative.

This CL makes sure `m_clipIndex` is non-negative when using it to access
and check command types. If no save command is found, skip restoring
and set `m_clipIndex` to 0, so that all commands below index `limit` can
be processed later. The added pixel test is a minimized version of the
PDF that triggered the assertion failure in crbug.com/1116869.

Bug: chromium:1116869
Change-Id: I47a71918c561c1cb121b91f929d3f0f60b3f22e9
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/78570
Reviewed-by: Daniel Hosseinian <dhoss@chromium.org>
Commit-Queue: Hui Yingst <nigi@chromium.org>
diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp
index 972ecf3..4442a65 100644
--- a/core/fxge/skia/fx_skia_device.cpp
+++ b/core/fxge/skia/fx_skia_device.cpp
@@ -1242,9 +1242,11 @@
     while (m_clipIndex > limit) {
       do {
         --m_clipIndex;
-        DCHECK(m_clipIndex >= 0);
-      } while (m_commands[m_clipIndex] != Clip::kSave);
-      m_pDriver->SkiaCanvas()->restore();
+      } while (m_clipIndex >= 0 && m_commands[m_clipIndex] != Clip::kSave);
+      if (m_clipIndex >= 0)
+        m_pDriver->SkiaCanvas()->restore();
+      else
+        m_clipIndex = 0;
     }
     while (m_clipIndex < limit) {
       if (Clip::kSave == m_commands[m_clipIndex]) {
diff --git a/testing/resources/pixel/bug_1116869.in b/testing/resources/pixel/bug_1116869.in
new file mode 100644
index 0000000..d123e38
--- /dev/null
+++ b/testing/resources/pixel/bug_1116869.in
@@ -0,0 +1,117 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 5 0 R
+  /MediaBox [0 0 400 400]
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+    /Pattern <<
+      /P1 6 0 R
+    >>
+    /XObject <<
+      /X10 9 0 R
+    >>
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+/X10 Do
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /Pattern
+  /PatternType 2
+  /Shading 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+  /ShadingType 2
+  /ColorSpace /DeviceCMYK
+  /Coords [0.0 0.0 1.0 0.0]
+  /Extend [true true]
+  /Function 8 0 R
+>>
+endobj
+{{object 8 0}} <<
+  /FunctionType 0
+  /BitsPerSample 1
+  /Domain [0.0 1.0]
+  /Filter /ASCIIHexDecode
+  /Range [0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]
+  /Size [2]
+  {{streamlen}}
+>>
+stream
+ff
+endstream
+endobj
+{{object 9 0}} <<
+  /Subtype /Form
+  /Resources <<
+    /ExtGState <<
+      /G7 10 0 R
+    >>
+  >>
+  {{streamlen}}
+>>
+stream
+q
+0 0 m
+3 31 l
+/G7 gs
+f
+endstream
+endobj
+{{object 10 0}} <<
+  /SMas <<
+    /G 11 0 R
+  >>
+>>
+endobj
+{{object 11 0}} <<
+  {{streamlen}}
+>>
+stream
+1 1 2 2 re
+W
+100 100 200 200 re s
+W
+0 g
+/F1 12 Tf
+0 0 60 30 re
+W
+m 100 100
+130 150 200 200 250 230 c
+/Pattern cs
+/P1 scn
+(ABC) Tj
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1116869_expected.pdf.0.png b/testing/resources/pixel/bug_1116869_expected.pdf.0.png
new file mode 100644
index 0000000..1ff3f1f
--- /dev/null
+++ b/testing/resources/pixel/bug_1116869_expected.pdf.0.png
Binary files differ