Browse Source

Fix map_newlink abort when interface list changes during getifaddrs

map_newlink() may abort when interface list changed between netlink
request for getting interfaces and getting addresses. This commit is
ported from the same change from glibc commit.

Signed-off-by: Vincent Hou <vincent.houyi@gmail.com>
Vincent Hou 4 years ago
parent
commit
6b21a5a5bd
1 changed files with 42 additions and 11 deletions
  1. 42 11
      libc/inet/ifaddrs.c

+ 42 - 11
libc/inet/ifaddrs.c

@@ -339,17 +339,19 @@ map_newlink (int idx, struct ifaddrs_storage *ifas, int *map, int max)
       else if (map[i] == idx)
       else if (map[i] == idx)
 	return i;
 	return i;
     }
     }
-  /* This should never be reached. If this will be reached, we have
-     a very big problem.  */
-  abort ();
+
+  /* This means interfaces changed inbetween the reading of the
+     RTM_GETLINK and RTM_GETADDR information.  We have to repeat
+     everything.  */
+  return -1;
 }
 }
 
 
 
 
 /* Create a linked list of `struct ifaddrs' structures, one for each
 /* Create a linked list of `struct ifaddrs' structures, one for each
    network interface on the host machine.  If successful, store the
    network interface on the host machine.  If successful, store the
    list in *IFAP and return 0.  On errors, return -1 and set `errno'.  */
    list in *IFAP and return 0.  On errors, return -1 and set `errno'.  */
-int
-getifaddrs (struct ifaddrs **ifap)
+static int
+getifaddrs_internal (struct ifaddrs **ifap)
 {
 {
   struct netlink_handle nh = { 0, 0, 0, NULL, NULL };
   struct netlink_handle nh = { 0, 0, 0, NULL, NULL };
   struct netlink_res *nlp;
   struct netlink_res *nlp;
@@ -496,6 +498,13 @@ getifaddrs (struct ifaddrs **ifap)
 		 kernel.  */
 		 kernel.  */
 	      ifa_index = map_newlink (ifim->ifi_index - 1, ifas,
 	      ifa_index = map_newlink (ifim->ifi_index - 1, ifas,
 				       map_newlink_data, newlink);
 				       map_newlink_data, newlink);
+	      if (__builtin_expect (ifa_index == -1, 0))
+		{
+		try_again:
+		  result = -EAGAIN;
+		  free (ifas);
+		  goto exit_free;
+		}
 	      ifas[ifa_index].ifa.ifa_flags = ifim->ifi_flags;
 	      ifas[ifa_index].ifa.ifa_flags = ifim->ifi_flags;
 
 
 	      while (RTA_OK (rta, rtasize))
 	      while (RTA_OK (rta, rtasize))
@@ -580,9 +589,11 @@ getifaddrs (struct ifaddrs **ifap)
 		 that we have holes in the interface part of the list,
 		 that we have holes in the interface part of the list,
 		 but we always have already the interface for this address.  */
 		 but we always have already the interface for this address.  */
 	      ifa_index = newlink + newaddr_idx;
 	      ifa_index = newlink + newaddr_idx;
-	      ifas[ifa_index].ifa.ifa_flags
-		= ifas[map_newlink (ifam->ifa_index - 1, ifas,
-				    map_newlink_data, newlink)].ifa.ifa_flags;
+	      int idx = map_newlink (ifam->ifa_index - 1, ifas,
+				     map_newlink_data, newlink);
+	      if (__builtin_expect (idx == -1, 0))
+		goto try_again;
+	      ifas[ifa_index].ifa.ifa_flags = ifas[idx].ifa.ifa_flags;
 	      if (ifa_index > 0)
 	      if (ifa_index > 0)
 		ifas[ifa_index - 1].ifa.ifa_next = &ifas[ifa_index].ifa;
 		ifas[ifa_index - 1].ifa.ifa_next = &ifas[ifa_index].ifa;
 	      ++newaddr_idx;
 	      ++newaddr_idx;
@@ -768,9 +779,13 @@ getifaddrs (struct ifaddrs **ifap)
 	      /* If we didn't get the interface name with the
 	      /* If we didn't get the interface name with the
 		 address, use the name from the interface entry.  */
 		 address, use the name from the interface entry.  */
 	      if (ifas[ifa_index].ifa.ifa_name == NULL)
 	      if (ifas[ifa_index].ifa.ifa_name == NULL)
-		ifas[ifa_index].ifa.ifa_name
-		  = ifas[map_newlink (ifam->ifa_index - 1, ifas,
-				      map_newlink_data, newlink)].ifa.ifa_name;
+		{
+		  int idx = map_newlink (ifam->ifa_index - 1, ifas,
+					 map_newlink_data, newlink);
+		  if (__builtin_expect (idx == -1, 0))
+		    goto try_again;
+		  ifas[ifa_index].ifa.ifa_name = ifas[idx].ifa.ifa_name;
+		}
 
 
 	      /* Calculate the netmask.  */
 	      /* Calculate the netmask.  */
 	      if (ifas[ifa_index].ifa.ifa_addr
 	      if (ifas[ifa_index].ifa.ifa_addr
@@ -850,6 +865,22 @@ getifaddrs (struct ifaddrs **ifap)
 
 
   return result;
   return result;
 }
 }
+
+
+/* Create a linked list of `struct ifaddrs' structures, one for each
+   network interface on the host machine.  If successful, store the
+   list in *IFAP and return 0.  On errors, return -1 and set `errno'.  */
+int
+getifaddrs (struct ifaddrs **ifap)
+{
+  int res;
+
+  do
+    res = getifaddrs_internal (ifap);
+  while (res == -EAGAIN);
+
+  return res;
+}
 libc_hidden_def(getifaddrs)
 libc_hidden_def(getifaddrs)
 
 
 void
 void