On Mon, 2007-11-05 at 22:42 +0100, Fabrice Bellard wrote: > Thayne Harbaugh wrote: > > On Sat, 2007-11-03 at 20:05 +0100, Fabrice Bellard wrote: > >> I think that using host addresses in __put_user and __get_user is not > >> logical. They should use target addresses as get_user and put_user. As > >> Paul said, It is not worth mixing get/put/copy and lock/unlock functions. > > > > Please see the "RFC: x86_64 Best way to fix 'cast to pointer'" email for > > some discussion of get/put/copy and lock/unlock. {get,put}_user() is > > used for individual ints or other atomically writable types that are > > passed as pointers into a syscall. copy_{to,from}_user_<struct>() are > > used for structures that are passed to a syscall. lock/unlock() will be > > used internally in these because lock/unlock does address translation. > > lock/unlock() are still needed and are independent. __{get,put}_user() > > will operate internally in these functions on structure data members > > where lock/unlock() access_ok() have already been called. > > I believed I was clear : once you keep lock/unlock, there is no point in > modifying the code to use put_user/get_user and copy[to,from]_user.
without lock/unlock how do you propose that target/host address translation be performed? Are you suggesting a g2h() inside of copy_{to,from}_user()? > So either you keep the code as it is and extend lock and unlock to > return an error code or you suppress all lock/unlock to use a Linux like > API (i.e. put_user/get_user , copy[to,from]_user, access_ok, > __put_user/__get_user). The error code because lock/unlock_user would then call access_ok()? > So for gettimeofday, if we exclude the fact that the conversion of > struct timeval will be factorized, you have either: > > { > struct timeval tv; > ret = get_errno(gettimeofday(&tv, NULL)); > if (!is_error(ret)) { > struct target_timeval *target_tv; > > lock_user_struct(target_tv, arg1, 0); > target_tv->tv_sec = tswapl(tv->tv_sec); > target_tv->tv_usec = tswapl(tv->tv_usec); > if (unlock_user_struct(target_tv, arg1, 1)) { > ret = -TARGET_EFAULT; > goto fail; > } > } > } > > Or > > { > struct timeval tv; > ret = get_errno(gettimeofday(&tv, NULL)); > if (!is_error(ret)) { > struct target_timeval target_tv; > > target_tv.tv_sec = tswapl(tv->tv_sec); > target_tv.tv_usec = tswapl(tv->tv_usec); > if (copy_to_user(arg1, &target_tv, sizeof(target_tv)) { > ret = -TARGET_EFAULT; > goto fail; > } > } > } I don't see where the second one is handling target/host address translation. A problem with both of the above examples is that they use tswapl(). Without the proper type casting tswapl() doesn't do proper sign extension when dealing with 64bit/32bit host/target relationships. I've fixed more than one location where this has resulted in bugs. What I'm suggesting is the following: static inline abi_long copy_to_user_timeval(abi_ulong target_timeval_addr, const struct timeval *tv) { if (!access_ok(VERIFY_WRITE, target_tv_addr, sizeof(*target_tv))) return -TARGET_EFAULT; lock_user_struct(target_tv, target_tv_addr, 0); __put_user(tv->tv_sec, &target_tv->tv_sec); __put_user(tv->tv_usec, &target_tv->tv_usec); unlock_user_struct(target_tv, target_tv_addr, 1); return 0; } . . . { struct timeval tv; ret = get_errno(gettimeofday(&tv, NULL)); if (!is_error(ret)) { if (copy_to_user_timeval(arg1, &tv)) { ret = -TARGET_EFAULT; goto fail; } } } (Ignoring the factorization of the timeval struct) copy_to_user_timeval() here properly handles target/host address translation. It also uses __put_user() which properly handles any sign extension. The difference between the main syscall code and the linux kernel is simply this: copy_to_user(arg1, &tv, sizeof(tv)) -> copy_to_user_timeval(arg1, &tv) ^^^^^^^^^^^^ ^^^^^^^^ Allowing that minor difference (since qemu needs to do translation between the structure types) is reasonable. Furthermore the access_ok() is kept inside the copy_to_user*() function just like in the linux kernel. The construct I've shown above is very comparable to kernel code. > Personally I prefer the Linux API style, but Paul's lock/unlock is also > perfectly logical. The advantage of keeping the Paul's API is that you > can minimize the number of changes. Sure, there are advantages to minimal changes. Personally I prefer the Linux API style because that's what is being emulated. > Another point is that if you use the Linux API style, it is not needed > to change the API as you want to do. The only useful addition I see is > to add an automatic tswap in get/put/__get/__put user as it is done now. Why would a tswap() need to be added? It's already inside of __{get,put}_user()? I'm a bit confused since the changes I'm making don't deviate from the intentions of the kernel code and my changes will address address translation and type casting as are necessary for qemu. I think that the big problem is that the current patches in my tree are different than what Stuart originally sent and the initial three patches that I sent I hadn't gone back to add the address translation to {get,put}_user() (yes, it's my fault, I should have paid more attention). I've attached a newer patch which reflects what I'm doing at the higher level (it still doesn't address the sparc64 code, though). > Moreover, it may be questionnable to do the page right check in s/right/write/; ? > access_ok(). The Linux kernel does not do it at that point : access_ok() > only verifies that the address is in the user address space. You're right - I don't see that any of the archs use the access type argument. > > [...] > > Fabrice.
Index: qemu/linux-user/qemu.h =================================================================== --- qemu.orig/linux-user/qemu.h 2007-11-03 12:36:40.000000000 -0600 +++ qemu/linux-user/qemu.h 2007-11-05 14:24:17.000000000 -0700 @@ -251,24 +251,63 @@ 0;\ }) -#define put_user(x,ptr)\ -({\ - int __ret;\ - if (access_ok(VERIFY_WRITE, ptr, sizeof(*ptr)))\ - __ret = __put_user(x, ptr);\ - else\ - __ret = -EFAULT;\ - __ret;\ +/* put_user()/get_user() take a guest address and check access */ +#define put_user(x, gaddr, target_type) \ +({ \ + abi_ulong __gaddr = (gaddr); \ + target_type __hptr; \ + abi_long __ret; \ + if (access_ok(VERIFY_WRITE, __gaddr, sizeof(target_type))) { \ + __hptr = lock_user(__gaddr, sizeof(target_type), 0); \ + __ret = __put_user((x), __hptr); \ + unlock_user(__hptr, __gaddr, sizeof(target_type)); \ + } else \ + __ret = -TARGET_EFAULT; \ + __ret; \ }) -#define get_user(x,ptr)\ -({\ - int __ret;\ - if (access_ok(VERIFY_READ, ptr, sizeof(*ptr)))\ - __ret = __get_user(x, ptr);\ - else\ - __ret = -EFAULT;\ - __ret;\ +#define get_user(x, gaddr, target_type) \ +({ \ + abi_ulong __gaddr = (gaddr); \ + target_type __hptr; \ + abi_long __ret; \ + if (access_ok(VERIFY_READ, __gaddr, sizeof(target_type))) { \ + __hptr = lock_user(__gaddr, sizeof(target_type), 1); \ + __ret = __get_user((x), __hptr); \ + unlock_user(__hptr, __gaddr, 0); \ + } else \ + __ret = -TARGET_EFAULT; \ + __ret; \ +}) + +#define copy_from_user(hptr, gaddr, len) \ +({ \ + abi_ulong __gaddr = (gaddr); \ + void *__hptr; \ + long __len = len; \ + abi_long __cfu_ret=0; \ + if (access_ok(VERIFY_READ, __gaddr, len)) { \ + __hptr = lock_user(__gaddr, len, 1); \ + memcpy((hptr), __hptr, len); \ + unlock_user(__hptr, __gaddr, 0); \ + } else \ + __cfu_ret = -TARGET_EFAULT; \ + __cfu_ret; \ +}) + +#define copy_to_user(gaddr, hptr, len) \ +({ \ + abi_ulong __gaddr = (gaddr); \ + void *__hptr; \ + long __len = len; \ + abi_long __ctu_ret=0; \ + if (access_ok(VERIFY_WRITE, __gaddr, len)) { \ + __hptr = lock_user(__gaddr, len, 0); \ + memcpy(__hptr, (hptr), len); \ + unlock_user(__hptr, __gaddr, len); \ + } else \ + __ctu_ret = -TARGET_EFAULT; \ + __ctu_ret; \ }) /* Functions for accessing guest memory. The tget and tput functions Index: qemu/linux-user/syscall.c =================================================================== --- qemu.orig/linux-user/syscall.c 2007-11-03 12:36:29.000000000 -0600 +++ qemu/linux-user/syscall.c 2007-11-05 14:36:54.000000000 -0700 @@ -1761,7 +1761,7 @@ break; } } - if (put_user(raddr, (abi_ulong *)third)) + if (put_user(raddr, third, abi_ulong *)) return -TARGET_EFAULT; ret = 0; break; @@ -3697,18 +3697,20 @@ if (!is_error(ret)) { struct target_statfs *target_stfs; + if (access_ok(VERIFY_WRITE, arg2, sizeof(*target_stfs))) + ret = -TARGET_EFAULT; + lock_user_struct(target_stfs, arg2, 0); - /* ??? put_user is probably wrong. */ - put_user(stfs.f_type, &target_stfs->f_type); - put_user(stfs.f_bsize, &target_stfs->f_bsize); - put_user(stfs.f_blocks, &target_stfs->f_blocks); - put_user(stfs.f_bfree, &target_stfs->f_bfree); - put_user(stfs.f_bavail, &target_stfs->f_bavail); - put_user(stfs.f_files, &target_stfs->f_files); - put_user(stfs.f_ffree, &target_stfs->f_ffree); - put_user(stfs.f_fsid.__val[0], &target_stfs->f_fsid.val[0]); - put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]); - put_user(stfs.f_namelen, &target_stfs->f_namelen); + __put_user(stfs.f_type, &target_stfs->f_type); + __put_user(stfs.f_bsize, &target_stfs->f_bsize); + __put_user(stfs.f_blocks, &target_stfs->f_blocks); + __put_user(stfs.f_bfree, &target_stfs->f_bfree); + __put_user(stfs.f_bavail, &target_stfs->f_bavail); + __put_user(stfs.f_files, &target_stfs->f_files); + __put_user(stfs.f_ffree, &target_stfs->f_ffree); + __put_user(stfs.f_fsid.__val[0], &target_stfs->f_fsid.val[0]); + __put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]); + __put_user(stfs.f_namelen, &target_stfs->f_namelen); unlock_user_struct(target_stfs, arg2, 1); } break; @@ -3724,18 +3726,20 @@ if (!is_error(ret)) { struct target_statfs64 *target_stfs; + if (access_ok(VERIFY_WRITE, arg3, sizeof(*target_stfs))) + ret = -TARGET_EFAULT; + lock_user_struct(target_stfs, arg3, 0); - /* ??? put_user is probably wrong. */ - put_user(stfs.f_type, &target_stfs->f_type); - put_user(stfs.f_bsize, &target_stfs->f_bsize); - put_user(stfs.f_blocks, &target_stfs->f_blocks); - put_user(stfs.f_bfree, &target_stfs->f_bfree); - put_user(stfs.f_bavail, &target_stfs->f_bavail); - put_user(stfs.f_files, &target_stfs->f_files); - put_user(stfs.f_ffree, &target_stfs->f_ffree); - put_user(stfs.f_fsid.__val[0], &target_stfs->f_fsid.val[0]); - put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]); - put_user(stfs.f_namelen, &target_stfs->f_namelen); + __put_user(stfs.f_type, &target_stfs->f_type); + __put_user(stfs.f_bsize, &target_stfs->f_bsize); + __put_user(stfs.f_blocks, &target_stfs->f_blocks); + __put_user(stfs.f_bfree, &target_stfs->f_bfree); + __put_user(stfs.f_bavail, &target_stfs->f_bavail); + __put_user(stfs.f_files, &target_stfs->f_files); + __put_user(stfs.f_ffree, &target_stfs->f_ffree); + __put_user(stfs.f_fsid.__val[0], &target_stfs->f_fsid.val[0]); + __put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]); + __put_user(stfs.f_namelen, &target_stfs->f_namelen); unlock_user_struct(target_stfs, arg3, 0); } break; @@ -4472,51 +4476,56 @@ #ifdef TARGET_ARM if (((CPUARMState *)cpu_env)->eabi) { struct target_eabi_stat64 *target_st; + + if (access_ok(VERIFY_WRITE, arg2, sizeof(*target_st))) + ret = -TARGET_EFAULT; + lock_user_struct(target_st, arg2, 1); memset(target_st, 0, sizeof(struct target_eabi_stat64)); - /* put_user is probably wrong. */ - put_user(st.st_dev, &target_st->st_dev); - put_user(st.st_ino, &target_st->st_ino); + __put_user(st.st_dev, &target_st->st_dev); + __put_user(st.st_ino, &target_st->st_ino); #ifdef TARGET_STAT64_HAS_BROKEN_ST_INO - put_user(st.st_ino, &target_st->__st_ino); + __put_user(st.st_ino, &target_st->__st_ino); #endif - put_user(st.st_mode, &target_st->st_mode); - put_user(st.st_nlink, &target_st->st_nlink); - put_user(st.st_uid, &target_st->st_uid); - put_user(st.st_gid, &target_st->st_gid); - put_user(st.st_rdev, &target_st->st_rdev); - /* XXX: better use of kernel struct */ - put_user(st.st_size, &target_st->st_size); - put_user(st.st_blksize, &target_st->st_blksize); - put_user(st.st_blocks, &target_st->st_blocks); - put_user(st.st_atime, &target_st->target_st_atime); - put_user(st.st_mtime, &target_st->target_st_mtime); - put_user(st.st_ctime, &target_st->target_st_ctime); + __put_user(st.st_mode, &target_st->st_mode); + __put_user(st.st_nlink, &target_st->st_nlink); + __put_user(st.st_uid, &target_st->st_uid); + __put_user(st.st_gid, &target_st->st_gid); + __put_user(st.st_rdev, &target_st->st_rdev); + __put_user(st.st_size, &target_st->st_size); + __put_user(st.st_blksize, &target_st->st_blksize); + __put_user(st.st_blocks, &target_st->st_blocks); + __put_user(st.st_atime, &target_st->target_st_atime); + __put_user(st.st_mtime, &target_st->target_st_mtime); + __put_user(st.st_ctime, &target_st->target_st_ctime); unlock_user_struct(target_st, arg2, 0); } else #endif { struct target_stat64 *target_st; + + if (access_ok(VERIFY_WRITE, arg2, sizeof(*target_st))) + ret = -TARGET_EFAULT; + lock_user_struct(target_st, arg2, 1); memset(target_st, 0, sizeof(struct target_stat64)); - /* ??? put_user is probably wrong. */ - put_user(st.st_dev, &target_st->st_dev); - put_user(st.st_ino, &target_st->st_ino); + __put_user(st.st_dev, &target_st->st_dev); + __put_user(st.st_ino, &target_st->st_ino); #ifdef TARGET_STAT64_HAS_BROKEN_ST_INO - put_user(st.st_ino, &target_st->__st_ino); + __put_user(st.st_ino, &target_st->__st_ino); #endif - put_user(st.st_mode, &target_st->st_mode); - put_user(st.st_nlink, &target_st->st_nlink); - put_user(st.st_uid, &target_st->st_uid); - put_user(st.st_gid, &target_st->st_gid); - put_user(st.st_rdev, &target_st->st_rdev); + __put_user(st.st_mode, &target_st->st_mode); + __put_user(st.st_nlink, &target_st->st_nlink); + __put_user(st.st_uid, &target_st->st_uid); + __put_user(st.st_gid, &target_st->st_gid); + __put_user(st.st_rdev, &target_st->st_rdev); /* XXX: better use of kernel struct */ - put_user(st.st_size, &target_st->st_size); - put_user(st.st_blksize, &target_st->st_blksize); - put_user(st.st_blocks, &target_st->st_blocks); - put_user(st.st_atime, &target_st->target_st_atime); - put_user(st.st_mtime, &target_st->target_st_mtime); - put_user(st.st_ctime, &target_st->target_st_ctime); + __put_user(st.st_size, &target_st->st_size); + __put_user(st.st_blksize, &target_st->st_blksize); + __put_user(st.st_blocks, &target_st->st_blocks); + __put_user(st.st_atime, &target_st->target_st_atime); + __put_user(st.st_mtime, &target_st->target_st_mtime); + __put_user(st.st_ctime, &target_st->target_st_ctime); unlock_user_struct(target_st, arg2, 0); } }