net: socket: Support SOCK_CLOEXEC (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 24 Feb 2017 20:56:22 +0000 (15:56 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 2 Mar 2017 18:01:29 +0000 (13:01 -0500)
If a socket is marked SOCK_CLOEXEC, then the intention is that none of that
socket's FDs (the socket itself, any listen files, ctls, etc) will get
passed on to its child after exec().

Note that accept() does not pass on CLOEXEC, yet.  The NONBLOCK was for the
attempt, which was when we were the parent socket.  The actual ctl that we
get in return is the child socket, which is controlled by accept4() (TBD).

Although I want to get rid of exec(), in the meantime, we should attempt to
support it.

While I was here, I added a helper for opening a Rock's ctlfd (a common
pattern), and supported NONBLOCK for pipes.

Rebuild glibc.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/accept.c
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/bind.c
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/connect.c
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/listen.c
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/pipe2.c
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/plan9_sockets.c
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/shutdown.c
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/socket.c
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/sys/plan9_helpers.h

index 6408877..bf24a22 100644 (file)
@@ -39,7 +39,7 @@ int accept(int fd, __SOCKADDR_ARG addr, socklen_t * __restrict alen)
        char *p;
        const char *net = 0;
        char listen[Ctlsize];
-       int open_flags = O_RDWR;
+       int open_flags;
 
        r = _sock_findrock(fd, 0);
        if (r == 0) {
@@ -65,6 +65,8 @@ int accept(int fd, __SOCKADDR_ARG addr, socklen_t * __restrict alen)
                        if (p == 0)
                                return -1;
                        strcpy(p + 1, "listen");
+                       open_flags = O_RDWR;
+                       /* This is for the listen - maybe don't block on open */
                        open_flags |= (r->sopts & SOCK_NONBLOCK ? O_NONBLOCK : 0);
                        lcfd = open(listen, open_flags);
                        if (lcfd < 0)
@@ -111,7 +113,10 @@ int accept(int fd, __SOCKADDR_ARG addr, socklen_t * __restrict alen)
 
                                /* open new connection */
                                _sock_srvname(file, name);
-                               nfd = open(file, O_RDWR);
+                               open_flags = O_RDWR;
+                               /* This is for the listen - maybe don't block on open */
+                               open_flags |= (r->sopts & SOCK_NONBLOCK ? O_NONBLOCK : 0);
+                               nfd = open(file, open_flags);
                                if (nfd < 0)
                                        continue;
 
index f1bbe0f..a642248 100644 (file)
@@ -50,7 +50,7 @@ int __bind(int fd, __CONST_SOCKADDR_ARG addr, socklen_t alen)
        if (r->domain != PF_INET)
                return 0;
 
-       cfd = open(r->ctl, O_RDWR);
+       cfd = _sock_open_ctlfd(r);
        if (cfd < 0) {
                errno = EBADF;
                return -1;
index 7347af8..205f00b 100644 (file)
@@ -36,6 +36,7 @@ int __connect(int fd, __CONST_SOCKADDR_ARG addr, socklen_t alen)
        struct sockaddr_in *lip, *rip;
        struct sockaddr_un *runix;
        static int vers;
+       int open_flags;
 
        r = _sock_findrock(fd, 0);
        if (r == 0) {
@@ -50,14 +51,14 @@ int __connect(int fd, __CONST_SOCKADDR_ARG addr, socklen_t alen)
 
        switch (r->domain) {
                case PF_INET:
-                       /* UDP sockets are already announced (during bind), so we can't issue
-                        * a connect message.  Either connect or announce, not both.  All sends
-                        * will later do a sendto, based off the contents of r->raddr, so we're
-                        * already done here */
+                       /* UDP sockets are already announced (during bind), so we can't
+                        * issue a connect message.  Either connect or announce, not both.
+                        * All sends will later do a sendto, based off the contents of
+                        * r->raddr, so we're already done here */
                        if (r->stype == SOCK_DGRAM)
                                return 0;
                        /* set up a tcp or udp connection */
-                       cfd = open(r->ctl, O_RDWR);
+                       cfd = _sock_open_ctlfd(r);
                        if (cfd < 0) {
                                return -1;
                        }
@@ -101,7 +102,9 @@ int __connect(int fd, __CONST_SOCKADDR_ARG addr, socklen_t alen)
                        /* tell server the /srv file to open */
                        runix = (struct sockaddr_un *)&r->raddr;
                        _sock_srvname(file, runix->sun_path);
-                       nfd = open(file, O_RDWR);
+                       open_flags = O_RDWR;
+                       open_flags |= (r->sopts & SOCK_CLOEXEC ? O_CLOEXEC : 0);
+                       nfd = open(file, open_flags);
                        if (nfd < 0) {
                                unlink(msg);
                                return -1;
index 40df2b9..ccbd9af 100644 (file)
@@ -44,7 +44,7 @@ int __listen(int fd, int backlog)
 
        switch (r->domain) {
                case PF_INET:
-                       cfd = open(r->ctl, O_RDWR);
+                       cfd = _sock_open_ctlfd(r);
                        if (cfd < 0) {
                                errno = EBADF;
                                return -1;
index 8089260..27a869d 100644 (file)
@@ -20,7 +20,7 @@ int __pipe2(int pipedes[2], int flags)
                __set_errno(EFAULT);
                return -1;
        }
-       dirfd = open("#pipe", O_PATH);
+       dirfd = open("#pipe", O_PATH | (flags & O_CLOEXEC));
        if (dirfd < 0)
                return -1;
        dfd = openat(dirfd, "data", O_RDWR | flags);
index 72ad486..c6c8d6a 100644 (file)
@@ -15,6 +15,7 @@
 #include <errno.h>
 #include <string.h>
 #include <assert.h>
+#include <ctype.h>
 
 /* bsd extensions */
 #include <sys/uio.h>
 
 #include <sys/plan9_helpers.h>
 
+int _sock_open_ctlfd(Rock *r)
+{
+       int open_flags = O_RDWR;
+
+       open_flags |= (r->sopts & SOCK_CLOEXEC ? O_CLOEXEC : 0);
+       return open(r->ctl, open_flags);
+}
+
 void
 _sock_ingetaddr(Rock *r, struct sockaddr_in *ip, socklen_t *alen,
                 const char *a)
@@ -32,12 +41,15 @@ _sock_ingetaddr(Rock *r, struct sockaddr_in *ip, socklen_t *alen,
        int n, fd;
        char *p;
        char name[Ctlsize];
+       int open_flags;
 
        /* get remote address */
        strcpy(name, r->ctl);
        p = strrchr(name, '/');
        strcpy(p + 1, a);
-       fd = open(name, O_RDONLY);
+       open_flags = O_RDONLY;
+       open_flags |= (r->sopts & SOCK_CLOEXEC ? O_CLOEXEC : 0);
+       fd = open(name, open_flags);
        if (fd >= 0) {
                n = read(fd, name, sizeof(name) - 1);
                if (n > 0) {
@@ -209,7 +221,7 @@ int _sock_data(int cfd, const char *net, int domain, int type, int protocol,
        int n, fd;
        Rock *r;
        char name[Ctlsize];
-       int open_flags = O_RDWR;
+       int open_flags;
 
        /* get the data file name */
        n = read(cfd, name, sizeof(name) - 1);
@@ -223,7 +235,9 @@ int _sock_data(int cfd, const char *net, int domain, int type, int protocol,
        snprintf(name, sizeof(name), "/net/%s/%d/data", net, n);
 
        /* open data file */
+       open_flags = O_RDWR;
        open_flags |= (type & SOCK_NONBLOCK ? O_NONBLOCK : 0);
+       open_flags |= (type & SOCK_CLOEXEC ? O_CLOEXEC : 0);
        fd = open(name, open_flags);
        close(cfd);     /* close this no matter what */
        if (fd < 0) {
@@ -309,6 +323,7 @@ static int _rock_get_listen_fd(Rock *r)
        char listen_file[Ctlsize + 3];
        char *p;
        int ret;
+       int open_flags;
 
        if (r->has_listen_fd)
                return r->listen_fd;
@@ -329,7 +344,9 @@ static int _rock_get_listen_fd(Rock *r)
                assert(strcmp(p, "ctl") == 0);
        }
        strlcpy(p, "listen", sizeof(listen_file) - (p - listen_file));
-       ret = open(listen_file, O_PATH);
+       open_flags = O_PATH;
+       open_flags |= (r->sopts & SOCK_CLOEXEC ? O_CLOEXEC : 0);
+       ret = open(listen_file, open_flags);
        /* Probably a bug in the rock code (or the kernel!) if we couldn't walk to
         * our listen. */
        assert(ret >= 0);
@@ -352,7 +369,11 @@ int _sock_lookup_listen_fd(int sock_fd)
 
 /* Given an FD, opens the FD with the name 'sibling' in the same directory.
  * e.g., you have a data, you open a ctl.  Don't use this with cloned FDs (i.e.
- * open clone, get a ctl back) until we fix 9p and fd2path. */
+ * open clone, get a ctl back) until we fix 9p and fd2path.
+ *
+ * Careful, this will always open O_CLOEXEC.  The rationale is that the callers
+ * of this are low-level libraries that quickly close the FD, before any
+ * non-malicious exec. */
 int get_sibling_fd(int fd, const char *sibling)
 {
        char path[MAX_PATH_LEN];
@@ -366,7 +387,7 @@ int get_sibling_fd(int fd, const char *sibling)
        graft++;
        *graft = 0;
        snprintf(graft, sizeof(path) - strlen(path), sibling);
-       return open(path, O_RDWR);
+       return open(path, O_RDWR | O_CLOEXEC);
 }
 
 /* Writes num to FD in ASCII in hex format. */
index 61d343b..c27e348 100644 (file)
@@ -49,7 +49,7 @@ int shutdown(int fd, int how)
                werrstr("Rock lookup failed");
                return -1;
        }
-       ctlfd = open(r->ctl, O_RDWR);
+       ctlfd = _sock_open_ctlfd(r);
        if (!ctlfd)
                return -1;
        ret = write(ctlfd, msg, msg_sz);
index fe05ad9..d8caa81 100644 (file)
@@ -36,16 +36,19 @@ int __socket(int domain, int type, int protocol)
        const char *net;
        char msg[128];
        static struct close_cb _sock_close_cb = {.func = _sock_fd_closed};
+       int open_flags;
 
        run_once(register_close_cb(&_sock_close_cb));
 
        switch (domain) {
                case PF_INET:
+                       open_flags = O_RDWR;
+                       open_flags |= (type & SOCK_CLOEXEC ? O_CLOEXEC : 0);
                        /* get a free network directory */
                        switch (_sock_strip_opts(type)) {
                                case SOCK_DGRAM:
                                        net = "udp";
-                                       cfd = open("/net/udp/clone", O_RDWR);
+                                       cfd = open("/net/udp/clone", open_flags);
                                        /* All BSD UDP sockets are in 'headers' mode, where each
                                         * packet has the remote addr:port, local addr:port and
                                         * other info. */
@@ -64,7 +67,7 @@ int __socket(int domain, int type, int protocol)
                                        break;
                                case SOCK_STREAM:
                                        net = "tcp";
-                                       cfd = open("/net/tcp/clone", O_RDWR);
+                                       cfd = open("/net/tcp/clone", open_flags);
                                        break;
                                default:
                                        errno = EPROTONOSUPPORT;
@@ -75,9 +78,11 @@ int __socket(int domain, int type, int protocol)
                        }
                        return _sock_data(cfd, net, domain, type, protocol, 0);
                case PF_UNIX:
-                       if (pipe(pfd) < 0) {
+                       open_flags = 0;
+                       open_flags |= (type & SOCK_CLOEXEC ? O_CLOEXEC : 0);
+                       open_flags |= (type & SOCK_NONBLOCK ? O_CLOEXEC : 0);
+                       if (pipe2(pfd, open_flags) < 0)
                                return -1;
-                       }
                        r = _sock_newrock(pfd[0]);
                        r->domain = domain;
                        r->stype = _sock_strip_opts(type);
index aa60625..2747bd9 100644 (file)
@@ -75,6 +75,7 @@ extern void _sock_ingetaddr(Rock *, struct sockaddr_in *, socklen_t *,
 extern int _sock_strip_opts(int type);
 extern int _sock_get_opts(int type);
 extern int _sock_lookup_listen_fd(int sock_fd);
+extern int _sock_open_ctlfd(Rock *r);
 
 int get_sibling_fd(int fd, const char *sibling);
 int write_hex_to_fd(int fd, uint64_t num);