From: Helge Deller <[email protected]> Some programs test availability of socket options by asking for the value with an NULL optval address, which currenrly always trigger an EFAULT in qemu. Fix it by allowing a NULL address, in the same manner as the Linux kernel on physical machines.
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/2390 Signed-off-by: Helge Deller <[email protected]> Reviewed-by: Pierrick Bouvier <[email protected]> (cherry picked from commit 08dc3e240fc00213c0eb29b71569dc0ca9301337) Signed-off-by: Michael Tokarev <[email protected]> diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 905db117ff..3a1b41e84b 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2648,6 +2648,10 @@ get_timeout: if (ret < 0) { return ret; } + /* special case: destination address is NULL, return 0 */ + if (optval_addr) { + len = 0; + } if (len == sizeof(struct target__kernel_sock_timeval)) { if (copy_to_user_timeval64(optval_addr, &tv)) { return -TARGET_EFAULT; @@ -2848,7 +2852,10 @@ get_timeout: } if (len > lv) len = lv; - if (len == 4) { + if (!optval_addr) { + /* writing to NULL does not give error */ + len = 0; + } else if (len == 4) { if (put_user_u32(val, optval_addr)) return -TARGET_EFAULT; } else { @@ -2881,18 +2888,24 @@ get_timeout: return -TARGET_EINVAL; lv = sizeof(lv); ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); +write_ret: if (ret < 0) return ret; - if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) { + if (!optval_addr) { + len = 0; + } else if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) { len = 1; - if (put_user_u32(len, optlen) - || put_user_u8(val, optval_addr)) + if (put_user_u8(val, optval_addr)) { return -TARGET_EFAULT; + } } else { if (len > sizeof(int)) len = sizeof(int); - if (put_user_u32(len, optlen) - || put_user_u32(val, optval_addr)) + if (put_user_u32(val, optval_addr)) { + return -TARGET_EFAULT; + } + } + if (put_user_u32(len, optlen)) { return -TARGET_EFAULT; } break; @@ -2943,20 +2956,7 @@ get_timeout: return -TARGET_EINVAL; lv = sizeof(lv); ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); - if (ret < 0) - return ret; - if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) { - len = 1; - if (put_user_u32(len, optlen) - || put_user_u8(val, optval_addr)) - return -TARGET_EFAULT; - } else { - if (len > sizeof(int)) - len = sizeof(int); - if (put_user_u32(len, optlen) - || put_user_u32(val, optval_addr)) - return -TARGET_EFAULT; - } + goto write_ret; break; default: ret = -TARGET_ENOPROTOOPT; @@ -2990,8 +2990,14 @@ get_timeout: if (ret < 0) { return ret; } - if (put_user_u32(lv, optlen) - || put_user_u32(val, optval_addr)) { + if (optval_addr) { + if (put_user_u32(val, optval_addr)) { + return -TARGET_EFAULT; + } + } else { + lv = 0; + } + if (put_user_u32(lv, optlen)) { return -TARGET_EFAULT; } break; -- 2.47.3
