From: Igor Shevlyakov <[email protected]>
The kernel looks the fd up before validating the rest of the call, so a
negative fd is EBADF whatever else is wrong with the arguments. Several
arms of do_setsockopt()/do_getsockopt() reject optlen or optname and
return without ever reaching the host syscall, so the host never gets to
supply that EBADF and the guest sees the argument error instead:
setsockopt(-1, SOL_TCP, TCP_NODELAY, ptr, 0) EINVAL, want EBADF
setsockopt(-1, 0x4321, 1, ptr, sizeof(int)) ENOPROTOOPT
getsockopt(-1, 0x4321, 1, ptr, &len) EOPNOTSUPP
Caught by glibc's posix/test-errno.
Signed-off-by: Igor Shevlyakov <[email protected]>
Reviewed-by: Helge Deller <[email protected]>
Signed-off-by: Helge Deller <[email protected]>
---
linux-user/syscall.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 9f454502cb..e7c9fb33a3 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -2119,6 +2119,15 @@ static abi_long do_setsockopt(int sockfd, int level, int
optname,
abi_long ret;
int val;
+ /*
+ * The kernel looks the fd up before it validates anything else, so a bad
+ * fd is EBADF even when the rest of the call is also wrong. Several arms
+ * below reject optlen or optname without ever reaching the host syscall,
+ * and would report that instead.
+ */
+ if (sockfd < 0) {
+ return -TARGET_EBADF;
+ }
switch(level) {
case SOL_TCP:
case SOL_UDP:
@@ -2623,6 +2632,10 @@ static abi_long do_getsockopt(int sockfd, int level, int
optname,
int len, val;
socklen_t lv;
+ /* EBADF wins over the argument checks below; see do_setsockopt(). */
+ if (sockfd < 0) {
+ return -TARGET_EBADF;
+ }
switch(level) {
case TARGET_SOL_SOCKET:
level = SOL_SOCKET;
--
2.54.0