base: Fix race condition in the socket listen function
authorMitch Hayenga <mitch.hayenga+gem5@gmail.com>
Wed, 29 Jan 2014 00:00:51 +0000 (18:00 -0600)
committerMitch Hayenga <mitch.hayenga+gem5@gmail.com>
Wed, 29 Jan 2014 00:00:51 +0000 (18:00 -0600)
gem5 makes the incorrect assumption that by binding a socket, it
effectively has allocated a port. Linux only allocates ports once you call
listen on the given socket, not when you call bind.  So even if the port was
free when bind was called, another process (gem5 instance) could race in
between the bind & listen calls and steal the port. In the current code, if
the call to bind fails due to the port being in use (EADDRINUSE), gem5 retries
for a different port.  However if listen fails, gem5 just panics. The fix is
testing the return value of listen and re-trying if it was due to EADDRINUSE.

Committed by: Nilay Vaish <nilay@cs.wisc.edu>

src/base/socket.cc

index 0c8903084ab4f2e310834144f8d06d25de19e15f..c39accd7e80f0b28cefa853d49dc4da323209367 100644 (file)
@@ -103,11 +103,14 @@ ListenSocket::listen(int port, bool reuse)
         return false;
     }
 
-    if (::listen(fd, 1) == -1)
-        panic("ListenSocket(listen): listen() failed!");
+    if (::listen(fd, 1) == -1) {
+        if (errno != EADDRINUSE)
+            panic("ListenSocket(listen): listen() failed!");
 
-    listening = true;
+        return false;
+    }
 
+    listening = true;
     anyListening = true;
     return true;
 }