N
Noob
Hello,
I'm dealing with a library whose author seems to have relied implicitly
on several non-portable features. I'm trying to expose every assumption.
(In the context of C89).
typedef struct {
unsigned int aa : 1;
unsigned int bb : 1;
unsigned int cc : 1;
unsigned int dd : 1;
unsigned int ee : 1;
unsigned int ff : 1;
unsigned int gg : 1;
unsigned int hh : 1;
unsigned int reserved : 24;
} bitmap_t;
typedef union {
unsigned char uc;
bitmap_t map;
} foo_t;
unsigned frob(unsigned u)
{
foo_t foo;
foo.map.aa = (u >> 0) & 1;
foo.map.bb = (u >> 1) & 1;
foo.map.cc = (u >> 2) & 1;
foo.map.dd = (u >> 3) & 1;
foo.map.ee = (u >> 4) & 1;
foo.map.ff = (u >> 5) & 1;
foo.map.gg = (u >> 6) & 1;
foo.map.hh = (u >> 7) & 1;
/* garbage in foo.map.reserved */
return foo.uc;
}
int main(void)
{
unsigned res;
/* my tests */
res = frob(1); /* 0000 0001 */
res = frob(0x40); /* 0100 0000 */
res = frob(0xAC); /* 1010 1100 */
res = frob(0x35); /* 0011 0101 */
return 0;
}
1. The definition of bitmap_t seems to imply that the author thinks
(unsigned int) is at least (or exactly) 32-bits wide.
I think we get UB on platforms where (unsigned int) is only 16-bits
wide? Even if the "reserved" field is never accessed?
2. (I'm not sure about this one.) foo is written to using the map field,
then read from using a different field. This specific instance might be
OK, because uc's type is unsigned char?
3. The code seems to assume field "aa" maps to the least-significant bit
of the parameter (bit 0), "bb" maps to bit 1, etc.
Consider frob(1);
foo.map.aa <- 1
all other fields <- 0
reserved is left undefined
The assumption seems to be:
For any 0 <= u <= 255, frob(u) == u
Moreover, "uc" will typically be only 8-bits wide, while "map" is
32-bits wide. Is there any guarantee whether which bits of "uc" and
"map" overlap? (May depend on endianness?)
Did I miss any more assumptions?
Regards.
I'm dealing with a library whose author seems to have relied implicitly
on several non-portable features. I'm trying to expose every assumption.
(In the context of C89).
typedef struct {
unsigned int aa : 1;
unsigned int bb : 1;
unsigned int cc : 1;
unsigned int dd : 1;
unsigned int ee : 1;
unsigned int ff : 1;
unsigned int gg : 1;
unsigned int hh : 1;
unsigned int reserved : 24;
} bitmap_t;
typedef union {
unsigned char uc;
bitmap_t map;
} foo_t;
unsigned frob(unsigned u)
{
foo_t foo;
foo.map.aa = (u >> 0) & 1;
foo.map.bb = (u >> 1) & 1;
foo.map.cc = (u >> 2) & 1;
foo.map.dd = (u >> 3) & 1;
foo.map.ee = (u >> 4) & 1;
foo.map.ff = (u >> 5) & 1;
foo.map.gg = (u >> 6) & 1;
foo.map.hh = (u >> 7) & 1;
/* garbage in foo.map.reserved */
return foo.uc;
}
int main(void)
{
unsigned res;
/* my tests */
res = frob(1); /* 0000 0001 */
res = frob(0x40); /* 0100 0000 */
res = frob(0xAC); /* 1010 1100 */
res = frob(0x35); /* 0011 0101 */
return 0;
}
1. The definition of bitmap_t seems to imply that the author thinks
(unsigned int) is at least (or exactly) 32-bits wide.
I think we get UB on platforms where (unsigned int) is only 16-bits
wide? Even if the "reserved" field is never accessed?
2. (I'm not sure about this one.) foo is written to using the map field,
then read from using a different field. This specific instance might be
OK, because uc's type is unsigned char?
3. The code seems to assume field "aa" maps to the least-significant bit
of the parameter (bit 0), "bb" maps to bit 1, etc.
Consider frob(1);
foo.map.aa <- 1
all other fields <- 0
reserved is left undefined
The assumption seems to be:
For any 0 <= u <= 255, frob(u) == u
Moreover, "uc" will typically be only 8-bits wide, while "map" is
32-bits wide. Is there any guarantee whether which bits of "uc" and
"map" overlap? (May depend on endianness?)
Did I miss any more assumptions?
Regards.