Bug 252673 - Read on a SEQPACKET AF_UNIX socket returns the wrong error when disallowed by Capsicum
Summary: Read on a SEQPACKET AF_UNIX socket returns the wrong error when disallowed by...
Status: Closed Unable to Reproduce
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: 12.2-STABLE
Hardware: Any Any
: --- Affects Only Me
Assignee: freebsd-bugs (Nobody)
Depends on:
Reported: 2021-01-14 15:07 UTC by David Chisnall
Modified: 2021-01-14 16:33 UTC (History)
2 users (show)

See Also:

test program (353 bytes, text/plain)
2021-01-14 15:33 UTC, Mark Johnston
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description David Chisnall freebsd_committer 2021-01-14 15:07:42 UTC
A socket is created with:

socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socks);

And then restricted with:

cap_rights_t rights;
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.
Comment 1 Mark Johnston freebsd_committer 2021-01-14 15:33:37 UTC
Created attachment 221566 [details]
test program

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?
Comment 2 David Chisnall freebsd_committer 2021-01-14 16:04:15 UTC
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?
Comment 3 Mark Johnston freebsd_committer 2021-01-14 16:26:33 UTC
(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.
Comment 4 David Chisnall freebsd_committer 2021-01-14 16:33:02 UTC
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.
Comment 5 David Chisnall freebsd_committer 2021-01-14 16:33:39 UTC
I'm happy to close this now - I can reopen it if I manage to create a reduced test case.