Bug #46440 memcmp() for struct sockaddr_storage is invalid
Submitted: 28 Jul 2009 22:58 Modified: 29 Jul 2009 5:56
Reporter: Will Murnane Email Updates:
Status: Duplicate Impact on me:
None 
Category:MySQL Server: Compiling Severity:S2 (Serious)
Version:mysql-cluster-gpl-7.0.6 OS:Solaris (Solaris 10u7)
Assigned to: CPU Architecture:Any

[28 Jul 2009 22:58] Will Murnane
Description:
On line 245 of sql/hostname.cc, memcmp is used to compare a struct sockaddr_storage* (in) to a struct sockaddr* (t_res->ai_addr).  I believe this is invalid, because of the following code I added to debug this problem.
printf("Getaddrinfo result: %0*X\n", t_res->ai_addrlen, t_res->ai_addr);
printf("In:                 %0*X\n", addrLen, in);
printf("Getaddrinfo result: %0*X\n", 4, ((struct sockaddr_in*)t_res->ai_addr)->sin_addr);
printf("In:                 %0*X\n", 4, ((struct sockaddr_in*)in)->sin_addr);
printf("Getaddrinfo result: %p\n", &((struct sockaddr_in*)t_res->ai_addr)->sin_addr);
printf("In:                 %p\n", &((struct sockaddr_in*)in)->sin_addr);

This code prints (for example):
Getaddrinfo result: 0000000008062110
In:                 0000000008047B84
Getaddrinfo result: FC5F5582
In:                 FC5F5582
Getaddrinfo result: 8062114
In:                 8047b88

This shows that because t_res->ai_addr (a struct sockaddr*) has a pointer as part of its structure, using memcmp() to compare the contents of "t_res->ai_addr" and "in" is invalid.

The attached patch *should* fix the problem... but it doesn't.

How to repeat:
Build mysql-cluster-gpl-7.0.6 on Solaris (or install the precompiled binaries).  Add a GRANT like:
GRANT ALL ON *.* to 'user'@'chasca.cs.umbc.edu';
Try to connect from the specified user and host.  Get an error like this:
Access denied for user 'user'@'::ffff:130.85.95.252'

Suggested fix:
--- mysql-cluster-gpl-7.0.6/sql/hostname.cc	Tue May 26 10:34:43 2009
+++ mysql-cluster-gpl-7.0.6-mod//sql/hostname.cc	Tue Jul 28 17:22:14 2009
@@ -137,6 +148,18 @@
 char * ip_to_hostname(struct sockaddr_storage *in, int addrLen, uint *errors)
 {
   char *name= NULL;
+  if (in->ss_family == AF_INET6)
+  {
+    if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6*)in)->sin6_addr))
+    {
+      struct sockaddr_in foo;
+      bzero(&foo, sizeof(struct sockaddr));
+      foo.sin_family = AF_INET;
+      foo.sin_port = ((struct sockaddr_in6*)in)->sin6_port;
+      memcpy(&foo.sin_addr.s_addr, &((struct sockaddr_in6*)in)->sin6_addr.s6_addr[12], 4);
+      in = (struct sockaddr_storage*)&foo;
+    }
+  }
 
   struct addrinfo hints,*res_lst= NULL,*t_res;
   int gxi_error;
@@ -149,7 +172,7 @@
   /* Historical comparison for 127.0.0.1 */
   gxi_error= getnameinfo((struct sockaddr *)in, addrLen,
                          hostname_buff, NI_MAXHOST,
-                         NULL, 0, NI_NUMERICHOST);
+                         NULL, 0, 0);
   if (gxi_error)
   {
     DBUG_PRINT("error",("getnameinfo returned %d", gxi_error));
@@ -242,8 +265,16 @@
   /* Check that 'getaddrinfo' returned the used ip */
   for (t_res= res_lst; t_res; t_res=t_res->ai_next)
   {
-    if (!memcmp(&(t_res->ai_addr), in,
-                sizeof(struct sockaddr_storage) ) )
+    if ((in->ss_family == t_res->ai_addr->sa_family) &&
+        (((in->ss_family == AF_INET) && 
+          (((struct sockaddr_in*)in)->sin_addr.s_addr == 
+          ((struct sockaddr_in*)t_res->ai_addr)->sin_addr.s_addr))
+        ||
+         ((in->ss_family == AF_INET6) &&
+           (!memcmp(
+            (void*)&((struct sockaddr_in6*)in)->sin6_addr,
+            (void*)&((struct sockaddr_in6*)t_res->ai_addr)->sin6_addr,
+            sizeof(struct in6_addr))))))
     {
       add_hostname(in,name);
       freeaddrinfo(res_lst);
[29 Jul 2009 5:56] Sveta Smirnova
Thank you for the report.

This is duplicate of bug #46336. Current 5.1 code does not contain memcmp call in sql/hostname.cc