[BZ #4181]
authorUlrich Drepper <drepper@redhat.com>
Thu, 15 Mar 2007 20:05:19 +0000 (20:05 +0000)
committerUlrich Drepper <drepper@redhat.com>
Thu, 15 Mar 2007 20:05:19 +0000 (20:05 +0000)
2007-03-15  Jakub Jelinek  <jakub@redhat.com>
[BZ #4181]
* inet/inet6_opt.c (add_padding): Only insert padding if npad > 0.
(inet6_opt_append): Don't check extlen is big enough if extbuf
is NULL.
(inet6_opt_finish): Likewise.
* inet/Makefile (tests): Add test-inet6_opt.
* inet/test-inet6_opt.c: New test.

* sysdeps/unix/sysv/linux/ifaddrs.c (__netlink_request): Never
reallocate the buffer, instead fail for MSG_TRUNC or for EBUSY
NLMSG_ERR.  Instead use a page sized buffer.
* sysdeps/unix/sysv/linux/check_pf.c (make_request): Use page sized
buffer.

ChangeLog
inet/Makefile
inet/inet6_opt.c
inet/test-inet6_opt.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/check_pf.c
sysdeps/unix/sysv/linux/ifaddrs.c

index 1a74ee9..6e5b907 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2007-03-15  Jakub Jelinek  <jakub@redhat.com>
+
+       [BZ #4181]
+       * inet/inet6_opt.c (add_padding): Only insert padding if npad > 0.
+       (inet6_opt_append): Don't check extlen is big enough if extbuf
+       is NULL.
+       (inet6_opt_finish): Likewise.
+       * inet/Makefile (tests): Add test-inet6_opt.
+       * inet/test-inet6_opt.c: New test.
+
+       * sysdeps/unix/sysv/linux/ifaddrs.c (__netlink_request): Never
+       reallocate the buffer, instead fail for MSG_TRUNC or for EBUSY
+       NLMSG_ERR.  Instead use a page sized buffer.
+       * sysdeps/unix/sysv/linux/check_pf.c (make_request): Use page sized
+       buffer.
+
 2007-03-14  Richard Henderson  <rth@redhat.com>
 
        * sysdeps/alpha/fpu/s_llround.c: New file.
index ad90b06..5823b69 100644 (file)
@@ -52,7 +52,7 @@ routines := htonl htons               \
 aux := check_pf ifreq
 
 tests := htontest test_ifindex tst-ntoa tst-ether_aton tst-network \
-        tst-gethnm test-ifaddrs bug-if1
+        tst-gethnm test-ifaddrs bug-if1 test-inet6_opt
 
 include ../Rules
 
index bddb851..17d3fee 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2006 Free Software Foundation, Inc.
+/* Copyright (C) 2006, 2007 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2006.
 
@@ -51,7 +51,7 @@ add_padding (uint8_t *extbuf, int offset, int npad)
 {
   if (npad == 1)
     extbuf[offset] = IP6OPT_PAD1;
-  else
+  else if (npad > 0)
     {
       struct ip6_opt *pad_opt = (struct ip6_opt *) (extbuf + offset);
 
@@ -102,21 +102,17 @@ inet6_opt_append (void *extbuf, socklen_t extlen, int offset, uint8_t type,
   int data_offset = offset + sizeof (struct ip6_opt);
   int npad = (align - data_offset % align) & (align - 1);
 
-  /* Now we can check whether the buffer is large enough.  */
-  if (data_offset + npad + len > extlen)
-    return -1;
-
-  if (npad != 0)
+  if (extbuf != NULL)
     {
-      if (extbuf != NULL)
-       add_padding (extbuf, offset, npad);
+      /* Now we can check whether the buffer is large enough.  */
+      if (data_offset + npad + len > extlen)
+       return -1;
+
+      add_padding (extbuf, offset, npad);
 
       offset += npad;
-    }
 
-  /* Now prepare the option itself.  */
-  if (extbuf != NULL)
-    {
+      /* Now prepare the option itself.  */
       struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset);
 
       opt->ip6o_type = type;
@@ -124,6 +120,8 @@ inet6_opt_append (void *extbuf, socklen_t extlen, int offset, uint8_t type,
 
       *databufp = opt + 1;
     }
+  else
+    offset += npad;
 
   return offset + sizeof (struct ip6_opt) + len;
 }
@@ -145,12 +143,14 @@ inet6_opt_finish (void *extbuf, socklen_t extlen, int offset)
   /* Required padding at the end.  */
   int npad = (8 - (offset & 7)) & 7;
 
-  /* Make sure the buffer is large enough.  */
-  if (offset + npad > extlen)
-    return -1;
-
   if (extbuf != NULL)
-    add_padding (extbuf, offset, npad);
+    {
+      /* Make sure the buffer is large enough.  */
+      if (offset + npad > extlen)
+       return -1;
+
+      add_padding (extbuf, offset, npad);
+    }
 
   return offset + npad;
 }
diff --git a/inet/test-inet6_opt.c b/inet/test-inet6_opt.c
new file mode 100644 (file)
index 0000000..4db9b59
--- /dev/null
@@ -0,0 +1,207 @@
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define OPT_X  42
+#define OPT_Y  43
+#define OPT_Z  44
+
+static void *
+encode_inet6_opt (socklen_t *elp)
+{
+  void *eb = NULL;
+  socklen_t el;
+  int cl;
+  void *db;
+  int offset;
+  uint8_t val1;
+  uint16_t val2;
+  uint32_t val4;
+  uint64_t val8;
+
+  *elp = 0;
+#define CHECK() \
+  if (cl == -1)                                                \
+    {                                                  \
+      printf ("cl == -1 on line %d\n", __LINE__);      \
+      free (eb);                                       \
+      return NULL;                                     \
+    }
+
+  /* Estimate the length */
+  cl = inet6_opt_init (NULL, 0);
+  CHECK ();
+  cl = inet6_opt_append (NULL, 0, cl, OPT_X, 12, 8, NULL);
+  CHECK ();
+  cl = inet6_opt_append (NULL, 0, cl, OPT_Y, 7, 4, NULL);
+  CHECK ();
+  cl = inet6_opt_append (NULL, 0, cl, OPT_Z, 7, 1, NULL);
+  CHECK ();
+  cl = inet6_opt_finish (NULL, 0, cl);
+  CHECK ();
+  el = cl;
+
+  eb = malloc (el + 8);
+  if (eb == NULL)
+    {
+      puts ("malloc failed");
+      return NULL;
+    }
+  /* Canary.  */
+  memcpy (eb + el, "deadbeef", 8);
+
+  cl = inet6_opt_init (eb, el);
+  CHECK ();
+
+  cl = inet6_opt_append (eb, el, cl, OPT_X, 12, 8, &db);
+  CHECK ();
+  val4 = 0x12345678;
+  offset = inet6_opt_set_val (db, 0, &val4, sizeof  (val4));
+  val8 = 0x0102030405060708LL;
+  inet6_opt_set_val (db, offset, &val8, sizeof  (val8));
+
+  cl = inet6_opt_append (eb, el, cl, OPT_Y, 7, 4, &db);
+  CHECK ();
+  val1 = 0x01;
+  offset = inet6_opt_set_val (db, 0, &val1, sizeof  (val1));
+  val2 = 0x1331;
+  offset = inet6_opt_set_val (db, offset, &val2, sizeof  (val2));
+  val4 = 0x01020304;
+  inet6_opt_set_val (db, offset, &val4, sizeof  (val4));
+
+  cl = inet6_opt_append (eb, el, cl, OPT_Z, 7, 1, &db);
+  CHECK ();
+  inet6_opt_set_val (db, 0, (void *) "abcdefg", 7);
+
+  cl = inet6_opt_finish (eb, el, cl);
+  CHECK ();
+
+  if (memcmp (eb + el, "deadbeef", 8) != 0)
+    {
+      puts ("Canary corrupted");
+      free (eb);
+      return NULL;
+    }
+  *elp = el;
+  return eb;
+}
+
+int
+decode_inet6_opt (void *eb, socklen_t el)
+{
+  int ret = 0;
+  int seq = 0;
+  int cl = 0;
+  int offset;
+  uint8_t type;
+  socklen_t len;
+  uint8_t val1;
+  uint16_t val2;
+  uint32_t val4;
+  uint64_t val8;
+  void *db;
+  char buf[8];
+
+  while ((cl = inet6_opt_next (eb, el, cl, &type, &len, &db)) != -1)
+    switch (type)
+      {
+      case OPT_X:
+       if (seq++ != 0)
+         {
+           puts ("OPT_X is not first");
+           ret = 1;
+         }
+       if (len != 12)
+         {
+           printf ("OPT_X's length %d != 12\n", len);
+           ret = 1;
+         }
+       offset = inet6_opt_get_val (db, 0, &val4, sizeof (val4));
+       if (val4 != 0x12345678)
+         {
+           printf ("OPT_X's val4 %x != 0x12345678\n", val4);
+           ret = 1;
+         }
+       offset = inet6_opt_get_val (db, offset, &val8, sizeof (val8));
+       if (offset != len || val8 != 0x0102030405060708LL)
+         {
+           printf ("OPT_X's val8 %llx != 0x0102030405060708\n",
+                   (long long) val8);
+           ret = 1;
+         }
+       break;
+      case OPT_Y:
+       if (seq++ != 1)
+         {
+           puts ("OPT_Y is not second");
+           ret = 1;
+         }
+       if (len != 7)
+         {
+           printf ("OPT_Y's length %d != 7\n", len);
+           ret = 1;
+         }
+       offset = inet6_opt_get_val (db, 0, &val1, sizeof (val1));
+       if (val1 != 0x01)
+         {
+           printf ("OPT_Y's val1 %x != 0x01\n", val1);
+           ret = 1;
+         }
+       offset = inet6_opt_get_val (db, offset, &val2, sizeof (val2));
+       if (val2 != 0x1331)
+         {
+           printf ("OPT_Y's val2 %x != 0x1331\n", val2);
+           ret = 1;
+         }
+       offset = inet6_opt_get_val (db, offset, &val4, sizeof (val4));
+       if (offset != len || val4 != 0x01020304)
+         {
+           printf ("OPT_Y's val4 %x != 0x01020304\n", val4);
+           ret = 1;
+         }
+       break;
+      case OPT_Z:
+       if (seq++ != 2)
+         {
+           puts ("OPT_Z is not third");
+           ret = 1;
+         }
+       if (len != 7)
+         {
+           printf ("OPT_Z's length %d != 7\n", len);
+           ret = 1;
+         }
+       offset = inet6_opt_get_val (db, 0, buf, 7);
+       if (offset != len || memcmp (buf, "abcdefg", 7) != 0)
+         {
+           buf[7] = '\0';
+           printf ("OPT_Z's buf \"%s\" != \"abcdefg\"\n", buf);
+           ret = 1;
+         }
+       break;
+      default:
+       printf ("Unknown option %d\n", type);
+       ret = 1;
+       break;
+      }
+  if (seq != 3)
+    {
+      puts ("Didn't see all of OPT_X, OPT_Y and OPT_Z");
+      ret = 1;
+    }
+  return ret;
+}
+
+int
+main (void)
+{
+  void *eb;
+  socklen_t el;
+  eb = encode_inet6_opt (&el);
+  if (eb == NULL)
+    return 1;
+  if (decode_inet6_opt (eb, el))
+    return 1;
+  return 0;
+}
index 13ccd7a..caf3155 100644 (file)
@@ -1,5 +1,5 @@
 /* Determine protocol families for which interfaces exist.  Linux version.
-   Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2006, 2007 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
@@ -71,17 +71,38 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
   memset (&nladdr, '\0', sizeof (nladdr));
   nladdr.nl_family = AF_NETLINK;
 
+#ifdef PAGE_SIZE
+  /* Help the compiler optimize out the malloc call if PAGE_SIZE
+     is constant and smaller or equal to PTHREAD_STACK_MIN/4.  */
+  const size_t buf_size = PAGE_SIZE;
+#else
+  const size_t buf_size = __getpagesize ();
+#endif
+  bool use_malloc = false;
+  char *buf;
+
+  if (__libc_use_alloca (buf_size))
+    buf = alloca (buf_size);
+  else
+    {
+      buf = malloc (buf_size);
+      if (buf != NULL)
+       use_malloc = true;
+      else
+       goto out_fail;
+    }
+
+  struct iovec iov = { buf, buf_size };
+
   if (TEMP_FAILURE_RETRY (__sendto (fd, (void *) &req, sizeof (req), 0,
                                    (struct sockaddr *) &nladdr,
                                    sizeof (nladdr))) < 0)
-    return -1;
+    goto out_fail;
 
   *seen_ipv4 = false;
   *seen_ipv6 = false;
 
   bool done = false;
-  char buf[4096];
-  struct iovec iov = { buf, sizeof (buf) };
   struct in6ailist
   {
     struct in6addrinfo info;
@@ -101,10 +122,10 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
 
       ssize_t read_len = TEMP_FAILURE_RETRY (__recvmsg (fd, &msg, 0));
       if (read_len < 0)
-       return -1;
+       goto out_fail;
 
       if (msg.msg_flags & MSG_TRUNC)
-       return -1;
+       goto out_fail;
 
       struct nlmsghdr *nlmh;
       for (nlmh = (struct nlmsghdr *) buf;
@@ -186,7 +207,7 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
     {
       *in6ai = malloc (in6ailistlen * sizeof (**in6ai));
       if (*in6ai == NULL)
-       return -1;
+       goto out_fail;
 
       *in6ailen = in6ailistlen;
 
@@ -198,6 +219,13 @@ make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6,
       while (in6ailist != NULL);
     }
 
+  if (use_malloc)
+    free (buf);
+  return 0;
+
+out_fail:
+  if (use_malloc)
+    free (buf);
   return 0;
 }
 
index 6c0f6b3..02e6935 100644 (file)
@@ -122,37 +122,36 @@ int
 __netlink_request (struct netlink_handle *h, int type)
 {
   struct netlink_res *nlm_next;
-  struct netlink_res **new_nlm_list;
-  static volatile size_t buf_size = 4096;
-  char *buf;
   struct sockaddr_nl nladdr;
   struct nlmsghdr *nlmh;
   ssize_t read_len;
   bool done = false;
-  bool use_malloc = false;
 
-  if (__netlink_sendreq (h, type) < 0)
-    return -1;
+#ifdef PAGE_SIZE
+  /* Help the compiler optimize out the malloc call if PAGE_SIZE
+     is constant and smaller or equal to PTHREAD_STACK_MIN/4.  */
+  const size_t buf_size = PAGE_SIZE;
+#else
+  const size_t buf_size = __getpagesize ();
+#endif
+  bool use_malloc = false;
+  char *buf;
 
-  size_t this_buf_size = buf_size;
-  size_t orig_this_buf_size = this_buf_size;
-  if (__libc_use_alloca (this_buf_size))
-    buf = alloca (this_buf_size);
+  if (__libc_use_alloca (buf_size))
+    buf = alloca (buf_size);
   else
     {
-      buf = malloc (this_buf_size);
+      buf = malloc (buf_size);
       if (buf != NULL)
        use_malloc = true;
       else
        goto out_fail;
     }
 
-  struct iovec iov = { buf, this_buf_size };
+  struct iovec iov = { buf, buf_size };
 
-  if (h->nlm_list != NULL)
-    new_nlm_list = &h->end_ptr->next;
-  else
-    new_nlm_list = &h->nlm_list;
+  if (__netlink_sendreq (h, type) < 0)
+    goto out_fail;
 
   while (! done)
     {
@@ -172,48 +171,7 @@ __netlink_request (struct netlink_handle *h, int type)
        continue;
 
       if (__builtin_expect (msg.msg_flags & MSG_TRUNC, 0))
-       {
-         if (this_buf_size >= SIZE_MAX / 2)
-           goto out_fail;
-
-         nlm_next = *new_nlm_list;
-         while (nlm_next != NULL)
-           {
-             struct netlink_res *tmpptr;
-
-             tmpptr = nlm_next->next;
-             free (nlm_next);
-             nlm_next = tmpptr;
-           }
-         *new_nlm_list = NULL;
-
-         if (__libc_use_alloca (2 * this_buf_size))
-           buf = extend_alloca (buf, this_buf_size, 2 * this_buf_size);
-         else
-           {
-             this_buf_size *= 2;
-
-             char *new_buf = realloc (use_malloc ? buf : NULL, this_buf_size);
-             if (new_buf == NULL)
-               goto out_fail;
-             buf = new_buf;
-
-             use_malloc = true;
-           }
-         buf_size = this_buf_size;
-
-         iov.iov_base = buf;
-         iov.iov_len = this_buf_size;
-
-         /* Increase sequence number, so that we can distinguish
-            between old and new request messages.  */
-         h->seq++;
-
-         if (__netlink_sendreq (h, type) < 0)
-           goto out_fail;
-
-         continue;
-       }
+       goto out_fail;
 
       size_t count = 0;
       size_t remaining_len = read_len;
@@ -237,36 +195,6 @@ __netlink_request (struct netlink_handle *h, int type)
              struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA (nlmh);
              if (nlmh->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr)))
                errno = EIO;
-             else if (nlerr->error == -EBUSY
-                      && orig_this_buf_size != this_buf_size)
-               {
-                 /* If EBUSY and MSG_TRUNC was seen, try again with a new
-                    netlink socket.  */
-                 struct netlink_handle hold = *h;
-                 if (__netlink_open (h) < 0)
-                   {
-                     *h = hold;
-                     goto out_fail;
-                   }
-                 __netlink_close (&hold);
-                 orig_this_buf_size = this_buf_size;
-                 nlm_next = *new_nlm_list;
-                 while (nlm_next != NULL)
-                   {
-                     struct netlink_res *tmpptr;
-
-                     tmpptr = nlm_next->next;
-                     free (nlm_next);
-                     nlm_next = tmpptr;
-                   }
-                 *new_nlm_list = NULL;
-                 count = 0;
-                 h->seq++;
-
-                 if (__netlink_sendreq (h, type) < 0)
-                   goto out_fail;
-                 break;
-               }
              else
                errno = -nlerr->error;
              goto out_fail;