diff mbox

Fix strict-aliasing warning in resolv/res_hconf.c

Message ID 1432139240.16668.77.camel@ubuntu-sellcey
State New
Headers show

Commit Message

Steve Ellcey May 20, 2015, 4:27 p.m. UTC
On Wed, 2015-05-20 at 10:42 +0200, Florian Weimer wrote:
> On 05/20/2015 10:23 AM, Andreas Schwab wrote:
> > Florian Weimer <fweimer@redhat.com> writes:
> > 
> >> Looking at struct ifreq, it is rather mysterious to me how this is
> >> supposed to work at all.  I mean, struct sockaddr has just 14 bytes
> >> storage for address information, but IPv6 addresses need 16 bytes, and
> >> socket addresses contain even more information than a raw address.
> > 
> > This ioctl is only defined for IPv4.
> 
> Oh, but then we can add a union member of the appropriate type (just one
> is needed):
> 
> diff --git a/sysdeps/gnu/net/if.h b/sysdeps/gnu/net/if.h
> index 49a048c..39f40de 100644
> --- a/sysdeps/gnu/net/if.h
> +++ b/sysdeps/gnu/net/if.h
> @@ -24,6 +24,7 @@
>  #ifdef __USE_MISC
>  # include <sys/types.h>
>  # include <sys/socket.h>
> +# include <netinet/in.h>
>  #endif
> 
> 
> @@ -139,6 +140,7 @@ struct ifreq
>  	struct sockaddr ifru_broadaddr;
>  	struct sockaddr ifru_netmask;
>  	struct sockaddr ifru_hwaddr;
> +	struct sockaddr_in ifru_addr_in;
>  	short int ifru_flags;
>  	int ifru_ivalue;
>  	int ifru_mtu;
> 
> 
> This doesn't change ABI.  And then the code in resolv/res_hconf.c could
> use that new member, without any casts.

I don't know if this change is going to be considered acceptable or not
but here is a complete patch with the new union member, a macro
definition to access it (in order to match the other union members) and
the needed change to resolv/res_hconf.c.

Steve Ellcey
sellcey@imgtec.com


2015-05-20  Steve Ellcey  <sellcey@imgtec.com>
	    Florian Weimer  <fweimer@redhat.com>

	* resolv/res_hconf.c (_res_hconf_reorder_addrs): Use new ifr_addr_in
	name to access address.
	* sysdeps/gnu/net/if.h (struct ifreq): Add new ifru_addr_in union
	member.
	(ifr_addr_in): New macro.

Comments

Florian Weimer May 22, 2015, 11:34 a.m. UTC | #1
On 05/20/2015 06:27 PM, Steve Ellcey wrote:

> I don't know if this change is going to be considered acceptable or not
> but here is a complete patch with the new union member, a macro
> definition to access it (in order to match the other union members) and
> the needed change to resolv/res_hconf.c.

It would be more conservative to drop the #define (due to the lack of
scope for preprocessor macros).  Maybe also add a comment to header
saying that application code should use the ifru_addr_in member, not the
other struct sockaddr members due to C aliasing issues.  Application
will run into the same issue, the existing definition was likely
impossible to use correctly.  This is the reason why I suggest not to
add a __ prefix to the ifru_addr_in member.

But from a API risk perspective, adding the member is fine—I think,
others might disagree.  There is no ABI risk because of the existing
padding in struct sockaddr.
Steve Ellcey May 26, 2015, 6:47 p.m. UTC | #2
On Fri, 2015-05-22 at 13:34 +0200, Florian Weimer wrote:
> On 05/20/2015 06:27 PM, Steve Ellcey wrote:
> 
> > I don't know if this change is going to be considered acceptable or not
> > but here is a complete patch with the new union member, a macro
> > definition to access it (in order to match the other union members) and
> > the needed change to resolv/res_hconf.c.
> 
> It would be more conservative to drop the #define (due to the lack of
> scope for preprocessor macros).  Maybe also add a comment to header
> saying that application code should use the ifru_addr_in member, not the
> other struct sockaddr members due to C aliasing issues.  Application
> will run into the same issue, the existing definition was likely
> impossible to use correctly.  This is the reason why I suggest not to
> add a __ prefix to the ifru_addr_in member.
> 
> But from a API risk perspective, adding the member is fine—I think,
> others might disagree.  There is no ABI risk because of the existing
> padding in struct sockaddr.

Skipping the macro may be more conservative but if we think users are
going to need or want to use the ifru_addr member to avoid the strict
aliasing warnings then not having the macro there seems awkward by
making that field different than all the rest.

I would like to get a fix for this checked in, this is the last of the
glibc problems I am having when building with the top-of-tree GCC.

Steve Ellcey
sellcey@imgtec.com
Florian Weimer May 26, 2015, 6:51 p.m. UTC | #3
On 05/26/2015 08:47 PM, Steve Ellcey wrote:
> On Fri, 2015-05-22 at 13:34 +0200, Florian Weimer wrote:
>> On 05/20/2015 06:27 PM, Steve Ellcey wrote:
>>
>>> I don't know if this change is going to be considered acceptable or not
>>> but here is a complete patch with the new union member, a macro
>>> definition to access it (in order to match the other union members) and
>>> the needed change to resolv/res_hconf.c.
>>
>> It would be more conservative to drop the #define (due to the lack of
>> scope for preprocessor macros).  Maybe also add a comment to header
>> saying that application code should use the ifru_addr_in member, not the
>> other struct sockaddr members due to C aliasing issues.  Application
>> will run into the same issue, the existing definition was likely
>> impossible to use correctly.  This is the reason why I suggest not to
>> add a __ prefix to the ifru_addr_in member.
>>
>> But from a API risk perspective, adding the member is fine—I think,
>> others might disagree.  There is no ABI risk because of the existing
>> padding in struct sockaddr.
> 
> Skipping the macro may be more conservative but if we think users are
> going to need or want to use the ifru_addr member to avoid the strict
> aliasing warnings then not having the macro there seems awkward by
> making that field different than all the rest.

Okay, seems reasonable.  But please add a comment about the aliasing issue.

> I would like to get a fix for this checked in, this is the last of the
> glibc problems I am having when building with the top-of-tree GCC.

I think it's okay, but better wait a one or two days to see if anyone
else has objections.
Pedro Alves May 26, 2015, 7:16 p.m. UTC | #4
On 05/22/2015 12:34 PM, Florian Weimer wrote:
> 
> But from a API risk perspective, adding the member is fine—I think,
> others might disagree.  There is no ABI risk because of the existing
> padding in struct sockaddr.

Is the union's alignment before/after the same?

Thanks,
Pedro Alves
Florian Weimer May 26, 2015, 7:23 p.m. UTC | #5
On 05/26/2015 09:16 PM, Pedro Alves wrote:
> On 05/22/2015 12:34 PM, Florian Weimer wrote:
>>
>> But from a API risk perspective, adding the member is fine—I think,
>> others might disagree.  There is no ABI risk because of the existing
>> padding in struct sockaddr.
> 
> Is the union's alignment before/after the same?

Good point.

As far as I can tell, the alignment inside struct ifaddrs does not
change because there is an unsigned int member which forces 4 byte
alignment.  Otherwise the interface would not have worked on
strict-alignment architectures because struct sockaddr lacks an
alignment specification.  Wow.  This is stuff is *broken*.
Roland McGrath May 26, 2015, 9:56 p.m. UTC | #6
As I said before, adding the #include in that public header is not right.
Pedro Alves May 27, 2015, 9:19 a.m. UTC | #7
On 05/20/2015 05:27 PM, Steve Ellcey wrote:

> diff --git a/resolv/res_hconf.c b/resolv/res_hconf.c
> index 73942e8..3b05287 100644
> --- a/resolv/res_hconf.c
> +++ b/resolv/res_hconf.c
> @@ -444,13 +444,13 @@ _res_hconf_reorder_addrs (struct hostent *hp)
>  
>  	      ifaddrs[new_num_ifs].addrtype = AF_INET;
>  	      ifaddrs[new_num_ifs].u.ipv4.addr =
> -		((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
> +		cur_ifr->ifr_addr_in.sin_addr.s_addr;

Without adding a new union member, isn't the simplest to just take a
copy step?  The interface already clearly assumes that a sockaddr_in
fits in a sockaddr.  Something like:

              union
                {
                  struct sockaddr sa;
                  struct sockaddr_in sin;
                } ss;

-  	      ifaddrs[new_num_ifs].u.ipv4.addr =
-		((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
+             ss.sa = cur_ifr->ifr_addr;
+	      ifaddrs[new_num_ifs].u.ipv4.addr = ss.sin.sin_addr.s_addr;

etc.  Maybe the compiler even elides the copying.  (And if it doesn't,
would it matter here?)

Thanks,
Pedro Alves
Florian Weimer May 27, 2015, 10:50 a.m. UTC | #8
On 05/27/2015 11:19 AM, Pedro Alves wrote:
> On 05/20/2015 05:27 PM, Steve Ellcey wrote:
> 
>> diff --git a/resolv/res_hconf.c b/resolv/res_hconf.c
>> index 73942e8..3b05287 100644
>> --- a/resolv/res_hconf.c
>> +++ b/resolv/res_hconf.c
>> @@ -444,13 +444,13 @@ _res_hconf_reorder_addrs (struct hostent *hp)
>>  
>>  	      ifaddrs[new_num_ifs].addrtype = AF_INET;
>>  	      ifaddrs[new_num_ifs].u.ipv4.addr =
>> -		((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
>> +		cur_ifr->ifr_addr_in.sin_addr.s_addr;
> 
> Without adding a new union member, isn't the simplest to just take a
> copy step?

I think it's still undefined behavior.

> etc.  Maybe the compiler even elides the copying.

If it does, we are back to square one, I fear.
Florian Weimer May 27, 2015, 11:04 a.m. UTC | #9
On 05/26/2015 11:56 PM, Roland McGrath wrote:
> As I said before, adding the #include in that public header is not right.

What do you recommend to get the definition of struct sockaddr_in
instead?  Or is there no way to fix this because we can't define the
struct by including this header?
Pedro Alves May 27, 2015, 11:09 a.m. UTC | #10
On 05/27/2015 11:50 AM, Florian Weimer wrote:
> On 05/27/2015 11:19 AM, Pedro Alves wrote:
>> On 05/20/2015 05:27 PM, Steve Ellcey wrote:
>>
>>> diff --git a/resolv/res_hconf.c b/resolv/res_hconf.c
>>> index 73942e8..3b05287 100644
>>> --- a/resolv/res_hconf.c
>>> +++ b/resolv/res_hconf.c
>>> @@ -444,13 +444,13 @@ _res_hconf_reorder_addrs (struct hostent *hp)
>>>  
>>>  	      ifaddrs[new_num_ifs].addrtype = AF_INET;
>>>  	      ifaddrs[new_num_ifs].u.ipv4.addr =
>>> -		((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
>>> +		cur_ifr->ifr_addr_in.sin_addr.s_addr;
>>
>> Without adding a new union member, isn't the simplest to just take a
>> copy step?
> 
> I think it's still undefined behavior.

How so?  AFAIK, it's implementation defined, and GCC allows type-punning provided
the memory is accessed through the union type.

 https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Optimize-Options.html#Type-punning

> 
>> etc.  Maybe the compiler even elides the copying.
> 
> If it does, we are back to square one, I fear.
> 


Thanks,
Pedro Alves
Florian Weimer May 27, 2015, 11:16 a.m. UTC | #11
On 05/27/2015 01:09 PM, Pedro Alves wrote:
> On 05/27/2015 11:50 AM, Florian Weimer wrote:
>> On 05/27/2015 11:19 AM, Pedro Alves wrote:
>>> On 05/20/2015 05:27 PM, Steve Ellcey wrote:
>>>
>>>> diff --git a/resolv/res_hconf.c b/resolv/res_hconf.c
>>>> index 73942e8..3b05287 100644
>>>> --- a/resolv/res_hconf.c
>>>> +++ b/resolv/res_hconf.c
>>>> @@ -444,13 +444,13 @@ _res_hconf_reorder_addrs (struct hostent *hp)
>>>>  
>>>>  	      ifaddrs[new_num_ifs].addrtype = AF_INET;
>>>>  	      ifaddrs[new_num_ifs].u.ipv4.addr =
>>>> -		((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
>>>> +		cur_ifr->ifr_addr_in.sin_addr.s_addr;
>>>
>>> Without adding a new union member, isn't the simplest to just take a
>>> copy step?
>>
>> I think it's still undefined behavior.
> 
> How so?  AFAIK, it's implementation defined, and GCC allows type-punning provided
> the memory is accessed through the union type.
> 
>  https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Optimize-Options.html#Type-punning

You mean, copy it to a union member of type struct sockaddr, and reading
from that union as struct sockaddr_in?

(Based on my reading of the standard, memcpy does not change effective
type if one has been assigned.)
Pedro Alves May 27, 2015, 11:39 a.m. UTC | #12
On 05/27/2015 12:16 PM, Florian Weimer wrote:
> On 05/27/2015 01:09 PM, Pedro Alves wrote:
>> On 05/27/2015 11:50 AM, Florian Weimer wrote:
>>> On 05/27/2015 11:19 AM, Pedro Alves wrote:
>>>> On 05/20/2015 05:27 PM, Steve Ellcey wrote:
>>>>
>>>>> diff --git a/resolv/res_hconf.c b/resolv/res_hconf.c
>>>>> index 73942e8..3b05287 100644
>>>>> --- a/resolv/res_hconf.c
>>>>> +++ b/resolv/res_hconf.c
>>>>> @@ -444,13 +444,13 @@ _res_hconf_reorder_addrs (struct hostent *hp)
>>>>>  
>>>>>  	      ifaddrs[new_num_ifs].addrtype = AF_INET;
>>>>>  	      ifaddrs[new_num_ifs].u.ipv4.addr =
>>>>> -		((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
>>>>> +		cur_ifr->ifr_addr_in.sin_addr.s_addr;
>>>>
>>>> Without adding a new union member, isn't the simplest to just take a
>>>> copy step?
>>>
>>> I think it's still undefined behavior.
>>
>> How so?  AFAIK, it's implementation defined, and GCC allows type-punning provided
>> the memory is accessed through the union type.
>>
>>  https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Optimize-Options.html#Type-punning
> 
> You mean, copy it to a union member of type struct sockaddr, and reading
> from that union as struct sockaddr_in?

Yes, just like the patchlet did:

+             ss.sa = cur_ifr->ifr_addr;
+	      ifaddrs[new_num_ifs].u.ipv4.addr = ss.sin.sin_addr.s_addr;

That was implementation defined in C90 (6.3.2.3) "A member of a union object is
accessed using a member of a different type", and blessed in C99/TC3, 6.5.2.3,
footnote 80:

 "If the member used to access the contents of a union object
 is not the same as the member last used to store a value in the object, the
 appropriate part of the object representation of the value is reinterpreted
 as an object representation in the new type as described in 6.2.6 (a
 process sometimes called "type punning")."

And given that sockaddr_in contains explicit padding and the sizes of the
objects match, C99+TC3 6.2.6.1 points 6 and 7 do not apply.

> 
> (Based on my reading of the standard, memcpy does not change effective
> type if one has been assigned.)

Thanks,
Pedro Alves
Florian Weimer May 27, 2015, 12:13 p.m. UTC | #13
On 05/27/2015 11:19 AM, Pedro Alves wrote:

> Without adding a new union member, isn't the simplest to just take a
> copy step?  The interface already clearly assumes that a sockaddr_in
> fits in a sockaddr.  Something like:
> 
>               union
>                 {
>                   struct sockaddr sa;
>                   struct sockaddr_in sin;
>                 } ss;
> 
> -  	      ifaddrs[new_num_ifs].u.ipv4.addr =
> -		((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
> +             ss.sa = cur_ifr->ifr_addr;
> +	      ifaddrs[new_num_ifs].u.ipv4.addr = ss.sin.sin_addr.s_addr;
> 
> etc.

I misread this proposal.  I now think this is a possible fix, and a very
conservative one at that.
Steve Ellcey May 29, 2015, 5:53 p.m. UTC | #14
On Thu, 2015-05-28 at 16:43 -0700, Roland McGrath wrote:
> > 	* resolv/res_hconf.c (_res_hconf_reorder_addrs): Use a union to
> > 	copy data from cur_ifr->ifr_addr.
>                        CUR_IFR->ifr_addr and CUR_IFR->ifr_netmask.
> 
> OK to commit with the log fix.
> 
> 
> Thanks,
> Roland

Is there a reason you want CUR_IFR in upper case?  It is not that way in
the code.

Steve Ellcey
sellcey@imgtec.com
Roland McGrath June 5, 2015, 8:51 p.m. UTC | #15
> Is there a reason you want CUR_IFR in upper case?  It is not that way in
> the code.

The convention for comments and log entries is to put local variable and
parameter names in upper case.
diff mbox

Patch

diff --git a/resolv/res_hconf.c b/resolv/res_hconf.c
index 73942e8..3b05287 100644
--- a/resolv/res_hconf.c
+++ b/resolv/res_hconf.c
@@ -444,13 +444,13 @@  _res_hconf_reorder_addrs (struct hostent *hp)
 
 	      ifaddrs[new_num_ifs].addrtype = AF_INET;
 	      ifaddrs[new_num_ifs].u.ipv4.addr =
-		((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
+		cur_ifr->ifr_addr_in.sin_addr.s_addr;
 
 	      if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
 		continue;
 
 	      ifaddrs[new_num_ifs].u.ipv4.mask =
-		((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
+		cur_ifr->ifr_addr_in.sin_addr.s_addr;
 
 	      /* Now we're committed to this entry.  */
 	      ++new_num_ifs;
diff --git a/sysdeps/gnu/net/if.h b/sysdeps/gnu/net/if.h
index 49a048c..b741d14 100644
--- a/sysdeps/gnu/net/if.h
+++ b/sysdeps/gnu/net/if.h
@@ -24,6 +24,7 @@ 
 #ifdef __USE_MISC
 # include <sys/types.h>
 # include <sys/socket.h>
+# include <netinet/in.h>
 #endif
 
 
@@ -139,6 +140,7 @@  struct ifreq
 	struct sockaddr ifru_broadaddr;
 	struct sockaddr ifru_netmask;
 	struct sockaddr ifru_hwaddr;
+	struct sockaddr_in ifru_addr_in;
 	short int ifru_flags;
 	int ifru_ivalue;
 	int ifru_mtu;
@@ -151,6 +153,7 @@  struct ifreq
 # define ifr_name	ifr_ifrn.ifrn_name	/* interface name 	*/
 # define ifr_hwaddr	ifr_ifru.ifru_hwaddr	/* MAC address 		*/
 # define ifr_addr	ifr_ifru.ifru_addr	/* address		*/
+# define ifr_addr_in	ifr_ifru.ifru_addr_in	/* sockaddr_in address	*/
 # define ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-p lnk	*/
 # define ifr_broadaddr	ifr_ifru.ifru_broadaddr	/* broadcast address	*/
 # define ifr_netmask	ifr_ifru.ifru_netmask	/* interface net mask	*/