Re: Improve getsockname

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Le jeudi 19 janvier 2023, 12:42:52 UTC Alejandro Colomar a écrit :
Hi all,
[adding Eric Blake from redhat and if I remember well member of POSIX comitee]

Eric to be short posix behavior of sockaddr sockaddr_storage is UB and example supplied are UB. See here:
https://pubs.opengroup.org/onlinepubs/009696699/basedefs/sys/socket.h.html

I think we could save the day by defining struct sockadd_storage using anonymous union (thanks to C11 support),
 but it will leave C++. But how can be done...

> Hi Bastien,
> Hi Bastien,
> 
> On 1/17/23 11:22, Bastien Roucariès wrote:
> > Hi,
> > 
> > I have a lot of student that does not use correctly getsockname in case of
> > dual stack.
> 
> Please show some examples of common mistakes, if you can.
oh the basic one is to pass a sockaddr_in instead of sockaddr_in6... Or to pass a sockaddr_in6 and do not think that it could be returned an IPV4 socket or an Unix stream socket.
> 
> > 
> > May be this kind of discussion should be factorized in  sockaddr_storage (the
> > historical note particularly).
> > 
> > i suppose the same should be done for getpeername
> > 
> > I think a safe programming example may be given that accept a socket as stdin
> > and print information on it. Using socat it could be simple to test.
> 
> Please provide some if you can.  However, be careful, since it might easily fall 
> into Undefined Behavior.

Ok see it attached.
> 
> > maybe
> > forcing ENOTSUPP if *addr > sizeof(sockadd_storage)
> > 
> > Regards
> > 
> > Bastien
> 
> I'll add some comments to the patch.
> 
> > From 0afb3ad23f8ea09331f21a377e3ad19c44e4df18 Mon Sep 17 00:00:00 2001
> > From: =?UTF-8?q?Bastien=20Roucari=C3=A8s?= <rouca@xxxxxxxxxx>
> > Date: Tue, 17 Jan 2023 10:07:43 +0000
> > Subject: [PATCH] Document use of struct sockaddr_storage in getsockname
> > 
> > Document:
> > - storage requierement
> > - future compatibility
> > ---
> >  man2/getsockname.2 | 56 ++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 56 insertions(+)
> > 
> > diff --git a/man2/getsockname.2 b/man2/getsockname.2
> > index e6e8980c9..5914c9e12 100644
> > --- a/man2/getsockname.2
> > +++ b/man2/getsockname.2
> > @@ -39,6 +39,17 @@ The returned address is truncated if the buffer provided is too small;
> >  in this case,
> >  .I addrlen
> >  will return a value greater than was supplied to the call.
> > +.PP
> > +For greater portability
> > +.I addr
> > +should point to a structure of type
> > +.I sockaddr_storage.
> 
> This is not correct.  If the object has a type of 'struct sockaddr_storage', 
> then it can only be accessed as a 'struct sockaddr_storage'.  Trying to access 
> it as any other structure type would be Undefined Behavior.  The way to do it 
> conforming to ISO C would be to declare a union which contains all the 'struct 
> sockaddr_*' that are interesting, and access it through the correct union 
> member.  Anything else is on UB land.  The only use of 'struct sockaddr_storage' 
> that I can think of, is for having a more consistent union size.

Ok I see, Eric could we redefine at posix level the struct sockaddr_storage to be something like, it seems that it is allowed by 
/*
 *  Following fields are implementation-defined.
 */

union sockaddr_storage {
  sa_family_t     ss_family;
  struct sockaddr sock;
  struct _sockaddr_storage storage;
  struct sockaddr_in in;
  struct sockaddr_in6 in6;
  struct sockaddr_un un;
};
> 
> > +.I sockaddr_storage
> > +structure is large enough to hold any of the other
> > +.I sockaddr_*
> > +variants and always well aligned. On return, it should be cast to the correct
> > +.I sockaddr_*
> 
> The fact that it is correctly aligned, and a cast will work most of the time, 
> isn't enough for strict aliasing rules.  The compiler is free to assume things, 
> just by the fact that it's a different type.

Ok any idea for writing this kind of stuff 
> 
> Cheers,
> 
> Alex
> 
> > +type, according to the current protocol family, given by the member ss_family.
> >  .SH RETURN VALUE
> >  On success, zero is returned.
> >  On error, \-1 is returned, and
> > @@ -80,10 +91,55 @@ For background on the
> >  .I socklen_t
> >  type, see
> >  .BR accept (2).
> > +.PP
> > +Security and portability wise, use of
> > +.I struct sockaddr_storage
> > +type as
> > +.I addr
> > +and
> > +.I addrlen
> > +set to
> > +.BI "sizeof(struct sockaddr_storage)"
> > +is strongly encouraged. Particularly this usage avoid bugs in dual stack IPv4+IPv6 configuration.
> > +.PP
> > +Historical use of
> > +.I addr
> > +requires one to use a structure specific to the protocol family in use,
> > +such as
> > +.I sockaddr_in
> > +(AF_INET or IPv4),
> > +.I sockaddr_in6
> > +(AF_INET6 or IPv6), or
> > +.I sockaddr_un
> > +(AF_UNIX)
> > +cast to a
> > +.I (struct sockaddr *).
> > +The purpose of the
> > +.I struct sockaddr *
> > +type
> > +is purely to allow casting of  domain-specific  socket  address  types  to  a
> > +"generic" type, so as to avoid compiler warnings about type mismatches in calls to the sockets API.
> > +Nevertheless,
> > +. I struct sockaddr *
> > +is too small to hold newer protocol address (for instance IPv6) and not always well aligned.
> > +.PP
> > +Even if
> > +.I sockaddr_storage
> > +type is large enough at compilation time to hold any of the
> > +.I sockaddr_*
> > +variants known by the library,
> > +this guarantee will not hold for future. Newer protocol may need an increase of
> > +.I sockaddr_storage
> > +size or alignement requirement, and safe program should always runtime check if returned
> > +.I addr
> > +is smaller or equal to compile time
> > +.BI "sizeof(struct sockaddr_storage)"
> > +size.
> >  .SH SEE ALSO
> >  .BR bind (2),
> >  .BR socket (2),
> >  .BR getifaddrs (3),
> > +.BR sockaddr_storage (3type),
> >  .BR ip (7),
> >  .BR socket (7),
> >  .BR unix (7)
> > -- 
> > 2.39.0
> > 
> 
> -- 
> <http://www.alejandro-colomar.es/>
> 

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stddef.h>

union sockaddr_mayalias {
  sa_family_t     ss_family;
  struct sockaddr sock;
  struct sockaddr_storage storage;
  struct sockaddr_in in;
  struct sockaddr_in6 in6;
  struct sockaddr_un un;
};
  
int main() {
  union sockaddr_mayalias sa = {};
  socklen_t addrlen = sizeof(sa);
  if(getsockname(STDIN_FILENO, &sa.sock, &addrlen) < 0) {
    perror("getsockname");
    return 1;
  }
  if(addrlen >= sizeof(sa)) {
    errno = EPROTONOSUPPORT;
    perror("getsockname return a not supported sock_addr");
    return 1;
  }
  
  switch(sa.ss_family) {
  case(AF_UNSPEC):
    printf("AF_UNSPEC socket\n");
    break;
  case(AF_INET):
    {
      char s[INET_ADDRSTRLEN];
      in_port_t port = ntohs(sa.in.sin_port);
      if (inet_ntop(AF_INET, &(sa.in.sin_addr), s, sizeof(s)) == NULL) {
	perror("inet_ntop");
	return 1;
      }
      printf("AF_INET socket %s:%i\n",s,(int)port);
      break;
    }
  case(AF_INET6):
    {
      char s[INET6_ADDRSTRLEN];
      in_port_t port = ntohs(sa.in6.sin6_port);
      if (inet_ntop(AF_INET6, &(sa.in6.sin6_addr), s, sizeof(s)) == NULL) {
	perror("inet_ntop");
	return 1;
      }
      printf("AF_INET6 socket %s:%i\n",s,(int)port);
      break;
    }
  case(AF_UNIX):
    if(addrlen ==  sizeof(sa_family_t)) {
      printf("AF_UNIX socket anonymous\n");
      break;
    }
    /* abstract */
    if(sa.un.sun_path[0]=='\0') {
      printf("AF_UNIX abstract socket 0x");
      for (int i = 0; i < (addrlen - sizeof(sa_family_t)); ++i)
	printf("%x",sa.un.sun_path[i]);
      printf("\n");
      break;
    }
    /* named */
    printf("AF_UNIX named socket ");
    for (int i=0; i < strnlen(sa.un.sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));++i)
      printf("%c",sa.un.sun_path[i]);
    printf("\n");
    break;
  default:
      errno = EPROTONOSUPPORT;
      perror("socket not supported");
      return 1;
}

    
}

Attachment: signature.asc
Description: This is a digitally signed message part.


[Index of Archives]     [Kernel Documentation]     [Netdev]     [Linux Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux