c sockets aliasing strict-aliasing

Cómo lanzar sockaddr_storage y evitar romper las reglas de alias estrictas



sockets aliasing (5)

... Simplemente no puedo encontrar buenos ejemplos con las mejores prácticas.

Tal vez mira aquí:

http://my-sample-code.googlecode.com/svn/trunk/socket/

Aclamaciones,

lupo

Estoy usando Beej''s Guide to Networking y encontré un problema de aliasing. Él propone una función para devolver la dirección IPv4 o IPv6 de una estructura particular:

1 void *get_in_addr( struct sockaddr *sa ) 2 { 3 if (sa->sa_family == AF_INET) 4 return &(((struct sockaddr_in*)sa)->sin_addr); 5 else 6 return &(((struct sockaddr_in6*)sa)->sin6_addr); 7 }

Esto hace que GCC escupe un error de alias estricto para sa en la línea 3. Según tengo entendido, es porque llamo a esta función así:

struct sockaddr_storage their_addr; ... inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), connection_name, sizeof connection_name);

Supongo que el aliasing tiene que ver con el hecho de que la variable their_addr es del tipo sockaddr_storage y otro puntero de un tipo diferente apunta a la misma memoria.

¿Es la mejor forma de evitar esto sockaddr_storage , sockaddr_in y sockaddr_in6 en una unión? Parece que este debería ser un territorio bien usado en las redes, simplemente no puedo encontrar ningún buen ejemplo con las mejores prácticas.

Además, si alguien puede explicar exactamente dónde se produce el problema del aliasing, lo agradecería mucho.


Tiendo a hacer esto para que GCC haga lo correcto con el tipo de juego de palabras, lo que está explícitamente permitido con los sindicatos

Estoy bastante seguro de que este (mal) uso de la unión no funcionará (o solo por accidente) con 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); }

La forma correcta de hacerlo es con memcpy , no con union .


El problema no tiene nada que ver con la llamada a la función. Más bien, es con ((struct sockaddr_in*)sa)->sin_addr . El problema es que sa es un puntero de un tipo, pero lo estás convirtiendo en un puntero de un tipo diferente y luego desreferenciando. Esto rompe una regla llamada "aliasing estricto", que dice que las variables de diferentes tipos nunca pueden alias. En su caso, aliasing a un tipo diferente es exactamente lo que desea hacer.

La solución simple es desactivar esta optimización, lo que permite aliasing de esta manera. En GCC, la bandera es -fno-strict-aliasing .

La mejor solución es usar una unión, como lo menciona 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); }

Dicho esto, no puedo hacer que GCC me dé una advertencia cuando use el código original, así que no estoy seguro de si esto te compra algo.


Hace poco tuve una advertencia de alias similar en el sistema HPUX al intentar escribir código para obtener la dirección MAC de la máquina

The &(((struct sockaddr_in *)addr)->sin_addr) queja de reglas de alias estrictos

Este es el código en algún contexto

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);

Superé la advertencia de alias haciendo lo siguiente

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

Y aunque esto es potencialmente peligroso, agregué las siguientes líneas antes

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

No estoy seguro si eso es algo que se consideraría una mala práctica, pero funcionó y fue multiplataforma con otros * Nix sabores y compiladores


Tiendo a hacer esto para que GCC haga lo correcto con el type-punning , que está explícitamente permitido con las uniones:

/*! 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;