linux-user: Allow getsockopt() with NULL optval address
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 <deller@gmx.de> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Helge Deller committed
Apr 28, 2026 at 23:40 UTC
08dc3e240fc00213c0eb29b71569dc0ca9301337
1 file changed
+28
-22
linux-user/syscall.c
+28
-22
@@ -2644,6 +2644,10 @@ get_timeout:
2644
if (ret < 0) {
2645
return ret;
2646
}
2647
+ /* special case: destination address is NULL, return 0 */
2648
+ if (optval_addr) {
2649
+ len = 0;
2650
+ }
2651
if (len == sizeof(struct target__kernel_sock_timeval)) {
2652
if (copy_to_user_timeval64(optval_addr, &tv)) {
2653
return -TARGET_EFAULT;
@@ -2844,7 +2848,10 @@ get_timeout:
2848
}
2849
if (len > lv)
2850
len = lv;
2847
- if (len == 4) {
2851
+ if (!optval_addr) {
2852
+ /* writing to NULL does not give error */
2853
+ len = 0;
2854
+ } else if (len == 4) {
2855
if (put_user_u32(val, optval_addr))
2856
return -TARGET_EFAULT;
2857
} else {
@@ -2877,18 +2884,24 @@ get_timeout:
2884
return -TARGET_EINVAL;
2885
lv = sizeof(lv);
2886
ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv));
2887
+write_ret:
2888
if (ret < 0)
2889
return ret;
2882
- if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) {
2890
+ if (!optval_addr) {
2891
+ len = 0;
2892
+ } else if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) {
2893
len = 1;
2884
- if (put_user_u32(len, optlen)
2885
- || put_user_u8(val, optval_addr))
2894
+ if (put_user_u8(val, optval_addr)) {
2895
return -TARGET_EFAULT;
2896
+ }
2897
} else {
2898
if (len > sizeof(int))
2899
len = sizeof(int);
2890
- if (put_user_u32(len, optlen)
2891
- || put_user_u32(val, optval_addr))
2900
+ if (put_user_u32(val, optval_addr)) {
2901
+ return -TARGET_EFAULT;
2902
+ }
2903
+ }
2904
+ if (put_user_u32(len, optlen)) {
2905
return -TARGET_EFAULT;
2906
}
2907
break;
@@ -2939,20 +2952,7 @@ get_timeout:
2952
return -TARGET_EINVAL;
2953
lv = sizeof(lv);
2954
ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv));
2942
- if (ret < 0)
2943
- return ret;
2944
- if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) {
2945
- len = 1;
2946
- if (put_user_u32(len, optlen)
2947
- || put_user_u8(val, optval_addr))
2948
- return -TARGET_EFAULT;
2949
- } else {
2950
- if (len > sizeof(int))
2951
- len = sizeof(int);
2952
- if (put_user_u32(len, optlen)
2953
- || put_user_u32(val, optval_addr))
2954
- return -TARGET_EFAULT;
2955
- }
2955
+ goto write_ret;
2956
break;
2957
default:
2958
ret = -TARGET_ENOPROTOOPT;
@@ -2986,8 +2986,14 @@ get_timeout:
2986
if (ret < 0) {
2987
return ret;
2988
}
2989
- if (put_user_u32(lv, optlen)
2990
- || put_user_u32(val, optval_addr)) {
2989
+ if (optval_addr) {
2990
+ if (put_user_u32(val, optval_addr)) {
2991
+ return -TARGET_EFAULT;
2992
+ }
2993
+ } else {
2994
+ lv = 0;
2995
+ }
2996
+ if (put_user_u32(lv, optlen)) {
2997
return -TARGET_EFAULT;
2998
}
2999
break;