A socket is created with:
socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socks);
And then restricted with:
cap_rights_limit(fd, cap_rights_init(&rights, CAP_WRITE));
When passed to the `read` system call, it fails (as it should) but returns `ENOENT`. This is not a documented error value for `read`. I believe the correct return value is `ENOTCAPABLE` (which is also not a documented return value for read, but would have immediately told me what the problem was).
I have not validated whether different kinds of socket have the same behaviour.
Created attachment 221566 [details]
I'm not able to reproduce this with the attached test program on any of head, 12.2, or 11.4. I get "read: Capabilities insufficient". Could you share a test program that demonstrates the problem?
Curious. That test also passes for me. I'll see if I can produce a reduced test case. In the program where I encountered this, the fd is actually `dup`'d and then passed to a child process (`vfork` + `execve`) and is actually calling `__sys_read` not `read`, because the first `read` is before enough libc is set up for `read` to not SEGV. I've added a `dup` and a `fork` to your test and it still does the right thing. Is there anything in the file descriptor inheritance logic across `execve` that may cause a change here?
(In reply to David Chisnall from comment #2)
I don't have any good ideas. Are you certain that something isn't setting O_CLOEXEC on the sockets?
You might try verifying with e.g. "procstat -f" or gdb's "info proc files" that the descriptor really is a socket. I can't see how you'd get ENOENT from a read on a socket.
I'm writing to the socket before the read and the write goes through. The lack of `CAP_READ` was a bug in my code and when I fixed that, the read also worked correctly.
I'm happy to close this now - I can reopen it if I manage to create a reduced test case.