[PATCH] Core: fixed uninitialized memory access

Maxim Dounin mdounin at mdounin.ru
Thu Sep 29 06:02:16 UTC 2016


Hello!

On Tue, Sep 27, 2016 at 11:23:00PM +0200, Jan Seda wrote:

> Struct sockaddr was allocated without zeroing. As getsockname() does not
> necessarily fill all the bytes, this left potentially uninitialized
> memory, which caused me a weird failure in the on-the-fly upgrade
> mechanism, where the new master failed to pair the config file AF_UNIX
> sockets with the inherited ones. ngx_cmp_sockaddr() would return
> NGX_DECLINED because the inherited one would contain uninitialized
> garbage after the actual name. This in turn would cause nginx to try to
> bind() the unix socket again, and obviously fail, because the listening
> socket was bound already (and inherited).
> 
> Easy to reproduce on debian wheezy, debian jessie and CentOS 7 systems
> (all x86_64) with statically linked openssl 1.1.0. The attached patch
> seems to fix the problem.
> 
> Also harmonized socklen calculation for AF_UNIX sockets to the way
> ngx_parse_unix_domain_url() does it (sizeof(struct sockaddr_un)), so
> ngx_cmp_sockaddr() is now less dangerous.

Thanks for catching this.

Dropping the length returned by getsockname() doesn't look like 
a correct solution though.  Instead, teaching ngx_cmp_sockaddr() to 
compare sockaddrs with not-completely-filled sun_path - that is, 
respecting socklen - should be the right way to go.

Please try the following patch:

diff --git a/src/core/ngx_inet.c b/src/core/ngx_inet.c
--- a/src/core/ngx_inet.c
+++ b/src/core/ngx_inet.c
@@ -1364,6 +1364,7 @@ ngx_cmp_sockaddr(struct sockaddr *sa1, s
     struct sockaddr_in6  *sin61, *sin62;
 #endif
 #if (NGX_HAVE_UNIX_DOMAIN)
+    size_t                len;
     struct sockaddr_un   *saun1, *saun2;
 #endif
 
@@ -1393,15 +1394,45 @@ ngx_cmp_sockaddr(struct sockaddr *sa1, s
 #if (NGX_HAVE_UNIX_DOMAIN)
     case AF_UNIX:
 
-        /* TODO length */
-
         saun1 = (struct sockaddr_un *) sa1;
         saun2 = (struct sockaddr_un *) sa2;
 
-        if (ngx_memcmp(&saun1->sun_path, &saun2->sun_path,
-                       sizeof(saun1->sun_path))
-            != 0)
-        {
+        if (slen1 <= (socklen_t) offsetof(struct sockaddr_un, sun_path)) {
+            return NGX_DECLINED;
+        }
+
+        if (slen2 <= (socklen_t) offsetof(struct sockaddr_un, sun_path)) {
+            return NGX_DECLINED;
+        }
+
+        if (saun1->sun_path[0] == '\0' || saun2->sun_path[0] == '\0') {
+
+            /* an abstract socket address */
+
+            if (ngx_memn2cmp((u_char *) saun1->sun_path,
+                             (u_char *) saun2->sun_path,
+                             slen1 - offsetof(struct sockaddr_un, sun_path),
+                             slen2 - offsetof(struct sockaddr_un, sun_path))
+                != 0)
+            {
+                return NGX_DECLINED;
+            }
+
+            break;
+        }
+
+        if (slen1 < slen2) {
+            len = slen1 - offsetof(struct sockaddr_un, sun_path);
+
+        } else {
+            len = slen2 - offsetof(struct sockaddr_un, sun_path);
+        }
+
+        if (len > sizeof(saun1->sun_path)) {
+            len = sizeof(saun1->sun_path);
+        }
+
+        if (ngx_strncmp(saun1->sun_path, saun2->sun_path, len) != 0) {
             return NGX_DECLINED;
         }
 

-- 
Maxim Dounin
http://nginx.org/



More information about the nginx-devel mailing list