@samitouri / QOSamiQemu / commits / 08dc3e240f

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;