The EFAULT changes use a result of NULL to detect a failure from lock*() functions. There are syscalls that accept NULL as a valid argument and now the syscalls return -EFAULT. These patches allow appropriate syscalls to accept NULL.
I have put together a regression test harness wrapped around the Linux Test Project (LTP). I've been able to find regressions that were caused by the EFAULT changes. It's more exhaustive than running an ls executable and has helped find existing bugs as well as regressions. It will run regression tests for multiple architectures. I'll be sending a few more patches for this same type of regression. I should also be sending in the test harness once I've worked out a few more details.
Index: qemu/linux-user/syscall.c =================================================================== --- qemu.orig/linux-user/syscall.c 2007-11-28 16:36:53.000000000 -0700 +++ qemu/linux-user/syscall.c 2007-11-28 17:03:31.000000000 -0700 @@ -5399,7 +5399,7 @@ grouplist = alloca(gidsetsize * sizeof(gid_t)); ret = get_errno(getgroups(gidsetsize, grouplist)); - if (!is_error(ret)) { + if (!is_error(ret) && arg1) { target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * 2, 0); if (!target_grouplist) goto efault; @@ -5549,7 +5549,7 @@ grouplist = alloca(gidsetsize * sizeof(gid_t)); ret = get_errno(getgroups(gidsetsize, grouplist)); - if (!is_error(ret)) { + if (!is_error(ret) && arg1) { target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * 4, 0); if (!target_grouplist) { ret = -TARGET_EFAULT;
Index: qemu/linux-user/syscall.c =================================================================== --- qemu.orig/linux-user/syscall.c 2007-11-28 17:26:46.000000000 -0700 +++ qemu/linux-user/syscall.c 2007-11-28 17:29:11.000000000 -0700 @@ -3398,10 +3398,14 @@ ret = 0; /* avoid warning */ break; case TARGET_NR_read: - if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) - goto efault; - ret = get_errno(read(arg1, p, arg3)); - unlock_user(p, arg2, ret); + if (!arg3) + ret = 0; + else { + if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) + goto efault; + ret = get_errno(read(arg1, p, arg3)); + unlock_user(p, arg2, ret); + } break; case TARGET_NR_write: if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
Index: qemu/linux-user/syscall.c =================================================================== --- qemu.orig/linux-user/syscall.c 2007-11-28 22:44:27.000000000 -0700 +++ qemu/linux-user/syscall.c 2007-11-28 22:46:16.000000000 -0700 @@ -1203,7 +1203,7 @@ abi_long ret; if (get_user_u32(addrlen, target_addrlen_addr)) - return -TARGET_EFAULT; + return -TARGET_EINVAL; addr = alloca(addrlen);
Index: qemu/linux-user/path.c =================================================================== --- qemu.orig/linux-user/path.c 2007-11-28 23:13:12.000000000 -0700 +++ qemu/linux-user/path.c 2007-11-28 23:13:24.000000000 -0700 @@ -152,7 +152,7 @@ { /* Only do absolute paths: quick and dirty, but should mostly be OK. Could do relative by tracking cwd. */ - if (!base || name[0] != '/') + if (!base || !name || name[0] != '/') return name; return follow_path(base, name) ?: name; Index: qemu/linux-user/syscall.c =================================================================== --- qemu.orig/linux-user/syscall.c 2007-11-28 23:01:10.000000000 -0700 +++ qemu/linux-user/syscall.c 2007-11-28 23:13:06.000000000 -0700 @@ -3864,8 +3864,12 @@ goto unimplemented; #endif case TARGET_NR_acct: - if (!(p = lock_user_string(arg1))) - goto efault; + if (arg1) { + if (!(p = lock_user_string(arg1))) + goto efault; + } else { + p = NULL; + } ret = get_errno(acct(path(p))); unlock_user(p, arg1, 0); break;