How to cast sockaddr_storage and avoid breaking strict-aliasing rules

后端 未结 4 948
情书的邮戳
情书的邮戳 2020-12-12 22:57

I\'m using Beej\'s Guide to Networking and came across an aliasing issue. He proposes a function to return either the IPv4 or IPv6 address of a particular struct:



        
相关标签:
4条回答
  • 2020-12-12 23:08

    I recently had a similar alias warning on HPUX system when trying to write code to get the MAC address of the machine

    The &(((struct sockaddr_in *)addr)->sin_addr) complains about strict-aliasing rules

    This is the code in some context

     char ip[INET6_ADDRSTRLEN] = {0};
     strucut sockaddr *addr
    
     ...
     get addr from ioctl(socket,SOCGIFCONF...) call
     ...
     inet_ntop(AF_INET, &(((struct sockaddr_in *)addr)->sin_addr),ip,sizeof ip);
    

    I overcame the aliasing warning by doing the following

    struct sockaddr_in sin;
    memcpy(&sin,addr,sizeof(struct sockaddr));
    inet_ntop(AF_INET, &sin.sin_addr,ip,sizeof ip);
    

    And whilst this is potentially dangerous I added the following lines before it

     static_assert(sizeof(sockaddr)==sizeof(sockaddr_in));
    

    I'm not sure if that is something would be considered bad practice, but it worked and was cross platform to other *Nix flavors and compilers

    0 讨论(0)
  • 2020-12-12 23:12

    The issue has nothing to do with the call to the function. Rather, it's with ((struct sockaddr_in*)sa)->sin_addr. The problem is that sa is a pointer of one type, but you're casting it to a pointer of a different type and then dereferencing it. This breaks a rule called "strict aliasing", which says that variables of different types can never alias. In your case, aliasing to a different type is exactly what you want to do.

    The simple solution is to turn off this optimization, which allows aliasing in this manner. On GCC, the flag is -fno-strict-aliasing.

    The better solution is to use a union, as mentioned by Nikolai.

    void *get_in_addr(struct sockaddr *sa)
    {
        union {
            struct sockaddr     *sa;
            struct sockaddr_in  *sa_in;
            struct sockaddr_in6 *sa_in6;
        } u;
        u.sa = sa;
        if (sa->sa_family == AF_INET)
            return &(u.sa_in->sin_addr);
        else
            return &(u.sa_in6->sin6_addr);
    }
    

    That said, I can't actually get GCC to give me a warning when using your original code, so I'm not sure if this buys you anything.

    0 讨论(0)
  • 2020-12-12 23:29

    I tend to do this to get GCC do the right thing with type-punning, which is explicitly allowed with unions

    I am pretty sure this (mis)use of union will not work (or only by accident) with GCC:

    short type_pun2 (int i, int *pi, short *ps) {
        *pi = i;
        return *ps;
    }
    
    union U {
        int i;
        short s;
    };
    
    short type_pun (int i) {
        U u;
        return type_pun2 (i, &u.i, &u.s);
    }
    

    The correct way to do that is with memcpy, not union.

    0 讨论(0)
  • 2020-12-12 23:35

    I tend to do this to get GCC do the right thing with type-punning, which is explicitly allowed with unions:

    
    /*! Multi-family socket end-point address. */
    typedef union address
    {
        struct sockaddr sa;
        struct sockaddr_in sa_in;
        struct sockaddr_in6 sa_in6;
        struct sockaddr_storage sa_stor;
    }
    address_t;
    

    0 讨论(0)
提交回复
热议问题