// Socket syscalls. // // The kernel side is in kernel/net/unix.c; this file is only the translation // between a syscall's user-supplied `struct sockaddr` and the path string the // socket layer actually wants, plus the file-descriptor bookkeeping. // // Two families now. AF_UNIX is answered by kernel/net/unix.c, AF_INET by // kernel/net/inet.c on top of lwIP; the socket layer dispatches on the domain // the socket was created with, so this file's only extra job is to read the // right shape of sockaddr out of user memory. AF_INET6 is still refused with // EAFNOSUPPORT rather than EINVAL, because a program that probes for IPv6 and // falls back to IPv4 needs to tell "not here" from "you passed nonsense". #include "types.h" #include "defs.h" #include "param.h" #include "mmu.h" #include "proc.h" #include "fs.h" #include "spinlock.h" #include "sleeplock.h" #include "file.h" #include "posix.h" #include "syscall.h" #include "uaccess.h" #include "errno.h" #include "socket.h" // Pull the sun_path out of a user `struct sockaddr_un`, NUL-terminated. // // The length the caller gives is checked rather than trusted: musl passes // `offsetof(sockaddr_un, sun_path) + strlen(path) + 1`, which is shorter than // the full struct, so reading sizeof(struct sockaddr_un) from user memory // would fault on a path that sits near the end of a page. static int fetch_sun_path(addr_t uaddr, int ulen, char *out) { struct sockaddr_un sa; int plen; if (ulen < (int)sizeof(sa.sun_family) || ulen > (int)sizeof(sa)) return -EINVAL; memset(&sa, 0, sizeof(sa)); if (copy_from_user(&sa, uaddr, (uint64)ulen) < 0) return -EFAULT; if (sa.sun_family == AF_INET) return -EAFNOSUPPORT; if (sa.sun_family != AF_UNIX) return -EAFNOSUPPORT; plen = ulen - (int)sizeof(sa.sun_family); if (plen <= 0) return -EINVAL; if (plen > UNIX_PATH_MAX - 1) plen = UNIX_PATH_MAX - 1; memmove(out, sa.sun_path, (uint)plen); out[plen] = '\0'; // A NUL inside the copied region is fine -- it just ends the path early, // which is what a caller passing the full struct length does. out[UNIX_PATH_MAX - 1] = '\0'; return 0; } // Wrap an established socket in a file and a descriptor. Consumes the // socket's reference on success; on failure the caller still owns it. // // `sflags` carries SOCK_CLOEXEC and SOCK_NONBLOCK, which socket(), socketpair() // and accept4() all take and all used to discard. // // Discarding them is not harmless in either direction. A dropped SOCK_CLOEXEC // leaks the descriptor across every exec the process makes -- so a server that // forks a child holds its listening socket open in it, and the port stays bound // after the server dies. A dropped SOCK_NONBLOCK is worse: the caller believes // it has a non-blocking socket, does not select() first, and blocks forever in // what it thinks is a poll loop. // // Both are one line each, and they were masked off with a comment saying they // were not honoured -- which is exactly the class of quiet lie the rest of this // file has just been cleaned of. static int sock_to_fd(struct socket *s, int sflags) { struct file *f; int fd; if ((f = filealloc()) == 0) return -ENFILE; if ((fd = fdalloc(f)) < 0) { f->ref = 0; return -EMFILE; } f->type = FD_SOCKET; f->sock = s; f->readable = 1; f->writable = 1; f->off = 0; f->nonblock = (sflags & SOCK_NONBLOCK) != 0; if (sflags & SOCK_CLOEXEC) myproc()->ofile_flags[fd] |= FD_CLOEXEC; return fd; } // Resolve a descriptor to the socket behind it. static int argsock(int n, struct socket **ps) { int fd; struct file *f; if (argint(n, &fd) < 0) return -EBADF; if (fd < 0 || fd >= NOFILE || !(f = myproc()->ofile[fd])) return -EBADF; if (f->type != FD_SOCKET) return -ENOTSOCK; *ps = f->sock; return 0; } SYSCALL_DEFINE(socket) { int domain, type, protocol; struct socket *s; int fd; argint(0, &domain); argint(1, &type); argint(2, &protocol); if (domain != AF_UNIX && domain != AF_INET) return -EAFNOSUPPORT; // SOCK_CLOEXEC / SOCK_NONBLOCK ride in the high bits of `type`. Split them // off rather than discarding them: the type check below wants the low byte, // and sock_to_fd wants the flags. int sflags = type & (SOCK_CLOEXEC | SOCK_NONBLOCK); if (type & ~(0xFF | SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; type &= 0xFF; if (domain == AF_UNIX && type != SOCK_STREAM) return -EPROTOTYPE; if (domain == AF_INET && type != SOCK_STREAM && type != SOCK_DGRAM) return -EPROTOTYPE; // Protocol 0 means "the default for this type". The one non-default we // support is a ping socket -- SOCK_DGRAM with IPPROTO_ICMP, Linux's way of // letting an unprivileged process do the one thing raw sockets were used // for. Anything else is still refused rather than quietly given a socket // that speaks a different protocol than the caller asked for. if (protocol != 0) { if (!(domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_ICMP)) return -EPROTONOSUPPORT; } // ENETDOWN rather than ENFILE when the stack itself is not running: the // old blanket ENFILE said "file table overflow" for a machine with no // network, which is a long way from the truth and a longer way from the // fix. if (domain == AF_INET && !net_is_ready()) return -ENETDOWN; if ((s = socknew(domain, type, protocol)) == 0) return -ENFILE; if ((fd = sock_to_fd(s, sflags)) < 0) { sockclose(s); return fd; } return fd; } // Pull an address and port out of a user `struct sockaddr_in`. // // Both come back in network byte order and stay that way: lwIP wants them that // way, so converting here and back again would be two swaps that cancel. static int fetch_sin(addr_t uaddr, int ulen, uint *addr, ushort *port) { struct sockaddr_in sa; if (ulen < (int)sizeof(sa)) return -EINVAL; if (copy_from_user(&sa, uaddr, sizeof(sa)) < 0) return -EFAULT; if (sa.sin_family != AF_INET) return -EAFNOSUPPORT; *addr = sa.sin_addr; *port = sa.sin_port; return 0; } // Network byte order to host, for a port. The kernel is little-endian and the // wire is big-endian; lwIP's own ntohs is not reachable from here without // dragging its headers into the syscall layer. static ushort port_from_net(ushort p) { return (ushort)(((p & 0xFF) << 8) | ((p >> 8) & 0xFF)); } SYSCALL_DEFINE(bind) { struct socket *s; addr_t uaddr; int ulen, r; char path[UNIX_PATH_MAX]; if ((r = argsock(0, &s)) < 0) return r; argaddr(1, &uaddr); argint(2, &ulen); if (sock_domain(s) == AF_INET) { uint ip; ushort port; if ((r = fetch_sin(uaddr, ulen, &ip, &port)) < 0) return r; return sockbind_in(s, ip, port_from_net(port)); } if ((r = fetch_sun_path(uaddr, ulen, path)) < 0) return r; return sockbind(s, path); } SYSCALL_DEFINE(listen) { struct socket *s; int backlog, r; if ((r = argsock(0, &s)) < 0) return r; argint(1, &backlog); return socklisten(s, backlog); } SYSCALL_DEFINE(connect) { struct socket *s; addr_t uaddr; int ulen, r; char path[UNIX_PATH_MAX]; if ((r = argsock(0, &s)) < 0) return r; argaddr(1, &uaddr); argint(2, &ulen); if (sock_domain(s) == AF_INET) { uint ip; ushort port; if ((r = fetch_sin(uaddr, ulen, &ip, &port)) < 0) return r; return sockconnect_in(s, ip, port_from_net(port)); } if ((r = fetch_sun_path(uaddr, ulen, path)) < 0) return r; return sockconnect(s, path); } // accept(2) and accept4(2) share everything but the flags word. // // They are two table entries rather than one with an alias, and the reason is // the argument count: accept takes three, so a handler that read a fourth // register would be reading whatever the caller happened to leave there and // would invent a SOCK_NONBLOCK out of stack litter. That is precisely the bug // the old `SYSCALL_ALIAS(288, accept)` would have grown the moment the flags // started being honoured. static addr_t do_accept(int sflags) { struct socket *s, *c; addr_t uaddr, ulenp; int r, fd; if ((r = argsock(0, &s)) < 0) return r; argaddr(1, &uaddr); argaddr(2, &ulenp); if ((c = sockaccept(s)) == 0) return -EINVAL; if ((fd = sock_to_fd(c, sflags)) < 0) { sockclose(c); return fd; } // The peer address of a Unix socket that never bound a name is the empty // one: family AF_UNIX and a zero-length path. Reporting it is optional // (both pointers may be null) but a caller that asks must not be left // with an uninitialised struct it then prints. if (uaddr && ulenp) { struct sockaddr_un sa; int len = (int)sizeof(sa.sun_family); int want = 0; memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; safestrcpy(sa.sun_path, sockname(c), UNIX_PATH_MAX); len += (int)strlen(sa.sun_path) + 1; if (get_user(want, ulenp) < 0) return fd; // the fd is already live; do not fail it if (want > len) want = len; if (want > 0) copy_to_user(uaddr, &sa, (uint64)want); put_user(len, ulenp); } return fd; } SYSCALL_DEFINE(accept) { return do_accept(0); } SYSCALL_DEFINE(accept4) { int flags = 0; argint(3, &flags); if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; return do_accept(flags); } SYSCALL_DEFINE(socketpair) { int domain, type, protocol; addr_t usv; struct socket *a, *b; int fd0, fd1, r; argint(0, &domain); argint(1, &type); argint(2, &protocol); argaddr(3, &usv); if (domain != AF_UNIX) return -EAFNOSUPPORT; int sflags = type & (SOCK_CLOEXEC | SOCK_NONBLOCK); type &= 0xFF; if (type != SOCK_STREAM) return -EPROTOTYPE; if ((r = sockpair(&a, &b)) < 0) return r; if ((fd0 = sock_to_fd(a, sflags)) < 0) { sockclose(a); sockclose(b); return fd0; } if ((fd1 = sock_to_fd(b, sflags)) < 0) { myproc()->ofile[fd0] = 0; sockclose(a); sockclose(b); return fd1; } int sv[2] = { fd0, fd1 }; if (copy_to_user(usv, sv, sizeof(sv)) < 0) { myproc()->ofile[fd0] = 0; myproc()->ofile[fd1] = 0; sockclose(a); sockclose(b); return -EFAULT; } return 0; } // Host byte order to network, for a port on its way back to userland. The // inverse of port_from_net above, and the same two lines: a sockaddr always // carries the port big-endian, whichever direction it is travelling. static ushort port_to_net(ushort p) { return (ushort)(((p & 0xFF) << 8) | ((p >> 8) & 0xFF)); } // send/recv with no flags are read/write, and sendto/recvfrom are those plus an // address. Implementing them here rather than leaving them ENOSYS saves every // ported program from being rewritten to use read() and write(). SYSCALL_DEFINE(sendto) { struct socket *s; addr_t ubuf, uaddr; int len, flags, ulen, r; uint ip = 0; ushort port = 0; if ((r = argsock(0, &s)) < 0) return r; argaddr(1, &ubuf); argint(2, &len); argint(3, &flags); argaddr(4, &uaddr); argint(5, &ulen); if (len < 0) return -EINVAL; char *p = (char *)user_ptr(ubuf, (uint64)len); if (len && !p) return -EFAULT; // A null address is send(), not an error: the socket already knows where // it is pointed, which for TCP is the only possibility anyway. if (uaddr) { if ((r = fetch_sin(uaddr, ulen, &ip, &port)) < 0) return r; port = port_from_net(port); } return socksendto(s, p, len, ip, port); } SYSCALL_DEFINE(recvfrom) { struct socket *s; addr_t ubuf, uaddr, ulenp; int len, flags, r; uint ip = 0; ushort port = 0; if ((r = argsock(0, &s)) < 0) return r; argaddr(1, &ubuf); argint(2, &len); argint(3, &flags); argaddr(4, &uaddr); argaddr(5, &ulenp); if (len < 0) return -EINVAL; char *p = (char *)user_ptr(ubuf, (uint64)len); if (len && !p) return -EFAULT; r = sockrecvfrom(s, p, len, &ip, &port); if (r < 0 || uaddr == 0) return r; // The address is reported only for the family that has one to report. A // Unix socket leaves ip at zero, and the honest answer there is a length of // zero rather than a sockaddr full of nothing that a caller would read as // 0.0.0.0:0 and believe. int alen = 0; if (sock_domain(s) == AF_INET) { struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr = ip; sa.sin_port = port_to_net(port); alen = (int)sizeof(sa); // Truncated rather than refused if the caller's buffer is smaller, // which is what accept() and getsockname() do everywhere: the length // written back tells them what they got. int cap = alen; if (ulenp && get_user(cap, ulenp) < 0) return -EFAULT; if (cap > alen) cap = alen; if (cap > 0 && copy_to_user(uaddr, &sa, (uint64)cap) < 0) return -EFAULT; } if (ulenp && copy_to_user(ulenp, &alen, sizeof(alen)) < 0) return -EFAULT; return r; } // shutdown(2). Half-close is not modelled -- there is one buffer per // direction and no per-direction flag -- so SHUT_WR and SHUT_RDWR both mean // "the peer sees end-of-file", which is the behaviour programs depend on. SYSCALL_DEFINE(sockshutdown) { struct socket *s; int how, r; if ((r = argsock(0, &s)) < 0) return r; argint(1, &how); return sock_shutdown(s, how); } // setsockopt / getsockopt. // // These both used to succeed unconditionally -- setsockopt was `return 0;` and // getsockopt wrote a zero for every option asked about. The comment said it was // so that a server setting SO_REUSEADDR and treating failure as fatal would not // fail. // // That trade is the wrong way round, and it is worth being precise about why, // because "accept and ignore" looks like the kind thing to do. A caller that // gets ENOPROTOOPT knows the option did not take effect and can decide what to // do. A caller that gets success *changes its behaviour on the strength of it*: // it sets SO_RCVTIMEO and then blocks forever in a read it believes will time // out; it sets TCP_NODELAY and reports latency numbers that measure Nagle. The // failure is silent, remote from the call, and indistinguishable from a bug in // the protocol stack. // // So the rule here is the one the rest of this kernel already follows: do it, // or say you did not. The four options below are genuinely applied to lwIP's // pcb; the readable ones report the truth; everything else is ENOPROTOOPT. // // SO_REUSEADDR is honoured rather than refused precisely because of the // original comment's concern -- it is the one option a server really does set // and really does check. // Fetch the int a setsockopt call is passing. Every option handled here is an // int; anything wanting a struct (SO_RCVTIMEO and friends) is refused above. static int opt_in(addr_t uval, int ulen, int *out) { if (!uval || ulen < (int)sizeof(int)) return -EINVAL; return get_user(*out, uval) < 0 ? -EFAULT : 0; } // Hand one int back, and report how many bytes that was. static int opt_out(addr_t uval, addr_t ulenp, int val) { int want = 0; if (!uval || !ulenp) return -EFAULT; if (get_user(want, ulenp) < 0) return -EFAULT; if (want < (int)sizeof(int)) return -EINVAL; if (put_user(val, uval) < 0) return -EFAULT; want = sizeof(int); return put_user(want, ulenp) < 0 ? -EFAULT : 0; } SYSCALL_DEFINE(setsockopt) { struct socket *s; addr_t uval; int level, name, ulen, val, r; if ((r = argsock(0, &s)) < 0) return r; argint(1, &level); argint(2, &name); argaddr(3, &uval); argint(4, &ulen); if ((r = opt_in(uval, ulen, &val)) < 0) return r; return sockopt_set(s, level, name, val); } SYSCALL_DEFINE(getsockopt) { struct socket *s; addr_t uval, ulenp; int level, name, val = 0, r; if ((r = argsock(0, &s)) < 0) return r; argint(1, &level); argint(2, &name); argaddr(3, &uval); argaddr(4, &ulenp); if ((r = sockopt_get(s, level, name, &val)) < 0) return r; return opt_out(uval, ulenp, val); }