Subject: [PATCH] sunrpc: xdr_bytes/xdr_string need to free buffer on error [BZ #21461]
From: fweimer at redhat dot com (Florian Weimer)
Date: Fri, 05 May 2017 15:18:28 +0200

[BZ #21461]

Upstream-Status: Backport

CVE: CVE-2017-8804
Signed-off-by: Rajkumar Veer<rveer@mvista.

Index: git/NEWS
===================================================================
--- git.orig/NEWS
+++ git/NEWS
@@ -20,6 +20,9 @@ using `glibc' in the "product" field.
   [21624] Unsafe alloca allows local attackers to alias stack and heap (CVE-2017-1000366)
 Version 2.24
 
+* The xdr_bytes and xdr_string routines free the internally allocated buffer
+  if deserialization of the buffer contents fails for any reason.
+
 * The minimum Linux kernel version that this version of the GNU C Library
   can be used with is 3.2, except on i[4567]86 and x86_64, where Linux
   kernel version 2.6.32 or later suffices (on architectures that already
Index: git/sunrpc/Makefile
===================================================================
--- git.orig/sunrpc/Makefile
+++ git/sunrpc/Makefile
@@ -96,9 +96,16 @@ rpcgen-objs = rpc_main.o rpc_hout.o rpc_
 extra-objs = $(rpcgen-objs) $(addprefix cross-,$(rpcgen-objs))
 others += rpcgen
 
-tests = tst-xdrmem tst-xdrmem2 test-rpcent
+tests = tst-xdrmem tst-xdrmem2 test-rpcent tst-xdrmem3
 xtests := tst-getmyaddr
 
+tests-special += $(objpfx)mtrace-tst-xdrmem3.out
+generated += mtrace-tst-xdrmem3.out tst-xdrmem3.mtrace
+tst-xdrmem3-ENV = MALLOC_TRACE=$(objpfx)tst-xdrmem3.mtrace
+$(objpfx)mtrace-tst-xdrmem3.out: $(objpfx)tst-xdrmem3.out
+	$(common-objpfx)malloc/mtrace $(objpfx)tst-xdrmem3.mtrace > $@; \
+	$(evaluate-test)
+
 ifeq ($(have-thread-library),yes)
 xtests += thrsvc
 endif
@@ -153,6 +160,7 @@ BUILD_CPPFLAGS += $(sunrpc-CPPFLAGS)
 $(objpfx)tst-getmyaddr: $(common-objpfx)linkobj/libc.so
 $(objpfx)tst-xdrmem: $(common-objpfx)linkobj/libc.so
 $(objpfx)tst-xdrmem2: $(common-objpfx)linkobj/libc.so
+$(objpfx)tst-xdrmem3: $(common-objpfx)linkobj/libc.so
 
 $(objpfx)rpcgen: $(addprefix $(objpfx),$(rpcgen-objs))
 
Index: git/sunrpc/tst-xdrmem3.c
===================================================================
--- /dev/null
+++ git/sunrpc/tst-xdrmem3.c
@@ -0,0 +1,83 @@
+/* Test xdr_bytes, xdr_string behavior on deserialization failure.
+   Copyright (C) 2017 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/>.  */
+
+#include <mcheck.h>
+#include <rpc/rpc.h>
+#include <support/check.h>
+#include <support/support.h>
+
+static int
+do_test (void)
+{
+  mtrace ();
+
+  /* If do_own_buffer, allocate the buffer and pass it to the
+     deserialization routine.  Otherwise the routine is requested to
+     allocate the buffer.  */
+  for (int do_own_buffer = 0; do_own_buffer < 2; ++do_own_buffer)
+    {
+      /* Length 16 MiB, but only 2 bytes of data in the packet.  */
+      unsigned char buf[] = "\x01\x00\x00\x00\xff";
+      XDR xdrs;
+      char *result;
+      unsigned int result_len;
+
+      /* Test xdr_bytes.  */
+      xdrmem_create (&xdrs, (char *) buf, sizeof (buf), XDR_DECODE);
+      result_len = 0;
+      if (do_own_buffer)
+        {
+          char *own_buffer = xmalloc (10);
+          result = own_buffer;
+          TEST_VERIFY (!xdr_bytes (&xdrs, &result, &result_len, 10));
+          TEST_VERIFY (result == own_buffer);
+          free (own_buffer);
+        }
+      else
+        {
+          result = NULL;
+          TEST_VERIFY (!xdr_bytes (&xdrs, &result, &result_len, -1));
+          TEST_VERIFY (result == NULL);
+        }
+      TEST_VERIFY (result_len == 16 * 1024 * 1024);
+      xdr_destroy (&xdrs);
+
+      /* Test xdr_string.  */
+      xdrmem_create (&xdrs, (char *) buf, sizeof (buf), XDR_DECODE);
+      if (do_own_buffer)
+        {
+          char *own_buffer = xmalloc (10);
+          result = own_buffer;
+          TEST_VERIFY (!xdr_string (&xdrs, &result, 10));
+          TEST_VERIFY (result == own_buffer);
+          free (own_buffer);
+        }
+      else
+        {
+          result = NULL;
+          TEST_VERIFY (!xdr_string (&xdrs, &result, -1));
+          TEST_VERIFY (result == NULL);
+        }
+      xdr_destroy (&xdrs);
+    }
+
+  return 0;
+}
+
+#include <support/test-driver.c>
+
Index: git/sunrpc/xdr.c
===================================================================
--- git.orig/sunrpc/xdr.c
+++ git/sunrpc/xdr.c
@@ -620,14 +620,24 @@ xdr_bytes (XDR *xdrs, char **cpp, u_int
 	}
       if (sp == NULL)
 	{
-	  *cpp = sp = (char *) mem_alloc (nodesize);
-	}
-      if (sp == NULL)
-	{
-	  (void) __fxprintf (NULL, "%s: %s", __func__, _("out of memory\n"));
+	  sp = (char *) mem_alloc (nodesize);
+	  if (sp == NULL)
+	    {
+	      (void) __fxprintf (NULL, "%s: %s", __func__,
+				 _("out of memory\n"));
+	      return FALSE;
+	    }
+	}
+      if (!xdr_opaque (xdrs, sp, nodesize))
+ 	{
+	  if (sp != *cpp)
+	    /* *cpp was NULL, so this function allocated a new
+	       buffer.  */
+	    free (sp);
 	  return FALSE;
 	}
-      /* fall into ... */
+      *cpp = sp;
+      return TRUE;
 
     case XDR_ENCODE:
       return xdr_opaque (xdrs, sp, nodesize);
@@ -781,14 +791,27 @@ xdr_string (XDR *xdrs, char **cpp, u_int
     {
     case XDR_DECODE:
       if (sp == NULL)
-	*cpp = sp = (char *) mem_alloc (nodesize);
-      if (sp == NULL)
 	{
-	  (void) __fxprintf (NULL, "%s: %s", __func__, _("out of memory\n"));
-	  return FALSE;
+	  sp = (char *) mem_alloc (nodesize);
+	  if (sp == NULL)
+	    {
+	      (void) __fxprintf (NULL, "%s: %s", __func__,
+				 _("out of memory\n"));
+	      return FALSE;
+	    }
 	}
       sp[size] = 0;
-      /* fall into ... */
+
+      if (!xdr_opaque (xdrs, sp, size))
+	{
+	  if (sp != *cpp)
+	    /* *cpp was NULL, so this function allocated a new
+	       buffer.  */
+	    free (sp);
+	  return FALSE;
+	}
+      *cpp = sp;
+      return TRUE;
 
     case XDR_ENCODE:
       return xdr_opaque (xdrs, sp, size);
Index: git/ChangeLog
===================================================================
--- git.orig/ChangeLog
+++ git/ChangeLog
@@ -1,3 +1,16 @@
+2017-05-05  Florian Weimer  <[email-masked]>
+
+       [BZ #21461]
+       * sunrpc/xdr.c (xdr_bytes): Deallocate allocated buffer on error.
+       (xdr_string): Likewise.
+       * sunrpc/Makefile (tests): Add tst-xdrmem3.
+       (tests-special): Add mtrace-tst-xdrmem3.out.
+       (generated): Add mtrace-tst-xdrmem3.out, tst-xdrmem3.mtrace.
+       (tst-xdrmem3-ENV): Set MALLOC_TRACE.
+       (mtrace-tst-xdrmem3.out): Run mtrace.
+       (tst-xdrmem3): Link against full libc.
+       * sunrpc/tst-xdrmem3.c: New file.
+
 2017-06-14  Florian Weimer  <[email-masked]>
 
 	* sysdeps/i386/i686/multiarch/strcspn-c.c: Add IS_IN (libc) guard.