Cast from sockaddr * to sockaddr_in * increases the necessary alignment

The compiler issues this warning when I work with some code that looks like

.... for(p = res; p != NULL; p = p->ai_next) { void *addr; std::string ipVer = "IPv0"; if(p->ai_family == AF_INET) { ipVer = "IPv4"; struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; addr = &(ipv4->sin_addr); } else { ipVer = "IPv6"; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; addr = &(ipv6->sin6_addr); } .... } 

where p = res are of type struct addrinfo , and types that generate warnings are sockaddr_in and sockaddr_in6 . The warning comes from the statements:

  • struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
  • struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;

All I want to know is what triggers this warning, and what I can do to fix it if this is not the right way to do something. Can I use any of static_cast / dynamic_cast / reinterpret_cast here?

An exact warning is cast from 'struct sockaddr *' to 'struct sockaddr_in *' increases required alignment from 2 to 4 .

+4
source share
3 answers

TL; DR: This warning does not indicate an error in your code, but you can avoid this by using poper C ++ reinterpret_cast (thanks to @Kurt Stutsman).


Explanation:

Reason for warning :

  • sockaddr consists of an array of unsigned short (usually 16 bits) and char, so its alignment requirement is 2.
  • sockaddr_in contains (among other things) a struct in_addr , which has an alignment requirement of 4, which in turn means sockaddr_in , must also be aligned with a 4 byte boundary.

For this reason, casting arbitrary sockaddr* to sockaddr_in* changes the alignment requirement, and accessing the object using the new pointer will even violate alias rules and lead to undefined behavior.

Why you can ignore it :

In your case, the object p->ai_addr indicates, most likely, it is the sockaddr_in or sockaddr_in6 object in any case (as determined by checking ai_family ), and therefore the operation is safe. However, the compiler does not know this and issues a warning.

In essence, this is the same as using static_cast to point to a pointer to a base class and a pointer to a derived class - it is unsafe in the general case, but if you know the correct dynamic type in appearance, it is well defined.

Decision:
I do not know how to do this (other than a warning), which is unusual for warnings allowed by -Weverything . You can copy the byte by tte object by byte to an object of the corresponding type, but then you will most likely not use addr same way as before, since it will now point to another (for example, local).
-Weverything is not what I would use for my usual builds anyway, because it adds too much noise, but if you want to keep it, @Kurt Stutsman mentioned a good solution in the comments:

clang ++ (g ++ does not give a warning in any case) does not give a warning if you use reinterpret_cast instead of c style styles (which you should not use in any case), although both have (in this case) exactly the same functionality. Perhaps because reinterpret_cast explicitly tells the compiler: "Believe me, I know what I'm doing."


On the side Note: in C ++ code you do not need the struct keywords.

+7
source

Well -Weverything allows quite a few warnings, some of which are known to throw unwanted warnings.

Here your code fires a cast-align warning that explicitly says

distinguishes from ... to ... increases the required alignment from ... to ...

And this is the case here because the alignment for struct addr is only 2, whereas for struct addr_in it is 4.

But you (and the programmer for getaddrinfo ...) know that the p->ai_addr already points to the actual struct addr_in , so the promotion is valid.

You can:

  • let him warn the fire and ignore it - after all, this is just a warning ...
  • disable it -Wno-cast-align after -Weverything

I have to admit that I rarely use -Weverything for this reason and use only -Wall


Alternatively, if you know that you are using only CLang, you can use pragmas to explicitly enable a warning only on those lines

 for(p = res; p != NULL; p = p->ai_next) { void *addr; std::string ipVer = "IPv0"; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-align" if(p->ai_family == AF_INET) { ipVer = "IPv4"; struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; addr = &(ipv4->sin_addr); } else { ipVer = "IPv6"; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; addr = &(ipv6->sin6_addr); } #pragma clang diagnostic pop .... } 
+6
source

Learn more about memcpy version. I thnk this is necessary for ARM, which cannot have invalid data.

I created a structure containing only the first two fields (I only need a port)

 struct sockaddr_in_header { sa_family_t sin_family; /* address family: AF_INET */ in_port_t sin_port; /* port in network byte order */ }; 

Then, to get the port, I used memcpy to push data onto the stack

 struct sockaddr_in_header sinh; unsigned short sin_port; memcpy(&sinh, conn->local_sockaddr, sizeof(struct sockaddr_in_header)); 

And return the port

 sin_port = ntohs(sinh.sin_port); 

This answer is really related to getting a port on Arm

How do I cast a sockaddr pointer to sockaddr_in on Arm

Authorities who think this is the same question as this one, however I do not want to ignore the warnings. Experience has taught me that this is a bad idea.

+1
source

All Articles