Source: lcms2
Version: 2.2+git20110628-2
Severity: important
Justification: Fails to build from source
Tags: patch
lcms2 FTBFS on Alpha due to a test bed failure. The important line from
the log is:
Checking Profile creation .....................................FAIL!
Profile creation: [Tags holding CRD info]
The full log is at:
http://buildd.debian-ports.org/status/fetch.php?pkg=lcms2&arch=alpha&ver=2.2%2Bgit20110628-2&stamp=1312363597
The cause of the failure is the code:
cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*)
LanguageCode);
cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*)
CountryCode);
in the file src/cmsnamed.c in the routines cmsMLUsetASCII(), etc. The
problem is that LanguageCode is declared as a string literal, thus, on
Alpha, can be aligned at any byte address. It is cast to
cmsUInt16Number * then dereferenced however on Alpha a 16-bit word must
be aligned to an address exactly divisible by two. Normally an access
to a 16-bit word at an odd address would invoke a trap into the kernel
to complete the access, but by a quirk of the way the compiler compiles
this particular code, it never makes an unaligned access and if the word
lies on addresses ending in 0x7 or 0xF it misreads the word. I am under
the impression that this cannot be considered a bug in the compiler, as,
if I am correct, dereferencing a pointer that cannot point to an object
(e.g. because it is misaligned) is undefined behaviour according to the
C standard.
I attach a patch that fixes the issue. It uses a union to convert from
the string LanguageCode to the 16-bit word. The alignment of a union
must satisfy the alignment of all its members thus guarantees that the
pointer to the word can be taken from the pointer to char. It is not
the way I would implement this from scratch---it is a fix given the
extant code. A better way would be to construct the word by something like:
((unsigned short int)LanguageCode[0]<<8) + LanguageCode[1]
and switch which array elements are chosen depending on system
endianess. But I wasn't game enough to implement this as I guessed
there might be two conflating issues: endianess of the CPU and endianess
of the ICC profile.
Actually lcms2 is really bad with regards memory alignment in general.
Lots of misaligned accesses are reported in the system log as being
fixed by the kernel. I intend to report them under another bug number
as they represent an efficiency issue, not a failure to work at all.
Cheers
Michael.
--- lcms2-2.2+git20110628/src/cmsnamed.c 2011-06-28 16:20:02.000000000 +1200
+++ lcms2-2.2+git20110628-new/src/cmsnamed.c 2011-10-05 22:41:05.000000000 +1300
@@ -185,8 +185,19 @@
cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString)+1;
wchar_t* WStr;
cmsBool rc;
- cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
- cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
+ union {
+ cmsUInt16Number uint16;
+ char str[2];
+ } code_convert;
+ cmsUInt16Number Lang;
+ cmsUInt16Number Cntry;
+
+ code_convert.str[0] = LanguageCode[0];
+ code_convert.str[1] = LanguageCode[1];
+ Lang = _cmsAdjustEndianess16(code_convert.uint16);
+ code_convert.str[0] = CountryCode[0];
+ code_convert.str[1] = CountryCode[1];
+ Cntry = _cmsAdjustEndianess16(code_convert.uint16);
if (mlu == NULL) return FALSE;
@@ -220,10 +231,21 @@
// Add a wide entry
cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString)
{
- cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) Language);
- cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) Country);
cmsUInt32Number len;
-
+ union {
+ cmsUInt16Number uint16;
+ char str[2];
+ } code_convert;
+ cmsUInt16Number Lang;
+ cmsUInt16Number Cntry;
+
+ code_convert.str[0] = Language[0];
+ code_convert.str[1] = Language[1];
+ Lang = _cmsAdjustEndianess16(code_convert.uint16);
+ code_convert.str[0] = Country[0];
+ code_convert.str[1] = Country[1];
+ Cntry = _cmsAdjustEndianess16(code_convert.uint16);
+
if (mlu == NULL) return FALSE;
if (WideString == NULL) return FALSE;
@@ -350,8 +372,19 @@
cmsUInt32Number StrLen = 0;
cmsUInt32Number ASCIIlen, i;
- cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
- cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
+ union {
+ cmsUInt16Number uint16;
+ char str[2];
+ } code_convert;
+ cmsUInt16Number Lang;
+ cmsUInt16Number Cntry;
+
+ code_convert.str[0] = LanguageCode[0];
+ code_convert.str[1] = LanguageCode[1];
+ Lang = _cmsAdjustEndianess16(code_convert.uint16);
+ code_convert.str[0] = CountryCode[0];
+ code_convert.str[1] = CountryCode[1];
+ Cntry = _cmsAdjustEndianess16(code_convert.uint16);
// Sanitize
if (mlu == NULL) return 0;
@@ -394,8 +427,19 @@
const wchar_t *Wide;
cmsUInt32Number StrLen = 0;
- cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
- cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
+ union {
+ cmsUInt16Number uint16;
+ char str[2];
+ } code_convert;
+ cmsUInt16Number Lang;
+ cmsUInt16Number Cntry;
+
+ code_convert.str[0] = LanguageCode[0];
+ code_convert.str[1] = LanguageCode[1];
+ Lang = _cmsAdjustEndianess16(code_convert.uint16);
+ code_convert.str[0] = CountryCode[0];
+ code_convert.str[1] = CountryCode[1];
+ Cntry = _cmsAdjustEndianess16(code_convert.uint16);
// Sanitize
if (mlu == NULL) return 0;
@@ -426,10 +470,19 @@
char ObtainedLanguage[3], char ObtainedCountry[3])
{
const wchar_t *Wide;
-
- cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode);
- cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode);
- cmsUInt16Number ObtLang, ObtCode;
+ cmsUInt16Number ObtLang, ObtCode;
+
+ union {
+ cmsUInt16Number uint16;
+ char str[3];
+ } code_convert;
+ cmsUInt16Number Lang;
+ cmsUInt16Number Cntry;
+
+ strcpy(code_convert.str, LanguageCode);
+ Lang = _cmsAdjustEndianess16(code_convert.uint16);
+ strcpy(code_convert.str, CountryCode);
+ Cntry = _cmsAdjustEndianess16(code_convert.uint16);
// Sanitize
if (mlu == NULL) return FALSE;