Test for socket calls returning uninitialized memory.

Bug: 28347599
Change-Id: I163db26458a24640013353848ddc42788770a0c8
diff --git a/net/test/csocket.py b/net/test/csocket.py
index 5dc495c..ad7fd9d 100644
--- a/net/test/csocket.py
+++ b/net/test/csocket.py
@@ -32,6 +32,7 @@
 SockaddrIn = cstruct.Struct("sockaddr_in", "=HH4sxxxxxxxx", "family port addr")
 SockaddrIn6 = cstruct.Struct("sockaddr_in6", "=HHI16sI",
                              "family port flowinfo addr scope_id")
+SockaddrStorage = cstruct.Struct("sockaddr_storage", "=H126s", "family data")
 
 # Constants.
 CMSG_ALIGNTO = struct.calcsize("@L")  # The kernel defines this as sizeof(long).
@@ -108,7 +109,7 @@
 
 
 def Bind(s, to):
-  """Python wrapper for connect."""
+  """Python wrapper for bind."""
   ret = libc.bind(s.fileno(), to.CPointer(), len(to))
   MaybeRaiseSocketError(ret)
   return ret
@@ -180,3 +181,32 @@
   MaybeRaiseSocketError(ret)
 
   return ret
+
+
+def Recvfrom(s, size, flags=0):
+  """Python wrapper for recvfrom."""
+  buf = ctypes.create_string_buffer(size)
+  addr = ctypes.create_string_buffer(len(SockaddrStorage))
+  alen = ctypes.c_int(len(addr))
+
+  ret = libc.recvfrom(s.fileno(), buf, len(buf), flags,
+                      addr, ctypes.byref(alen))
+  MaybeRaiseSocketError(ret)
+
+  data = buf[:ret]
+  alen = alen.value
+  addr = addr.raw[:alen]
+
+  # Attempt to convert the address to something we understand.
+  if alen == 0:
+    addr = None
+  elif alen == len(SockaddrIn) and SockaddrIn(addr).family == socket.AF_INET:
+    addr = SockaddrIn(addr)
+  elif alen == len(SockaddrIn6) and SockaddrIn6(addr).family == socket.AF_INET6:
+    addr = SockaddrIn6(addr)
+  elif alen == len(SockaddrStorage):  # Can this ever happen?
+    addr = SockaddrStorage(addr)
+  else:
+    pass  # Unknown or malformed. Return the raw bytes.
+
+  return data, addr
diff --git a/net/test/csocket_test.py b/net/test/csocket_test.py
new file mode 100755
index 0000000..7c47598
--- /dev/null
+++ b/net/test/csocket_test.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unit tests for csocket."""
+
+import socket
+import unittest
+
+import csocket
+
+
+class CsocketTest(unittest.TestCase):
+
+  def CheckRecvfrom(self, family, addr):
+    s = socket.socket(family, socket.SOCK_DGRAM, 0)
+    s.bind((addr, 0))
+
+    addr = s.getsockname()
+    sockaddr = csocket.Sockaddr(addr)
+    s.sendto("foo", addr)
+    data, addr = csocket.Recvfrom(s, 4096, 0)
+    self.assertEqual("foo", data)
+    self.assertEqual(sockaddr, addr)
+
+    s.close()
+
+  def testRecvfrom(self):
+    self.CheckRecvfrom(socket.AF_INET, "127.0.0.1")
+    self.CheckRecvfrom(socket.AF_INET6, "::1")
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/net/test/leak_test.py b/net/test/leak_test.py
new file mode 100755
index 0000000..c748155
--- /dev/null
+++ b/net/test/leak_test.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from errno import *  # pylint: disable=wildcard-import
+from socket import *  # pylint: disable=wildcard-import
+import threading
+import time
+import unittest
+
+import csocket
+import net_test
+
+
+class LeakTest(net_test.NetworkTest):
+
+  def testRecvfromLeak(self):
+    s = socket(AF_INET6, SOCK_DGRAM, 0)
+    s.bind(("::1", 0))
+
+    # Call shutdown on another thread while a recvfrom is in progress.
+    net_test.SetSocketTimeout(s, 200)
+    def ShutdownSocket():
+      time.sleep(0.1)
+      self.assertRaisesErrno(ENOTCONN, s.shutdown, SHUT_RDWR)
+
+    t = threading.Thread(target=ShutdownSocket)
+    t.start()
+
+    # This could have been written with just "s.recvfrom", but because we're
+    # testing for a bug where the kernel returns garbage, it's probably safer
+    # to call the syscall directly.
+    data, addr = csocket.Recvfrom(s, 4096)
+    self.assertEqual("", data)
+    self.assertEqual(None, addr)
+
+
+if __name__ == "__main__":
+  unittest.main()