Casting pointer to unrelated type
Solution 1:
Assuming that the parameter buffer
doesn't actually point to an object of type struct libusb_control_setup
(probably an unsigned char
array?), then yes this is a strict aliasing violation which is undefined behavior.
The rules governing aliasing are specified in section 6.5p7 of the C standard:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:88)
- a type compatible with the effective type of the object,
- a qualified version of a type compatible with the effective type of the object,
- a type that is the signed or unsigned type corresponding to the effective type of the object,
- a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
- an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
- a character type.
88 ) The intent of this list is to specify those circumstances in which an object may or may not be aliased.
Note that this does not include treating a char
array as if it were some other type, although the reverse is allowed.
The proper way to handle this is to create a local structure of the given type, then use memcpy
to copy the bytes over.
struct libusb_control_setup setup;
memcpy(&setup, buffer, sizeof setup);
Solution 2:
As an addition to @dbush answer
Regarding the provided example, that's how I would have done it, but since this is code from respectable project which is obviously used in a lot of places I'm not sure what to think about it.
Pointer punning is used by many programmers, who think that is safe because it works on their computers. Many programmers also think that using memcpy
will make their code less efficient and more memory greedy.
In most circumstances (when possible of course) the compiler will optimize out the memcpy
call
example:
typedef struct
{
int a;
double b;
int (*callback)(int);
}mt;
int foo(char *ptr, int par)
{
mt m;
memcpy(&m, ptr, sizeof(m));
printf("%f\n", m.b);
m.callback(par);
return m.a;
}
The x86 compiler produces code :
.LC0:
.string "%f\n"
foo:
push rbp
mov ebp, esi
sub rsp, 32
movdqu xmm1, XMMWORD PTR [rdi]
mov rax, QWORD PTR [rdi+16]
mov edi, OFFSET FLAT:.LC0
movaps XMMWORD PTR [rsp], xmm1
movsd xmm0, QWORD PTR [rsp+8]
mov QWORD PTR [rsp+16], rax
mov eax, 1
call printf
mov edi, ebp
call [QWORD PTR [rsp+16]]
mov eax, DWORD PTR [rsp]
add rsp, 32
pop rbp
ret
But ARM Cortex M0 will call the memcpy
as an unaligned version of the pointer will cause hardware exception.
.LC0:
.ascii "%f\012\000"
foo:
push {r4, lr}
movs r4, r1
sub sp, sp, #32
movs r1, r0
movs r2, #24
add r0, sp, #8
bl memcpy
ldr r2, [sp, #16]
ldr r3, [sp, #20]
ldr r0, .L3
str r2, [sp]
str r3, [sp, #4]
bl printf
movs r0, r4
ldr r3, [sp, #24]
blx r3
ldr r0, [sp, #8]
add sp, sp, #32
pop {r4, pc}
.L3:
.word .LC0