Index: qemu/linux-user/syscall.c =================================================================== --- qemu.orig/linux-user/syscall.c 2007-09-16 15:56:36.000000000 -0400 +++ qemu/linux-user/syscall.c 2007-09-16 15:56:37.000000000 -0400 @@ -1289,64 +1289,50 @@ target_ulong __unused4; }; -static inline void target_to_host_ipc_perm(struct ipc_perm *host_ip, - target_ulong target_addr) +static inline void copy_from_user_ipc_perm(struct ipc_perm *host_ip, + struct target_ipc_perm *target_ip) { - struct target_ipc_perm *target_ip; - struct target_semid_ds *target_sd; - - lock_user_struct(target_sd, target_addr, 1); - target_ip=&(target_sd->sem_perm); - host_ip->__key = tswapl(target_ip->__key); - host_ip->uid = tswapl(target_ip->uid); - host_ip->gid = tswapl(target_ip->gid); - host_ip->cuid = tswapl(target_ip->cuid); - host_ip->cgid = tswapl(target_ip->cgid); - host_ip->mode = tswapl(target_ip->mode); - unlock_user_struct(target_sd, target_addr, 0); + /* access has already been checked */ + __get_user(host_ip->__key, &target_ip->__key); + __get_user(host_ip->uid, &target_ip->uid); + __get_user(host_ip->gid, &target_ip->gid); + __get_user(host_ip->cuid, &target_ip->cuid); + __get_user(host_ip->cgid, &target_ip->cgid); + __get_user(host_ip->mode, &target_ip->mode); + __get_user(host_ip->__seq, &target_ip->__seq); } -static inline void host_to_target_ipc_perm(target_ulong target_addr, +static inline void copy_to_user_ipc_perm(struct target_ipc_perm *target_ip, struct ipc_perm *host_ip) { - struct target_ipc_perm *target_ip; - struct target_semid_ds *target_sd; - - lock_user_struct(target_sd, target_addr, 0); - target_ip = &(target_sd->sem_perm); - target_ip->__key = tswapl(host_ip->__key); - target_ip->uid = tswapl(host_ip->uid); - target_ip->gid = tswapl(host_ip->gid); - target_ip->cuid = tswapl(host_ip->cuid); - target_ip->cgid = tswapl(host_ip->cgid); - target_ip->mode = tswapl(host_ip->mode); - unlock_user_struct(target_sd, target_addr, 1); -} - -static inline void target_to_host_semid_ds(struct semid_ds *host_sd, - target_ulong target_addr) -{ - struct target_semid_ds *target_sd; - - lock_user_struct(target_sd, target_addr, 1); - target_to_host_ipc_perm(&(host_sd->sem_perm),target_addr); - host_sd->sem_nsems = tswapl(target_sd->sem_nsems); - host_sd->sem_otime = tswapl(target_sd->sem_otime); - host_sd->sem_ctime = tswapl(target_sd->sem_ctime); - unlock_user_struct(target_sd, target_addr, 0); + /* access has already been checked */ + __put_user(host_ip->__key, &target_ip->__key); + __put_user(host_ip->uid, &target_ip->uid); + __put_user(host_ip->gid, &target_ip->gid); + __put_user(host_ip->cuid, &target_ip->cuid); + __put_user(host_ip->cgid, &target_ip->cgid); + __put_user(host_ip->mode, &target_ip->mode); + __put_user(host_ip->__seq, &target_ip->__seq); +} + +static inline void copy_from_user_semid_ds(struct semid_ds *host_sd, + struct target_semid_ds *target_sd) +{ + /* access has already been checked */ + copy_from_user_ipc_perm(&(host_sd->sem_perm),&(target_sd->sem_perm)); + __get_user(host_sd->sem_nsems, &target_sd->sem_nsems); + __get_user(host_sd->sem_otime, &target_sd->sem_otime); + __get_user(host_sd->sem_ctime, &target_sd->sem_ctime); } -static inline void host_to_target_semid_ds(target_ulong target_addr, +static inline void copy_to_user_semid_ds(struct target_semid_ds *target_sd, struct semid_ds *host_sd) { - struct target_semid_ds *target_sd; - - lock_user_struct(target_sd, target_addr, 0); - host_to_target_ipc_perm(target_addr,&(host_sd->sem_perm)); - target_sd->sem_nsems = tswapl(host_sd->sem_nsems); - target_sd->sem_otime = tswapl(host_sd->sem_otime); - target_sd->sem_ctime = tswapl(host_sd->sem_ctime); - unlock_user_struct(target_sd, target_addr, 1); + /* access has already been checked */ + copy_to_user_ipc_perm(&(target_sd->sem_perm),&(host_sd->sem_perm)); + __put_user(host_sd->sem_nsems, &target_sd->sem_nsems); + __put_user(host_sd->sem_otime, &target_sd->sem_otime); + __put_user(host_sd->sem_ctime, &target_sd->sem_ctime); } union semun { @@ -1357,113 +1343,108 @@ union target_semun { int val; - target_long buf; - unsigned short int *array; + struct target_semid_ds *buf; + unsigned short *array; + long host_ptr; }; -static inline void target_to_host_semun(unsigned long cmd, - union semun *host_su, - target_ulong target_addr, +static inline long copy_from_user_semun(unsigned long cmd, + union semun host_su, + union target_semun target_su, struct semid_ds *ds) { - union target_semun *target_su; - switch( cmd ) { - case IPC_STAT: case IPC_SET: - lock_user_struct(target_su, target_addr, 1); - target_to_host_semid_ds(ds,target_su->buf); - host_su->buf = ds; - unlock_user_struct(target_su, target_addr, 0); + if( !access_ok(VERIFY_READ,(target_ulong)target_su.buf,sizeof(struct target_semid_ds)) ) return -1; + copy_from_user_semid_ds(ds,target_su.buf); + host_su.buf = ds; break; - case GETVAL: - case SETVAL: - lock_user_struct(target_su, target_addr, 1); - host_su->val = tswapl(target_su->val); - unlock_user_struct(target_su, target_addr, 0); - break; - case GETALL: case SETALL: - lock_user_struct(target_su, target_addr, 1); - *host_su->array = tswap16(*target_su->array); - unlock_user_struct(target_su, target_addr, 0); + { + int i; + for(i=0;isem_nsems;i++) + if (get_user(host_su.array[i],&(target_su.array[i]))) return -1; break; + } default: gemu_log("semun operation not fully supported: %d\n", (int)cmd); } + + return 0; } -static inline void host_to_target_semun(unsigned long cmd, - target_ulong target_addr, - union semun *host_su, +static inline long copy_to_user_semun(unsigned long cmd, + union target_semun target_su, + union semun host_su, struct semid_ds *ds) { - union target_semun *target_su; - switch( cmd ) { case IPC_STAT: - case IPC_SET: - lock_user_struct(target_su, target_addr, 0); - host_to_target_semid_ds(target_su->buf,ds); - unlock_user_struct(target_su, target_addr, 1); - break; - case GETVAL: - case SETVAL: - lock_user_struct(target_su, target_addr, 0); - target_su->val = tswapl(host_su->val); - unlock_user_struct(target_su, target_addr, 1); + if( !access_ok(VERIFY_WRITE,(target_ulong)target_su.buf,sizeof(struct target_semid_ds)) ) return -1; + copy_to_user_semid_ds(target_su.buf,ds); break; case GETALL: - case SETALL: - lock_user_struct(target_su, target_addr, 0); - *target_su->array = tswap16(*host_su->array); - unlock_user_struct(target_su, target_addr, 1); + { + int i; + for(i=0;isem_nsems;i++) + if (put_user(host_su.array[i],&(target_su.array[i]))) return -1; break; + } default: gemu_log("semun operation not fully supported: %d\n", (int)cmd); } + + return 0; } static inline long do_semctl(long first, long second, long third, long ptr) { union semun arg; + union target_semun targ = (union target_semun)ptr; struct semid_ds dsarg; int cmd = third&0xff; long ret = 0; + arg.buf = &dsarg; + switch( cmd ) { case GETVAL: - target_to_host_semun(cmd,&arg,ptr,&dsarg); - ret = get_errno(semctl(first, second, cmd, arg)); - host_to_target_semun(cmd,ptr,&arg,&dsarg); + ret = get_errno(semctl(first, second, cmd, targ)); break; case SETVAL: - target_to_host_semun(cmd,&arg,ptr,&dsarg); - ret = get_errno(semctl(first, second, cmd, arg)); - host_to_target_semun(cmd,ptr,&arg,&dsarg); + ret = get_errno(semctl(first, second, cmd, targ.val)); break; case GETALL: - target_to_host_semun(cmd,&arg,ptr,&dsarg); + /* Use IPC_STAT to figure out how many semaphores are in + * the set, and allocate buffer space for them. + */ + semctl(first,second, IPC_STAT,arg); + arg.array = alloca(sizeof(unsigned short)*dsarg.sem_nsems); ret = get_errno(semctl(first, second, cmd, arg)); - host_to_target_semun(cmd,ptr,&arg,&dsarg); + if (!is_error(ret)) { + if( copy_to_user_semun(cmd,targ,arg,&dsarg) ) return -EFAULT; + } break; case SETALL: - target_to_host_semun(cmd,&arg,ptr,&dsarg); + /* Use IPC_STAT to figure out how many semaphores are in + * the set, and allocate buffer space for them. + */ + semctl(first,second, IPC_STAT,arg); + arg.array = alloca(sizeof(unsigned short)*dsarg.sem_nsems); + if( copy_from_user_semun(cmd,arg,targ,&dsarg) ) return -EFAULT; ret = get_errno(semctl(first, second, cmd, arg)); - host_to_target_semun(cmd,ptr,&arg,&dsarg); break; case IPC_STAT: - target_to_host_semun(cmd,&arg,ptr,&dsarg); ret = get_errno(semctl(first, second, cmd, arg)); - host_to_target_semun(cmd,ptr,&arg,&dsarg); + if( copy_to_user_semun(cmd,targ,arg,&dsarg) ) return -EFAULT; break; case IPC_SET: - target_to_host_semun(cmd,&arg,ptr,&dsarg); + if( copy_from_user(arg.buf,targ.buf,sizeof(union target_semun)) ) return -EFAULT; + if( copy_from_user_semun(cmd,arg,targ,&dsarg) ) return -EFAULT; ret = get_errno(semctl(first, second, cmd, arg)); - host_to_target_semun(cmd,ptr,&arg,&dsarg); break; default: - ret = get_errno(semctl(first, second, cmd, arg)); + ret = get_errno(semctl(first, second, cmd, ptr)); } return ret; @@ -1493,7 +1474,7 @@ struct target_msqid_ds *target_md; lock_user_struct(target_md, target_addr, 1); - target_to_host_ipc_perm(&(host_md->msg_perm),target_addr); + copy_from_user_ipc_perm(&(host_md->msg_perm),target_addr); host_md->msg_stime = tswapl(target_md->msg_stime); host_md->msg_rtime = tswapl(target_md->msg_rtime); host_md->msg_ctime = tswapl(target_md->msg_ctime); @@ -1511,7 +1492,7 @@ struct target_msqid_ds *target_md; lock_user_struct(target_md, target_addr, 0); - host_to_target_ipc_perm(target_addr,&(host_md->msg_perm)); + copy_to_user_ipc_perm(target_addr,&(host_md->msg_perm)); target_md->msg_stime = tswapl(host_md->msg_stime); target_md->msg_rtime = tswapl(host_md->msg_rtime); target_md->msg_ctime = tswapl(host_md->msg_ctime);