Improve testing coverage of new code and fix bugs that it found.
diff --git a/Makefile b/Makefile
index ebdb147..ce119b7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 
-#COVERAGE_FLAGS=-fprofile-arcs -ftest-coverage 
+COVERAGE_FLAGS=-fprofile-arcs -ftest-coverage
 #COVERAGE_FLAGS=
 OPT_FLAGS=-O0
 OPT_FLAGS=
@@ -15,5 +15,10 @@
 
 $(PROGRAMS): $(QCMS_OBJS)
 
+gen-coverage:
+	mkdir -p lcov
+	lcov -d . -c --output-file lcov/lcov.info
+	genhtml -o lcov/ lcov/lcov.info
+
 clean:
 	rm -f $(PROGRAMS) $(QCMS_OBJS)
diff --git a/iccread.c b/iccread.c
index 2e4a645..e1c9c9a 100644
--- a/iccread.c
+++ b/iccread.c
@@ -674,6 +674,7 @@
 		num_output_table_entries = read_u16(src, offset + 50);
 		entry_size = 2;
 	} else {
+		assert(0); // the caller checks that this doesn't happen
 		invalid_source(src, "Unexpected lut type");
 		return NULL;
 	}
@@ -727,9 +728,9 @@
 	clut_offset = offset + 52 + lut->num_input_table_entries * in_chan * entry_size;
 	for (i = 0; i < clut_size * out_chan; i+=3) {
 		if (type == LUT8_TYPE) {
-			lut->clut_table[i*3+0] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 0));
-			lut->clut_table[i*3+1] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 1));
-			lut->clut_table[i*3+2] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 2));
+			lut->clut_table[i+0] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 0));
+			lut->clut_table[i+1] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 1));
+			lut->clut_table[i+2] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 2));
 		} else {
 			lut->clut_table[i+0] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 0));
 			lut->clut_table[i+1] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 2));
@@ -1045,8 +1046,8 @@
 				}
 			}
 			if (find_tag(index, TAG_B2A0)) {
-				if (read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT8_TYPE ||
-				    read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT16_TYPE) {
+				if (read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT8_TYPE ||
+				    read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT16_TYPE) {
 					profile->B2A0 = read_tag_lutType(src, index, TAG_B2A0);
 				} else if (read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT_MBA_TYPE) {
 					profile->mBA = read_tag_lutmABType(src, index, TAG_B2A0);
@@ -1076,6 +1077,7 @@
 				goto invalid_tag_table;
 
 		} else {
+			assert(0 && "read_color_space protects against entering here");
 			goto invalid_tag_table;
 		}
 	} else {
diff --git a/invalid-coverage.c b/invalid-coverage.c
index da82b11..b96df73 100644
--- a/invalid-coverage.c
+++ b/invalid-coverage.c
@@ -38,58 +38,66 @@
 {
 	*(uint8_t*)(buf + offset) = value;
 }
+static void write_u16(size_t offset, uint16_t value)
+{
+	*(uint8_t*)(buf + offset + 1) = value & 0xff;
+	*(uint8_t*)(buf + offset) = (value>>8) & 0xff;
+}
 
 
+#define PROFILE_LENGTH 18000
 int main()
 {
 	qcms_profile_release(qcms_profile_sRGB());
 
-	buf = calloc(1500, 1);
-	assert(!qcms_profile_from_memory(buf, 1500));
+	buf = calloc(PROFILE_LENGTH, 1);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 	// invalid size
-	write_u32(0, 2500);
-	assert(!qcms_profile_from_memory(buf, 1500));
+	write_u32(0, PROFILE_LENGTH + 2500);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	// proper size
-	write_u32(0, 1500);
-	assert(!qcms_profile_from_memory(buf, 1500));
+	write_u32(0, PROFILE_LENGTH);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 #define INPUT_DEVICE_PROFILE   0x73636e72 // 'scnr'
 	write_u32(12, INPUT_DEVICE_PROFILE);
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	write_u8(8, 0x3); // invalid major revision
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	write_u8(8, 0x2); // major revision
 	write_u8(9, 0x55); // invalid minor revision
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	write_u8(8, 0x2); // major revision
 	write_u8(9, 0x40); // minor revision
 	write_u8(10, 1); // reserved 1
 	write_u8(11, 0); // reserved 2
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 	write_u8(10, 0); // reserved 1
 
 	write_u8(64, 0x32); // invalid rendering intent
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 	write_u8(64, 0); // invalid rendering intent
 
 #define RGB_SIGNATURE  0x52474220
 #define GRAY_SIGNATURE 0x47524159
-	write_u32(16, RGB_SIGNATURE);
-	assert(!qcms_profile_from_memory(buf, 1500));
+	write_u32(16, 0xdeadbeef);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
+	write_u32(16, RGB_SIGNATURE);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 #define XYZ_SIGNATURE  0x58595A20
 	write_u32(20, XYZ_SIGNATURE);
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	write_u32(128, 15000); // tag count
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	write_u32(128, 15); // tag count
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
      #define TAG_bXYZ 0x6258595a
      #define TAG_gXYZ 0x6758595a
@@ -99,67 +107,202 @@
      #define TAG_gTRC 0x67545243
      #define TAG_kTRC 0x6b545243
      #define TAG_A2B0 0x41324230
+     #define TAG_B2A0 0x42324130
+     #define TAG_CHAD 0x63686164
 
 	write_u32(128 + 4, TAG_rXYZ); // tag
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 	write_u32(128 + 4 + 4, 1000); // offset
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	write_u32(128 + 4 + 4*1*3, TAG_gXYZ); // tag
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 	write_u32(128 + 4 + 4*1*3 + 4, 1000); // offset
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	write_u32(128 + 4 + 4*2*3, TAG_bXYZ); // tag
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 	write_u32(128 + 4 + 4*2*3 + 4, 1000); // offset
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 #define XYZ_TYPE   0x58595a20 // 'XYZ '
 #define CURVE_TYPE 0x63757276 // 'curv'
 #define LUT16_TYPE 0x6d667432 // 'mft2'
 #define LUT8_TYPE  0x6d667431 // 'mft1'
-
+#define LUT_MAB_TYPE		0x6d414220 // 'mAB '
+#define LUT_MBA_TYPE		0x6d424120 // 'mBA '
+#define CHROMATIC_TYPE          0x73663332 // 'sf32'
+#define PARAMETRIC_CURVE_TYPE   0x70617261 // 'para'
 	write_u32(1000, XYZ_TYPE);
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	write_u32(128 + 4 + 4*3*3, TAG_rTRC); // tag
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 	write_u32(128 + 4 + 4*3*3 + 4, 1100); // offset
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	write_u32(128 + 4 + 4*4*3, TAG_gTRC); // tag
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 	write_u32(128 + 4 + 4*4*3 + 4, 1100); // offset
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	write_u32(128 + 4 + 4*5*3, TAG_bTRC); // tag
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 	write_u32(128 + 4 + 4*5*3 + 4, 1100); // offset
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(128 + 4 + 4*6*3, TAG_A2B0); // tag
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(128 + 4 + 4*6*3 + 4, 1200); // offset
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(128 + 4 + 4*7*3, TAG_CHAD); // tag
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u32(128 + 4 + 4*7*3 + 4, 5000); // offset
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(1200, 5); // invalid lut type
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(1200, LUT8_TYPE);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u8(1200 + 8, 9); // max clut size
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u8(1200 + 10, 9); // max clut size
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u8(1200 + 8, 3); // in_chan
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u8(1200 + 9, 3); // proper out_chan
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u8(1200 + 10, 1); // sane clut size
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(1200, LUT16_TYPE);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u16(1200 + 48, 3); // input_table_entries
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u16(1200 + 50, 3); // output_table_entries
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(128 + 4 + 4*6*3, TAG_A2B0); // tag
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(128 + 4 + 4*6*3, TAG_B2A0); // tag
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(1200, LUT_MBA_TYPE);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(1200, LUT_MAB_TYPE);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(128 + 4 + 4*6*3, TAG_A2B0); // tag
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u8(1200 + 8, 15); // > max in_chan
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u8(1200 + 9, 15); // > max out_chan
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u8(1200 + 9, 4); // long out_chan
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u8(1200 + 8, 2); // short in_chan
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u8(1200 + 9, 3); // proper out_chan
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u8(1200 + 8, 3); // proper out_chan
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 
+	write_u32(1200 + 12, 5); // b curve offset
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(1200 + 12, 2000); // b curve offset
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(1200 + 16, 2000); // matrix offset
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(1200 + 20, 2000); // m curve offset
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(1200 + 24, 3000); // clut offset
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u8(4200, 255); // clut channel size
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u8(4201, 255); // clut channel size
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u8(4202, 255); // clut channel size
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u8(4200, 2); // clut channel size
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u8(4201, 2); // clut channel size
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u8(4202, 2); // clut channel size
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u8(4216, 1); // clut precision
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u8(4216, 2); // clut precision
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(1200 + 28, 2000); // a curve offset
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(3200, PARAMETRIC_CURVE_TYPE);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u16(3208, 5);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u16(3208, 4);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u16(3208, 2);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(3200, CURVE_TYPE);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	// reset the values
+	write_u16(3208, 0);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+
+	write_u32(3212, CURVE_TYPE);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u32(3224, CURVE_TYPE);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+
+	write_u32(5000, CHROMATIC_TYPE);
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+#if 1
 	write_u32(1100, CURVE_TYPE);
-	qcms_profile_release(qcms_profile_from_memory(buf, 1500));
+	qcms_profile_release(qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	write_u32(1108, 100000); // curve count
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	write_u32(1108, 1); // curve count
-	qcms_profile_release(qcms_profile_from_memory(buf, 1500));
+	qcms_profile_release(qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	/* test out gray profiles */
 	write_u32(16, GRAY_SIGNATURE);
-	assert(!qcms_profile_from_memory(buf, 1500));
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
-	write_u32(128 + 4 + 4*6*3, TAG_kTRC); // tag
-	assert(!qcms_profile_from_memory(buf, 1500));
-	write_u32(128 + 4 + 4*6*3 + 4, 1100); // offset
-	qcms_profile_release(qcms_profile_from_memory(buf, 1500));
+	write_u32(128 + 4 + 4*8*3, TAG_kTRC); // tag
+	assert(!qcms_profile_from_memory(buf, PROFILE_LENGTH));
+	write_u32(128 + 4 + 4*8*3 + 4, 1100); // offset
+	qcms_profile_release(qcms_profile_from_memory(buf, PROFILE_LENGTH));
 
 	/* test out profiles that are the wrong size */
 	qcms_profile_from_path("sample-trunc.icc");
-
+#endif
 	return 0;
 }
 
diff --git a/malloc-fail.c b/malloc-fail.c
index ed93b1e..9554aa4 100644
--- a/malloc-fail.c
+++ b/malloc-fail.c
@@ -78,9 +78,13 @@
 	unsigned char outt[4];
 	qcms_transform *transform;
 	qcms_profile *input_profile, *output_profile, *rgb;
-	qcms_CIE_xyY white_point = { 0, 0, 1.};
+	qcms_CIE_xyY invalid_white_point = { 0., 0., 1.};
+	qcms_CIE_xyY white_point = { 0.9, 1., 1.};
 	qcms_CIE_xyYTRIPLE primaries = { {.9, .3, 1.}, {.2, .4, 1.}, {.7, .4, 1.}};
 
+	rgb = qcms_profile_create_rgb_with_gamma(invalid_white_point, primaries, 1.8);
+	if (rgb)
+		qcms_profile_release(rgb);
 	rgb = qcms_profile_create_rgb_with_gamma(white_point, primaries, 1.8);
 	input_profile = qcms_profile_sRGB();
 	output_profile = qcms_profile_sRGB();