about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog23
-rw-r--r--bits/sockaddr.h5
-rw-r--r--bits/socket.h8
-rw-r--r--inet/Makefile4
-rw-r--r--inet/tst-sockaddr.c125
-rw-r--r--sysdeps/mach/hurd/bits/socket.h8
-rw-r--r--sysdeps/unix/bsd/bits/sockaddr.h5
-rw-r--r--sysdeps/unix/sysv/linux/bits/socket.h8
-rw-r--r--sysdeps/unix/sysv/linux/m68k/bits/sockaddr.h42
9 files changed, 213 insertions, 15 deletions
diff --git a/ChangeLog b/ChangeLog
index e2b3d4db0e..4455c6db7c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2016-05-23  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #20111]
+	* bits/sockaddr.h (_SS_SIZE): Define.
+	* bits/socket.h (_SS_SIZE): Remove.
+	(_SS_PADSIZE): Adjust to account for all padding.
+	(struct sockaddr_storage): Update comment.  Avoid implicit
+	padding.
+	* sysdeps/mach/hurd/bits/socket.h (_SS_SIZE): Remove.
+	(_SS_PADSIZE): Adjust to account for all padding.
+	(struct sockaddr_storage): Update comment.  Avoid implicit
+	padding.
+	* sysdeps/unix/bsd/bits/sockaddr.h (_SS_SIZE): Define.
+	* sysdeps/unix/sysv/linux/bits/socket.h (_SS_SIZE): Remove.
+	(_SS_PADSIZE): Adjust to account for all padding.
+	(struct sockaddr_storage): Update comment.  Avoid implicit
+	padding.
+	* sysdeps/unix/sysv/linux/m68k/bits/sockaddr.h: New file.
+	__SS_SIZE is 126 in this version.
+	* inet/tst-sockaddr.c: New file.
+	* inet/Makefile (tests): Add tst-sockaddr.c
+	(tst-sockaddr.c): Compile with non-strict aliasing.
+
 2016-05-23  Joseph Myers  <joseph@codesourcery.com>
 
 	* conform/data/limits.h-data (CHARCLASS_NAME_MAX): Also expect for
diff --git a/bits/sockaddr.h b/bits/sockaddr.h
index e91f837954..0af58c9ebb 100644
--- a/bits/sockaddr.h
+++ b/bits/sockaddr.h
@@ -1,4 +1,4 @@
-/* Definition of `struct sockaddr_*' common members.  Generic/4.2 BSD version.
+/* Definition of struct sockaddr_* common members and sizes, generic version.
    Copyright (C) 1995-2016 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
@@ -36,4 +36,7 @@ typedef unsigned short int sa_family_t;
 
 #define __SOCKADDR_COMMON_SIZE	(sizeof (unsigned short int))
 
+/* Size of struct sockaddr_storage.  */
+#define _SS_SIZE 128
+
 #endif	/* bits/sockaddr.h */
diff --git a/bits/socket.h b/bits/socket.h
index ab9f242cf0..a22fd56031 100644
--- a/bits/socket.h
+++ b/bits/socket.h
@@ -152,20 +152,20 @@ struct sockaddr
 
 
 /* Structure large enough to hold any socket address (with the historical
-   exception of AF_UNIX).  We reserve 128 bytes.  */
+   exception of AF_UNIX).  */
 #if ULONG_MAX > 0xffffffff
 # define __ss_aligntype	__uint64_t
 #else
 # define __ss_aligntype	__uint32_t
 #endif
-#define _SS_SIZE	128
-#define _SS_PADSIZE	(_SS_SIZE - (2 * sizeof (__ss_aligntype)))
+#define _SS_PADSIZE \
+  (_SS_SIZE - __SOCKADDR_COMMON_SIZE - sizeof (__ss_aligntype))
 
 struct sockaddr_storage
   {
     __SOCKADDR_COMMON (ss_);	/* Address family, etc.  */
-    __ss_aligntype __ss_align;	/* Force desired alignment.  */
     char __ss_padding[_SS_PADSIZE];
+    __ss_aligntype __ss_align;	/* Force desired alignment.  */
   };
 
 
diff --git a/inet/Makefile b/inet/Makefile
index 0e7a3c3b45..2207b93977 100644
--- a/inet/Makefile
+++ b/inet/Makefile
@@ -50,7 +50,7 @@ aux := check_pf check_native ifreq
 
 tests := htontest test_ifindex tst-ntoa tst-ether_aton tst-network \
 	 tst-gethnm test-ifaddrs bug-if1 test-inet6_opt tst-ether_line \
-	 tst-getni1 tst-getni2 tst-inet6_rth tst-checks
+	 tst-getni1 tst-getni2 tst-inet6_rth tst-checks tst-sockaddr
 
 include ../Rules
 
@@ -84,6 +84,8 @@ CFLAGS-either_hton.c = -fexceptions
 CFLAGS-getnetgrent.c = -fexceptions
 CFLAGS-getnetgrent_r.c = -fexceptions
 
+CFLAGS-tst-sockaddr.c = -fno-strict-aliasing
+
 endif
 
 ifeq ($(build-static-nss),yes)
diff --git a/inet/tst-sockaddr.c b/inet/tst-sockaddr.c
new file mode 100644
index 0000000000..fe0307b6ff
--- /dev/null
+++ b/inet/tst-sockaddr.c
@@ -0,0 +1,125 @@
+/* Tests for socket address type definitions.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+/* This is a copy of the previous definition of struct
+   sockaddr_storage.  It is not equal to the old value of _SS_SIZE
+   (128) on all architectures.  We must stay compatible with the old
+   definition.  */
+
+#define OLD_REFERENCE_SIZE 128
+#define OLD_PADSIZE (OLD_REFERENCE_SIZE - (2 * sizeof (__ss_aligntype)))
+struct sockaddr_storage_old
+  {
+    __SOCKADDR_COMMON (old_);
+    __ss_aligntype old_align;
+    char old_padding[OLD_PADSIZE];
+  };
+
+static bool errors;
+
+static void
+check (bool ok, const char *message)
+{
+  if (!ok)
+    {
+      printf ("error: failed check: %s\n", message);
+      errors = true;
+    }
+}
+
+static int
+do_test (void)
+{
+  check (OLD_REFERENCE_SIZE >= _SS_SIZE,
+         "old target size is not smaller than actual size");
+  check (sizeof (struct sockaddr_storage_old)
+         == sizeof (struct sockaddr_storage),
+         "old and new sizes match");
+  check (__alignof (struct sockaddr_storage_old)
+         == __alignof (struct sockaddr_storage),
+         "old and new alignment matches");
+  check (offsetof (struct sockaddr_storage_old, old_family)
+         == offsetof (struct sockaddr_storage, ss_family),
+         "old and new family offsets match");
+  check (sizeof (struct sockaddr_storage) == _SS_SIZE,
+         "struct sockaddr_storage size");
+
+  /* Check for lack of holes in the struct definition.   */
+  check (offsetof (struct sockaddr_storage, __ss_padding)
+         == __SOCKADDR_COMMON_SIZE,
+         "implicit padding before explicit padding");
+  check (offsetof (struct sockaddr_storage, __ss_align)
+         == __SOCKADDR_COMMON_SIZE
+           + sizeof (((struct sockaddr_storage) {}).__ss_padding),
+         "implicit padding before explicit padding");
+
+  /* Check for POSIX compatibility requirements between struct
+     sockaddr_storage and struct sockaddr_un.  */
+  check (sizeof (struct sockaddr_storage) >= sizeof (struct sockaddr_un),
+         "sockaddr_storage is at least as large as sockaddr_un");
+  check (__alignof (struct sockaddr_storage)
+         >= __alignof (struct sockaddr_un),
+         "sockaddr_storage is at least as aligned as sockaddr_un");
+  check (offsetof (struct sockaddr_storage, ss_family)
+         == offsetof (struct sockaddr_un, sun_family),
+         "family offsets match");
+
+  /* Check that the compiler preserves bit patterns in aggregate
+     copies.  Based on <https://gcc.gnu.org/PR71120>.  */
+  check (sizeof (struct sockaddr_storage) >= sizeof (struct sockaddr_in),
+         "sockaddr_storage is at least as large as sockaddr_in");
+  {
+    struct sockaddr_storage addr;
+    memset (&addr, 0, sizeof (addr));
+    {
+      struct sockaddr_in *sinp = (struct sockaddr_in *)&addr;
+      sinp->sin_family = AF_INET;
+      sinp->sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+      sinp->sin_port = htons (80);
+    }
+    struct sockaddr_storage copy;
+    copy = addr;
+
+    struct sockaddr_storage *p = malloc (sizeof (*p));
+    if (p == NULL)
+      {
+        printf ("error: malloc: %m\n");
+        return 1;
+      }
+    *p = copy;
+    const struct sockaddr_in *sinp = (const struct sockaddr_in *)p;
+    check (sinp->sin_family == AF_INET, "sin_family");
+    check (sinp->sin_addr.s_addr == htonl (INADDR_LOOPBACK), "sin_addr");
+    check (sinp->sin_port == htons (80), "sin_port");
+    free (p);
+  }
+
+  return errors;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/sysdeps/mach/hurd/bits/socket.h b/sysdeps/mach/hurd/bits/socket.h
index 02c5dac032..257e438ed2 100644
--- a/sysdeps/mach/hurd/bits/socket.h
+++ b/sysdeps/mach/hurd/bits/socket.h
@@ -156,20 +156,20 @@ struct sockaddr
 
 
 /* Structure large enough to hold any socket address (with the historical
-   exception of AF_UNIX).  We reserve 128 bytes.  */
+   exception of AF_UNIX).  */
 #if ULONG_MAX > 0xffffffff
 # define __ss_aligntype	__uint64_t
 #else
 # define __ss_aligntype	__uint32_t
 #endif
-#define _SS_SIZE	128
-#define _SS_PADSIZE	(_SS_SIZE - (2 * sizeof (__ss_aligntype)))
+#define _SS_PADSIZE \
+  (_SS_SIZE - __SOCKADDR_COMMON_SIZE - sizeof (__ss_aligntype))
 
 struct sockaddr_storage
   {
     __SOCKADDR_COMMON (ss_);	/* Address family, etc.  */
-    __ss_aligntype __ss_align;	/* Force desired alignment.  */
     char __ss_padding[_SS_PADSIZE];
+    __ss_aligntype __ss_align;	/* Force desired alignment.  */
   };
 
 
diff --git a/sysdeps/unix/bsd/bits/sockaddr.h b/sysdeps/unix/bsd/bits/sockaddr.h
index aa127689bf..f5900f9d73 100644
--- a/sysdeps/unix/bsd/bits/sockaddr.h
+++ b/sysdeps/unix/bsd/bits/sockaddr.h
@@ -1,4 +1,4 @@
-/* Definition of `struct sockaddr_*' common members.  4.4 BSD version.
+/* Definition of struct sockaddr_* common members and sizes, BSD version.
    Copyright (C) 1995-2016 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
@@ -39,4 +39,7 @@ typedef unsigned char sa_family_t;
 
 #define _HAVE_SA_LEN	1	/* We have the sa_len field.  */
 
+/* Size of struct sockaddr_storage.  */
+#define _SS_SIZE 128
+
 #endif	/* bits/sockaddr.h */
diff --git a/sysdeps/unix/sysv/linux/bits/socket.h b/sysdeps/unix/sysv/linux/bits/socket.h
index a57048c04d..2eb95f7e20 100644
--- a/sysdeps/unix/sysv/linux/bits/socket.h
+++ b/sysdeps/unix/sysv/linux/bits/socket.h
@@ -175,16 +175,16 @@ struct sockaddr
 
 
 /* Structure large enough to hold any socket address (with the historical
-   exception of AF_UNIX).  We reserve 128 bytes.  */
+   exception of AF_UNIX).  */
 #define __ss_aligntype	unsigned long int
-#define _SS_SIZE	128
-#define _SS_PADSIZE	(_SS_SIZE - (2 * sizeof (__ss_aligntype)))
+#define _SS_PADSIZE \
+  (_SS_SIZE - __SOCKADDR_COMMON_SIZE - sizeof (__ss_aligntype))
 
 struct sockaddr_storage
   {
     __SOCKADDR_COMMON (ss_);	/* Address family, etc.  */
-    __ss_aligntype __ss_align;	/* Force desired alignment.  */
     char __ss_padding[_SS_PADSIZE];
+    __ss_aligntype __ss_align;	/* Force desired alignment.  */
   };
 
 
diff --git a/sysdeps/unix/sysv/linux/m68k/bits/sockaddr.h b/sysdeps/unix/sysv/linux/m68k/bits/sockaddr.h
new file mode 100644
index 0000000000..5721f99fd9
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/m68k/bits/sockaddr.h
@@ -0,0 +1,42 @@
+/* Definition of struct sockaddr_* members and sizes, Linux/m68k version.
+   Copyright (C) 1995-2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/*
+ * Never include this file directly; use <sys/socket.h> instead.
+ */
+
+#ifndef _BITS_SOCKADDR_H
+#define _BITS_SOCKADDR_H	1
+
+
+/* POSIX.1g specifies this type name for the `sa_family' member.  */
+typedef unsigned short int sa_family_t;
+
+/* This macro is used to declare the initial common members
+   of the data types used for socket addresses, `struct sockaddr',
+   `struct sockaddr_in', `struct sockaddr_un', etc.  */
+
+#define	__SOCKADDR_COMMON(sa_prefix) \
+  sa_family_t sa_prefix##family
+
+#define __SOCKADDR_COMMON_SIZE	(sizeof (unsigned short int))
+
+/* Size of struct sockaddr_storage.  */
+#define _SS_SIZE 126
+
+#endif	/* bits/sockaddr.h */