* [Qemu-devel] [PATCH] efault - verify pages are in cache and are read/write @ 2007-10-31 22:30 Thayne Harbaugh 2007-10-31 22:35 ` [Qemu-devel] Re: [PATCH] efault - update __get_user() __put_user() Thayne Harbaugh 2007-10-31 22:49 ` [Qemu-devel] Re: [PATCH] efault Thayne Harbaugh 0 siblings, 2 replies; 12+ messages in thread From: Thayne Harbaugh @ 2007-10-31 22:30 UTC (permalink / raw) To: qemu-devel [-- Attachment #1: Type: text/plain, Size: 415 bytes --] This patch adds the function page_check_range() to verify that pages are in the cache and that they are appropriately readable/writable. It also hooks up access_ok() to page_check_range() so that code patterns are similar to kernel code. When copying data from user space access_ok() is used to check that pages are readable. When copying data to user space access_ok() is used to check that pages are writable. [-- Attachment #2: 06_efault.patch.1.1 --] [-- Type: text/x-patch, Size: 2306 bytes --] Index: qemu/exec.c =================================================================== --- qemu.orig/exec.c 2007-10-31 10:49:10.000000000 -0600 +++ qemu/exec.c 2007-10-31 10:55:50.000000000 -0600 @@ -1875,6 +1875,33 @@ spin_unlock(&tb_lock); } +int page_check_range(target_ulong start, target_ulong len, int flags) +{ + PageDesc *p; + target_ulong end; + target_ulong addr; + + end = TARGET_PAGE_ALIGN(start+len); /* must do before we loose bits in the next step */ + start = start & TARGET_PAGE_MASK; + + if( end < start ) + /* we've wrapped around */ + return -1; + for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { + p = page_find(addr >> TARGET_PAGE_BITS); + if( !p ) + return -1; + if( !(p->flags & PAGE_VALID) ) + return -1; + + if (!(p->flags & PAGE_READ) && (flags & PAGE_READ) ) + return -1; + if (!(p->flags & PAGE_WRITE) && (flags & PAGE_WRITE) ) + return -1; + } + return 0; +} + /* called from signal handler: invalidate the code and unprotect the page. Return TRUE if the fault was succesfully handled. */ int page_unprotect(target_ulong address, unsigned long pc, void *puc) Index: qemu/cpu-all.h =================================================================== --- qemu.orig/cpu-all.h 2007-10-31 10:49:10.000000000 -0600 +++ qemu/cpu-all.h 2007-10-31 10:55:50.000000000 -0600 @@ -691,6 +691,7 @@ int page_get_flags(target_ulong address); void page_set_flags(target_ulong start, target_ulong end, int flags); void page_unprotect_range(target_ulong data, target_ulong data_size); +int page_check_range(target_ulong start, target_ulong len, int flags); CPUState *cpu_copy(CPUState *env); Index: qemu/linux-user/qemu.h =================================================================== --- qemu.orig/linux-user/qemu.h 2007-10-31 10:55:48.000000000 -0600 +++ qemu/linux-user/qemu.h 2007-10-31 10:55:50.000000000 -0600 @@ -185,7 +185,8 @@ #define VERIFY_READ 0 #define VERIFY_WRITE 1 -#define access_ok(type,addr,size) (1) +#define access_ok(type,addr,size) \ + (page_check_range((target_ulong)addr,size,(type==VERIFY_READ)?PAGE_READ:PAGE_WRITE)==0) /* NOTE get_user and put_user use host addresses. */ #define __put_user(x,ptr)\ ^ permalink raw reply [flat|nested] 12+ messages in thread
* [Qemu-devel] Re: [PATCH] efault - update __get_user() __put_user() 2007-10-31 22:30 [Qemu-devel] [PATCH] efault - verify pages are in cache and are read/write Thayne Harbaugh @ 2007-10-31 22:35 ` Thayne Harbaugh 2007-10-31 22:43 ` [Qemu-devel] Re: [PATCH] efault - add data type to put_user()/get_user() Thayne Harbaugh 2007-10-31 22:49 ` [Qemu-devel] Re: [PATCH] efault Thayne Harbaugh 1 sibling, 1 reply; 12+ messages in thread From: Thayne Harbaugh @ 2007-10-31 22:35 UTC (permalink / raw) To: qemu-devel [-- Attachment #1: Type: text/plain, Size: 103 bytes --] This patch is a minor update to __get_user() and __put_user() to emphasize that they take host points. [-- Attachment #2: 06_efault.patch.1.2 --] [-- Type: text/x-patch, Size: 2072 bytes --] Index: qemu/linux-user/qemu.h =================================================================== --- qemu.orig/linux-user/qemu.h 2007-10-31 11:03:03.000000000 -0600 +++ qemu/linux-user/qemu.h 2007-10-31 11:04:04.000000000 -0600 @@ -188,22 +188,22 @@ #define access_ok(type,addr,size) \ (page_check_range((target_ulong)addr,size,(type==VERIFY_READ)?PAGE_READ:PAGE_WRITE)==0) -/* NOTE get_user and put_user use host addresses. */ -#define __put_user(x,ptr)\ +/* NOTE __get_user and __put_user use host pointers and don't check access. */ +#define __put_user(x, hptr)\ ({\ - int size = sizeof(*ptr);\ + int size = sizeof(*hptr);\ switch(size) {\ case 1:\ - *(uint8_t *)(ptr) = (typeof(*ptr))(x);\ + *(uint8_t *)(hptr) = (typeof(*hptr))(x);\ break;\ case 2:\ - *(uint16_t *)(ptr) = tswap16((typeof(*ptr))(x));\ + *(uint16_t *)(hptr) = tswap16((typeof(*hptr))(x));\ break;\ case 4:\ - *(uint32_t *)(ptr) = tswap32((typeof(*ptr))(x));\ + *(uint32_t *)(hptr) = tswap32((typeof(*hptr))(x));\ break;\ case 8:\ - *(uint64_t *)(ptr) = tswap64((typeof(*ptr))(x));\ + *(uint64_t *)(hptr) = tswap64((typeof(*hptr))(x));\ break;\ default:\ abort();\ @@ -211,21 +211,21 @@ 0;\ }) -#define __get_user(x, ptr) \ +#define __get_user(x, hptr) \ ({\ - int size = sizeof(*ptr);\ + int size = sizeof(*hptr);\ switch(size) {\ case 1:\ - x = (typeof(*ptr))*(uint8_t *)(ptr);\ + x = (typeof(*hptr))*(uint8_t *)(hptr);\ break;\ case 2:\ - x = (typeof(*ptr))tswap16(*(uint16_t *)(ptr));\ + x = (typeof(*hptr))tswap16(*(uint16_t *)(hptr));\ break;\ case 4:\ - x = (typeof(*ptr))tswap32(*(uint32_t *)(ptr));\ + x = (typeof(*hptr))tswap32(*(uint32_t *)(hptr));\ break;\ case 8:\ - x = (typeof(*ptr))tswap64(*(uint64_t *)(ptr));\ + x = (typeof(*hptr))tswap64(*(uint64_t *)(hptr));\ break;\ default:\ abort();\ ^ permalink raw reply [flat|nested] 12+ messages in thread
* [Qemu-devel] Re: [PATCH] efault - add data type to put_user()/get_user() 2007-10-31 22:35 ` [Qemu-devel] Re: [PATCH] efault - update __get_user() __put_user() Thayne Harbaugh @ 2007-10-31 22:43 ` Thayne Harbaugh 2007-11-02 23:26 ` Thayne Harbaugh 0 siblings, 1 reply; 12+ messages in thread From: Thayne Harbaugh @ 2007-10-31 22:43 UTC (permalink / raw) To: qemu-devel [-- Attachment #1: Type: text/plain, Size: 657 bytes --] This patch updates get_user() and put_user() to take a third argument of data type. get_user() and put_user() use target address which are target_ulong and don't reflect the data type pointed to in target memory. Simply casting the target_ulong to a type before passing to get/put_user() is poor because target_ulong isn't always a simple cast to a host type (consider 32 bit on 64 bit where address are either extended or truncate). Also, simple casting of the argument to get/put_user() results in several warnings when target and long pointer sizes don't match. This patch has additional updates to fix places where get/put_user() are already used. [-- Attachment #2: 06_efault.patch.1.3 --] [-- Type: text/x-patch, Size: 10442 bytes --] Index: qemu/linux-user/qemu.h =================================================================== --- qemu.orig/linux-user/qemu.h 2007-10-30 07:56:57.000000000 -0600 +++ qemu/linux-user/qemu.h 2007-10-30 17:55:57.000000000 -0600 @@ -230,26 +231,47 @@ 0;\ }) -#define put_user(x,ptr)\ +/* put_user()/get_user() take a guest address and check access */ +#define put_user(x, gaddr, target_type)\ ({\ int __ret;\ - if (access_ok(VERIFY_WRITE, ptr, sizeof(*ptr)))\ - __ret = __put_user(x, ptr);\ + if (access_ok(VERIFY_WRITE, gaddr, sizeof(target_type)))\ + __ret = __put_user(x, (target_type)g2h(gaddr)); \ else\ - __ret = -EFAULT;\ + __ret = -TARGET_EFAULT;\ __ret;\ }) -#define get_user(x,ptr)\ +#define get_user(x, gaddr, target_type)\ ({\ int __ret;\ - if (access_ok(VERIFY_READ, ptr, sizeof(*ptr)))\ - __ret = __get_user(x, ptr);\ + if (access_ok(VERIFY_READ, gaddr, sizeof(target_type)))\ + __ret = __get_user(x, (target_type)g2h(gaddr)); \ else\ - __ret = -EFAULT;\ + __ret = -TARGET_EFAULT;\ __ret;\ }) +#define copy_from_user(hptr, gaddr, len) \ +({ \ + int __cfu_ret=0; \ + if (access_ok(VERIFY_READ, gaddr, (len))) \ + memcpy(hptr, g2h(gaddr), (len)); \ + else \ + __cfu_ret = -TARGET_EFAULT; \ + __cfu_ret; \ +}) + +#define copy_to_user(gaddr, hptr, len) \ +({ \ + int __ctu_ret=0; \ + if (access_ok(VERIFY_WRITE, gaddr, (len))) \ + memcpy(g2h(gaddr), hptr, (len)); \ + else \ + __ctu_ret = -TARGET_EFAULT; \ + __ctu_ret; \ +}) + /* Functions for accessing guest memory. The tget and tput functions read/write single values, byteswapping as neccessary. The lock_user gets a pointer to a contiguous area of guest memory, but does not perform Index: qemu/linux-user/syscall.c =================================================================== --- qemu.orig/linux-user/syscall.c 2007-10-30 07:56:57.000000000 -0600 +++ qemu/linux-user/syscall.c 2007-10-30 17:56:06.000000000 -0600 @@ -1757,7 +1757,7 @@ break; } } - if (put_user(raddr, (target_ulong *)third)) + if (put_user(raddr, third, target_ulong *)) return -TARGET_EFAULT; ret = 0; break; @@ -3689,17 +3689,16 @@ struct target_statfs *target_stfs; 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; @@ -3716,17 +3715,16 @@ struct target_statfs64 *target_stfs; 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; @@ -4465,24 +4463,22 @@ struct target_eabi_stat64 *target_st; 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 @@ -4490,24 +4486,23 @@ struct target_stat64 *target_st; 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); } } ^ permalink raw reply [flat|nested] 12+ messages in thread
* [Qemu-devel] Re: [PATCH] efault - add data type to put_user()/get_user() 2007-10-31 22:43 ` [Qemu-devel] Re: [PATCH] efault - add data type to put_user()/get_user() Thayne Harbaugh @ 2007-11-02 23:26 ` Thayne Harbaugh 2007-11-03 15:56 ` Thiemo Seufer 2007-11-03 19:05 ` Fabrice Bellard 0 siblings, 2 replies; 12+ messages in thread From: Thayne Harbaugh @ 2007-11-02 23:26 UTC (permalink / raw) To: qemu-devel [-- Attachment #1: Type: text/plain, Size: 852 bytes --] On Wed, 2007-10-31 at 16:44 -0600, Thayne Harbaugh wrote: > This patch updates get_user() and put_user() to take a third argument of > data type. get_user() and put_user() use target address which are > target_ulong and don't reflect the data type pointed to in target > memory. > > Simply casting the target_ulong to a type before passing to > get/put_user() is poor because target_ulong isn't always a simple cast > to a host type (consider 32 bit on 64 bit where address are either > extended or truncate). Also, simple casting of the argument to > get/put_user() results in several warnings when target and long pointer > sizes don't match. > > This patch has additional updates to fix places where get/put_user() are > already used. This is an updated patch that doesn't conflict with the abi_long/abi_ulong changes from a couple weeks ago. [-- Attachment #2: 06_efault.patch.1.3 --] [-- Type: text/x-patch, Size: 10443 bytes --] Index: qemu/linux-user/qemu.h =================================================================== --- qemu.orig/linux-user/qemu.h 2007-10-30 07:56:57.000000000 -0600 +++ qemu/linux-user/qemu.h 2007-10-30 17:55:57.000000000 -0600 @@ -230,26 +231,47 @@ 0;\ }) -#define put_user(x,ptr)\ +/* put_user()/get_user() take a guest address and check access */ +#define put_user(x, gaddr, target_type)\ ({\ int __ret;\ - if (access_ok(VERIFY_WRITE, ptr, sizeof(*ptr)))\ - __ret = __put_user(x, ptr);\ + if (access_ok(VERIFY_WRITE, gaddr, sizeof(target_type)))\ + __ret = __put_user(x, (target_type)g2h(gaddr)); \ else\ - __ret = -EFAULT;\ + __ret = -TARGET_EFAULT;\ __ret;\ }) -#define get_user(x,ptr)\ +#define get_user(x, gaddr, target_type)\ ({\ int __ret;\ - if (access_ok(VERIFY_READ, ptr, sizeof(*ptr)))\ - __ret = __get_user(x, ptr);\ + if (access_ok(VERIFY_READ, gaddr, sizeof(target_type)))\ + __ret = __get_user(x, (target_type)g2h(gaddr)); \ else\ - __ret = -EFAULT;\ + __ret = -TARGET_EFAULT;\ __ret;\ }) +#define copy_from_user(hptr, gaddr, len) \ +({ \ + int __cfu_ret=0; \ + if (access_ok(VERIFY_READ, gaddr, (len))) \ + memcpy(hptr, g2h(gaddr), (len)); \ + else \ + __cfu_ret = -TARGET_EFAULT; \ + __cfu_ret; \ +}) + +#define copy_to_user(gaddr, hptr, len) \ +({ \ + int __ctu_ret=0; \ + if (access_ok(VERIFY_WRITE, gaddr, (len))) \ + memcpy(g2h(gaddr), hptr, (len)); \ + else \ + __ctu_ret = -TARGET_EFAULT; \ + __ctu_ret; \ +}) + /* Functions for accessing guest memory. The tget and tput functions read/write single values, byteswapping as neccessary. The lock_user gets a pointer to a contiguous area of guest memory, but does not perform Index: qemu/linux-user/syscall.c =================================================================== --- qemu.orig/linux-user/syscall.c 2007-10-30 07:56:57.000000000 -0600 +++ qemu/linux-user/syscall.c 2007-10-30 17:56:06.000000000 -0600 @@ -1757,7 +1757,7 @@ break; } } - if (put_user(raddr, (abi_ulong *)third)) + if (put_user(raddr, third, abi_ulong *)) return -TARGET_EFAULT; ret = 0; break; @@ -3689,17 +3689,16 @@ struct target_statfs *target_stfs; 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; @@ -3716,17 +3715,16 @@ struct target_statfs64 *target_stfs; 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; @@ -4465,24 +4463,22 @@ struct target_eabi_stat64 *target_st; 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 @@ -4490,24 +4486,23 @@ struct target_stat64 *target_st; 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); } } ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] efault - add data type to put_user()/get_user() 2007-11-02 23:26 ` Thayne Harbaugh @ 2007-11-03 15:56 ` Thiemo Seufer 2007-11-03 19:05 ` Fabrice Bellard 1 sibling, 0 replies; 12+ messages in thread From: Thiemo Seufer @ 2007-11-03 15:56 UTC (permalink / raw) To: Thayne Harbaugh; +Cc: qemu-devel Thayne Harbaugh wrote: > > On Wed, 2007-10-31 at 16:44 -0600, Thayne Harbaugh wrote: > > This patch updates get_user() and put_user() to take a third argument of > > data type. get_user() and put_user() use target address which are > > target_ulong and don't reflect the data type pointed to in target > > memory. > > > > Simply casting the target_ulong to a type before passing to > > get/put_user() is poor because target_ulong isn't always a simple cast > > to a host type (consider 32 bit on 64 bit where address are either > > extended or truncate). Also, simple casting of the argument to > > get/put_user() results in several warnings when target and long pointer > > sizes don't match. > > > > This patch has additional updates to fix places where get/put_user() are > > already used. > > This is an updated patch that doesn't conflict with the > abi_long/abi_ulong changes from a couple weeks ago. Still misses the sparc64 bits in linux-user/signal.c. Thiemo ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] efault - add data type to put_user()/get_user() 2007-11-02 23:26 ` Thayne Harbaugh 2007-11-03 15:56 ` Thiemo Seufer @ 2007-11-03 19:05 ` Fabrice Bellard 2007-11-05 20:22 ` Thayne Harbaugh 1 sibling, 1 reply; 12+ messages in thread From: Fabrice Bellard @ 2007-11-03 19:05 UTC (permalink / raw) To: thayne, qemu-devel 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. The ultimate goal of such cleanup is not only to generate -EFAULT correctly but also to be able to have arbitrary address space changes. In fact it would be good to be able to introduce an arbitrary address space change (such as a translation as Paul did) so that we can verify that all the Linux emulation stills works in this case. Regards, Fabrice. Thayne Harbaugh wrote: > On Wed, 2007-10-31 at 16:44 -0600, Thayne Harbaugh wrote: >> This patch updates get_user() and put_user() to take a third argument of >> data type. get_user() and put_user() use target address which are >> target_ulong and don't reflect the data type pointed to in target >> memory. >> >> Simply casting the target_ulong to a type before passing to >> get/put_user() is poor because target_ulong isn't always a simple cast >> to a host type (consider 32 bit on 64 bit where address are either >> extended or truncate). Also, simple casting of the argument to >> get/put_user() results in several warnings when target and long pointer >> sizes don't match. >> >> This patch has additional updates to fix places where get/put_user() are >> already used. > > This is an updated patch that doesn't conflict with the > abi_long/abi_ulong changes from a couple weeks ago. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] efault - add data type to put_user()/get_user() 2007-11-03 19:05 ` Fabrice Bellard @ 2007-11-05 20:22 ` Thayne Harbaugh 2007-11-05 20:43 ` Thayne Harbaugh 2007-11-05 21:42 ` Fabrice Bellard 0 siblings, 2 replies; 12+ messages in thread From: Thayne Harbaugh @ 2007-11-05 20:22 UTC (permalink / raw) To: Fabrice Bellard; +Cc: qemu-devel 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. > The ultimate goal of such cleanup is not only to generate -EFAULT > correctly but also to be able to have arbitrary address space changes. Yes. This will be possible once all my clean-ups are pushed. > In fact it would be good to be able to introduce an arbitrary address > space change (such as a translation as Paul did) so that we can verify > that all the Linux emulation stills works in this case. I'll be testing this way. > Regards, > > Fabrice. > > Thayne Harbaugh wrote: > > On Wed, 2007-10-31 at 16:44 -0600, Thayne Harbaugh wrote: > >> This patch updates get_user() and put_user() to take a third argument of > >> data type. get_user() and put_user() use target address which are > >> target_ulong and don't reflect the data type pointed to in target > >> memory. > >> > >> Simply casting the target_ulong to a type before passing to > >> get/put_user() is poor because target_ulong isn't always a simple cast > >> to a host type (consider 32 bit on 64 bit where address are either > >> extended or truncate). Also, simple casting of the argument to > >> get/put_user() results in several warnings when target and long pointer > >> sizes don't match. > >> > >> This patch has additional updates to fix places where get/put_user() are > >> already used. > > > > This is an updated patch that doesn't conflict with the > > abi_long/abi_ulong changes from a couple weeks ago. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] efault - add data type to put_user()/get_user() 2007-11-05 20:22 ` Thayne Harbaugh @ 2007-11-05 20:43 ` Thayne Harbaugh 2007-11-05 21:42 ` Fabrice Bellard 1 sibling, 0 replies; 12+ messages in thread From: Thayne Harbaugh @ 2007-11-05 20:43 UTC (permalink / raw) To: Fabrice Bellard; +Cc: qemu-devel Uhhh, I'm quite uncomfortable now. After sending the emails describing how everything should be done I realized that I had never reworked my base patches. All my higher-level patches are sound, but I never reworked my {get,put}_user() and copy_{to,from}_user() patches to follow the same pattern. Fixes are short coming. Thanks for your patience. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] efault - add data type to put_user()/get_user() 2007-11-05 20:22 ` Thayne Harbaugh 2007-11-05 20:43 ` Thayne Harbaugh @ 2007-11-05 21:42 ` Fabrice Bellard 2007-11-05 23:00 ` Thayne Harbaugh 1 sibling, 1 reply; 12+ messages in thread From: Fabrice Bellard @ 2007-11-05 21:42 UTC (permalink / raw) To: qemu-devel; +Cc: thayne 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. 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). 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; } } } 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. 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. Moreover, it may be questionnable to do the page right check in 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. > [...] Fabrice. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] efault - add data type to put_user()/get_user() 2007-11-05 21:42 ` Fabrice Bellard @ 2007-11-05 23:00 ` Thayne Harbaugh 2007-11-05 23:33 ` Fabrice Bellard 0 siblings, 1 reply; 12+ messages in thread From: Thayne Harbaugh @ 2007-11-05 23:00 UTC (permalink / raw) To: Fabrice Bellard; +Cc: qemu-devel [-- Attachment #1: Type: text/plain, Size: 6100 bytes --] 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. [-- Attachment #2: 06_efault.patch.1.3 --] [-- Type: text/x-patch, Size: 11602 bytes --] 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); } } ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] Re: [PATCH] efault - add data type to put_user()/get_user() 2007-11-05 23:00 ` Thayne Harbaugh @ 2007-11-05 23:33 ` Fabrice Bellard 0 siblings, 0 replies; 12+ messages in thread From: Fabrice Bellard @ 2007-11-05 23:33 UTC (permalink / raw) To: thayne, qemu-devel Thayne Harbaugh wrote: > 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()? Yes. Keep in mind that g2h() is only the simple case in case the target address space is directly addressable. I don't want that the API makes this supposition, so in the general case copy_[to,from]user are the only method you have to exchange data with the user space. >> 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()? Of course. >> 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. copy_to_user() does it. > 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. This is an unrelated problem. If tswapl is not sufficient then another function can be added. > [...] Regards, Fabrice. ^ permalink raw reply [flat|nested] 12+ messages in thread
* [Qemu-devel] Re: [PATCH] efault 2007-10-31 22:30 [Qemu-devel] [PATCH] efault - verify pages are in cache and are read/write Thayne Harbaugh 2007-10-31 22:35 ` [Qemu-devel] Re: [PATCH] efault - update __get_user() __put_user() Thayne Harbaugh @ 2007-10-31 22:49 ` Thayne Harbaugh 1 sibling, 0 replies; 12+ messages in thread From: Thayne Harbaugh @ 2007-10-31 22:49 UTC (permalink / raw) To: qemu-devel These three efault patches are the basis for another 30 patches which do the following: * Correct compiler warnings. * Add coding consistency. * Detect error cases and handle them properly. * Divide syscall.c to closer resemble the Linux kernel for code partitioning and organization. * Add new features and functionality. * Correct errors from previous patches that I've sent 8^) I'd appreciate feed back and comments. When I can make these patches acceptable then I'll be able to send the additional patches and have them meet the same design requirements. Thanks. ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2007-11-05 23:34 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2007-10-31 22:30 [Qemu-devel] [PATCH] efault - verify pages are in cache and are read/write Thayne Harbaugh 2007-10-31 22:35 ` [Qemu-devel] Re: [PATCH] efault - update __get_user() __put_user() Thayne Harbaugh 2007-10-31 22:43 ` [Qemu-devel] Re: [PATCH] efault - add data type to put_user()/get_user() Thayne Harbaugh 2007-11-02 23:26 ` Thayne Harbaugh 2007-11-03 15:56 ` Thiemo Seufer 2007-11-03 19:05 ` Fabrice Bellard 2007-11-05 20:22 ` Thayne Harbaugh 2007-11-05 20:43 ` Thayne Harbaugh 2007-11-05 21:42 ` Fabrice Bellard 2007-11-05 23:00 ` Thayne Harbaugh 2007-11-05 23:33 ` Fabrice Bellard 2007-10-31 22:49 ` [Qemu-devel] Re: [PATCH] efault Thayne Harbaugh
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).