reject negative inv.a
Turns out we _can_ create a (only very very slightly) negative a term
when inverting a profile that passes our sanity tests.
Reject them like we do in fitting. Was thinking about nudging it up to
0, but that would imply some strange things, like x not mattering...
Bug: oss-fuzz:16581
Change-Id: I2b30a36250936042da08e0f1771f5dc7610b9853
Reviewed-on: https://skia-review.googlesource.com/c/skcms/+/236082
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
diff --git a/profiles/fuzz/negative_a_when_inverted.icc b/profiles/fuzz/negative_a_when_inverted.icc
new file mode 100644
index 0000000..e5e34d5
--- /dev/null
+++ b/profiles/fuzz/negative_a_when_inverted.icc
Binary files differ
diff --git a/profiles/fuzz/negative_a_when_inverted.icc.txt b/profiles/fuzz/negative_a_when_inverted.icc.txt
new file mode 100644
index 0000000..8c157bb
--- /dev/null
+++ b/profiles/fuzz/negative_a_when_inverted.icc.txt
@@ -0,0 +1,38 @@
+ Size : 0x00000090 : 144
+ Data color space : 0x47524159 : 'GRAY'
+ PCS : 0x58595A20 : 'XYZ '
+ Tag count : 0x00000001 : 1
+
+ Tag : Type : Size : Offset
+ ------ : ------ : ------ : --------
+ 'kTRC' : 'curv' : 102 : 32
+
+rTRC : 16-bit table with 41 entries
+gTRC : 16-bit table with 41 entries
+bTRC : 16-bit table with 41 entries
+ XYZ : | 0.95751953 0 0 |
+ | 0 1.0004883 0 |
+ | 0 0 0.82029724 |
+252 random bytes transformed to linear XYZD50 bytes:
+ 4d201a 1f571a 1f201a 00201a 002074 1f201a 2d2000
+ 1f201a 330d8d 1f001a 4d201a 1f2063 1f201a 1f201a
+ 1f201a 1f201a 1f202c 1fb0ad 1f311a 679f1a 82201a
+ 6e20a1 683e1a 1f203a 1f201a 1f2000 9c4f1a 1f201a
+ 1f201a 1f001a b822bb 1f209f 1f3c1a b7201a 1f202f
+ 1f201a 4d7cc4 1f5a1a 5a201a 1f201a 1f201a 1f201e
+ 1f201a 1f885a 1f201a 1f201b 1a201a 1f136e 1f201a
+ a320ca 9c20a2 1f176c 1f201a 1f201a 1f204f 1f8f1a
+ 002000 1f201a 1f201a 1f621a 1f201a 1f2087 1f5636
+ 1f201a 1f001a 1f2034 1f461a 55251a 372057 1f201a
+ 1f2006 000a1a 1f201a 1f201a 00202b 287c0e 77d31a
+ c1351a 1f201a 9d201a 1f3059 1f201a 1f0300 00201a
+81 edge-case pixels transformed to sRGB 8888 (unpremul):
+ 00636462 00636462 00636462 00636462 00636462 00636462 00636462 00636462 00636462
+ 00636462 00636462 00636462 00636462 00636462 00636462 00636462 00636462 00636462
+ 00636462 00636462 00636462 00636462 00636462 00636462 00636462 00636462 00636462
+ 7f636462 7f636462 7f636462 7f636462 7f636462 7f636462 7f636462 7f636462 7f636462
+ 7f636462 7f636462 7f636462 7f636462 7f636462 7f636462 7f636462 7f636462 7f636462
+ 7f636462 7f636462 7f636462 7f636462 7f636462 7f636462 7f636462 7f636462 7f636462
+ ff636462 ff636462 ff636462 ff636462 ff636462 ff636462 ff636462 ff636462 ff636462
+ ff636462 ff636462 ff636462 ff636462 ff636462 ff636462 ff636462 ff636462 ff636462
+ ff636462 ff636462 ff636462 ff636462 ff636462 ff636462 ff636462 ff636462 ff636462
diff --git a/skcms.cc b/skcms.cc
index 65188ca..12c1229 100644
--- a/skcms.cc
+++ b/skcms.cc
@@ -1476,13 +1476,18 @@
// We need to enforce the same constraints here that we do when fitting a curve,
// a >= 0 and ad+b >= 0. These constraints are checked by tf_is_valid(), so they're true
- // of the source function if we're here. And if the source a >= 0, inv.a definitely is too.
- assert (inv.a >= 0);
+ // of the source function if we're here.
- // On the other hand our ad+b could have gone slightly negative here. Tweak inv.b if needed.
+ // Just like when fitting the curve, there's really no way to rescue a < 0.
+ if (inv.a < 0) {
+ return false;
+ }
+ // On the other hand we can rescue an ad+b that's gone slightly negative here.
if (inv.a * inv.d + inv.b < 0) {
inv.b = -inv.a * inv.d;
}
+
+ assert (inv.a >= 0);
assert (inv.a * inv.d + inv.b >= 0);
// Now in principle we're done.
diff --git a/tests.c b/tests.c
index 3b849c2..57083a7 100644
--- a/tests.c
+++ b/tests.c
@@ -680,6 +680,9 @@
// Reasonable table, but gets approximated very badly
"profiles/misc/crbug_976551.icc", // chromium:976551
+
+ // The a term goes negative when inverting.
+ "profiles/fuzz/negative_a_when_inverted.icc", // oss-fuzz:16581
};
static void test_Parse(bool regen) {