std/jpeg: ignore too-short COM or APPn markers
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 91cb81e..4cc7e77 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -49070,11 +49070,14 @@
           }
           self->private_impl.f_payload_length = t_4;
         }
-        if (self->private_impl.f_payload_length < 2u) {
+        if (self->private_impl.f_payload_length >= 2u) {
+          self->private_impl.f_payload_length -= 2u;
+        } else if ((v_marker == 254u) || ((224u <= v_marker) && (v_marker < 240u))) {
+          continue;
+        } else {
           status = wuffs_base__make_status(wuffs_jpeg__error__bad_marker);
           goto exit;
         }
-        self->private_impl.f_payload_length -= 2u;
       }
       if (v_marker < 192u) {
         status = wuffs_base__make_status(wuffs_jpeg__error__unsupported_marker);
diff --git a/script/print-jpeg-markers.go b/script/print-jpeg-markers.go
index 7e5665f..a380326 100644
--- a/script/print-jpeg-markers.go
+++ b/script/print-jpeg-markers.go
@@ -86,7 +86,7 @@
 }
 
 func posError(pos int, lenSrc int) error {
-	return fmt.Errorf("bad JPEG, pos = 0x%08X = %10d, len(src) is 0x%08X = %10d",
+	return fmt.Errorf("bad JPEG, pos = 0x%08X = %10d, len(src) = 0x%08X = %10d",
 		pos, pos, lenSrc, lenSrc)
 }
 
diff --git a/std/jpeg/decode_jpeg.wuffs b/std/jpeg/decode_jpeg.wuffs
index 872fa29..0619fdf 100644
--- a/std/jpeg/decode_jpeg.wuffs
+++ b/std/jpeg/decode_jpeg.wuffs
@@ -426,10 +426,18 @@
         } else {
             this.payload_length = args.src.read_u16be_as_u32?()
             // Payload length includes the 2 bytes for the u16be just read.
-            if this.payload_length < 2 {
+            if this.payload_length >= 2 {
+                this.payload_length -= 2
+            } else if (marker == 0xFE) or  // COM.
+                    ((0xE0 <= marker) and (marker < 0xF0)) {  // APPn.
+                // Strictly speaking, this is invalid according to Section
+                // B.1.1.4 "Marker segments". However, libjpeg-turbo allows it
+                // (for COM and APPn markers; see its jdmarker.c file's
+                // skip_variable function) so we do too.
+                continue
+            } else {
                 return "#bad marker"
             }
-            this.payload_length -= 2
         }
 
         // Switch on the marker.
diff --git a/test/data/artificial-jpeg/hippopotamus-bad-comment-length.jpeg b/test/data/artificial-jpeg/hippopotamus-bad-comment-length.jpeg
new file mode 100644
index 0000000..e8ae5ac
--- /dev/null
+++ b/test/data/artificial-jpeg/hippopotamus-bad-comment-length.jpeg
Binary files differ
diff --git a/test/data/artificial-jpeg/make-hippopotamus-bad-comment-length.go b/test/data/artificial-jpeg/make-hippopotamus-bad-comment-length.go
new file mode 100644
index 0000000..5a5e1ea
--- /dev/null
+++ b/test/data/artificial-jpeg/make-hippopotamus-bad-comment-length.go
@@ -0,0 +1,77 @@
+// Copyright 2024 The Wuffs Authors.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+//go:build ignore
+// +build ignore
+
+package main
+
+// Usage: go run make-hippopotamus-bad-comment-length.go
+//
+// This program inserts a four-byte "FF FE 00 00" fragment into the
+// test/data/hippopotamus.jpeg file. The "FF FE" is a COM (comment) marker. The
+// "00 00" payload length is technically invalid (the minimum length is 2, per
+// the itu-t81.pdf spec section B.1.1.4 "Marker segments") but libjpeg-turbo is
+// more lenient and allows less-than-2 (for COM and APPn markers).
+//
+// https://www.w3.org/Graphics/JPEG/itu-t81.pdf
+//
+// https://github.com/libjpeg-turbo/libjpeg-turbo/blob/2192560d74e6e6cf99dd05928885573be00a8208/jdmarker.c#L869-L876
+//
+// $ go run script/print-jpeg-markers.go test/data/hippopotamus.jpeg
+// pos = 0x00000000 =          0    marker = 0xFF 0xD8  SOI
+// pos = 0x00000002 =          2    marker = 0xFF 0xE0  APP0
+// pos = 0x00000014 =         20    marker = 0xFF 0xDB  DQT
+// pos = 0x00000059 =         89    marker = 0xFF 0xDB  DQT
+// pos = 0x0000009E =        158    marker = 0xFF 0xC0  SOF0 (Sequential/Baseline)
+// pos = 0x000000B1 =        177    marker = 0xFF 0xC4  DHT
+// pos = 0x000000CC =        204    marker = 0xFF 0xC4  DHT
+// pos = 0x000000FC =        252    marker = 0xFF 0xC4  DHT
+// pos = 0x00000119 =        281    marker = 0xFF 0xC4  DHT
+// pos = 0x0000014B =        331    marker = 0xFF 0xDA  SOS
+// pos = 0x000004C1 =       1217    marker = 0xFF 0xD9  EOI
+//
+// $ go run script/print-jpeg-markers.go test/data/artificial-jpeg/hippopotamus-bad-comment-length.jpeg
+// pos = 0x00000000 =          0    marker = 0xFF 0xD8  SOI
+// pos = 0x00000002 =          2    marker = 0xFF 0xE0  APP0
+// pos = 0x00000014 =         20    marker = 0xFF 0xDB  DQT
+// pos = 0x00000059 =         89    marker = 0xFF 0xFE  COM
+// bad JPEG, pos = 0x0000005B =         91, len(src) = 0x000004C7 =       1223
+
+import (
+	"fmt"
+	"os"
+)
+
+func main() {
+	if err := main1(); err != nil {
+		os.Stderr.WriteString(err.Error() + "\n")
+		os.Exit(1)
+	}
+}
+
+func main1() error {
+	src, err := os.ReadFile("../hippopotamus.jpeg")
+	if err != nil {
+		return err
+	} else if len(src) != 0x4C3 {
+		return fmt.Errorf("bad input length: 0x%X", len(src))
+	}
+
+	part0 := src[0x000:0x059]
+	part1 := src[0x059:0x4C3]
+
+	dst := []byte(nil)
+	dst = append(dst, part0...)
+	dst = append(dst, 0xFF, 0xFE, 0x00, 0x00)
+	dst = append(dst, part1...)
+
+	return os.WriteFile("hippopotamus-bad-comment-length.jpeg", dst, 0666)
+}
diff --git a/test/nia-checksums-of-data.txt b/test/nia-checksums-of-data.txt
index 02412ae..60765ac 100644
--- a/test/nia-checksums-of-data.txt
+++ b/test/nia-checksums-of-data.txt
@@ -21,6 +21,7 @@
 OK. 39a71dce test/data/artificial-gif/small-frame-interlaced.gif
 OK. beaec397 test/data/artificial-gif/transparent-index.gif
 BAD f33b9bc1 test/data/artificial-gif/zero-width-frame.gif
+OK. 96bdbbb3 test/data/artificial-jpeg/hippopotamus-bad-comment-length.jpeg
 OK. 96bdbbb3 test/data/artificial-jpeg/hippopotamus-sof-dht-swap.jpeg
 OK. 0564b364 test/data/artificial-png/apng-skip-idat.png
 OK. e08a7cc8 test/data/artificial-png/exif.png