linux-user: Perform more checks on iovec lists
Validate count between 0 and IOV_MAX. Limit total length of operation in the same way the kernel does. Signed-off-by: Richard Henderson <rth@twiddle.net> Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
This commit is contained in:
		
							parent
							
								
									1bdd7c7ea8
								
							
						
					
					
						commit
						f287b2c2d4
					
				@ -1744,55 +1744,96 @@ static abi_long do_getsockopt(int sockfd, int level, int optname,
 | 
				
			|||||||
    return ret;
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* FIXME
 | 
					static struct iovec *lock_iovec(int type, abi_ulong target_addr,
 | 
				
			||||||
 * lock_iovec()/unlock_iovec() have a return code of 0 for success where
 | 
					                                int count, int copy)
 | 
				
			||||||
 * other lock functions have a return code of 0 for failure.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr,
 | 
					 | 
				
			||||||
                           int count, int copy)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    struct target_iovec *target_vec;
 | 
					    struct target_iovec *target_vec;
 | 
				
			||||||
    abi_ulong base;
 | 
					    struct iovec *vec;
 | 
				
			||||||
 | 
					    abi_ulong total_len, max_len;
 | 
				
			||||||
    int i;
 | 
					    int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
 | 
					    if (count == 0) {
 | 
				
			||||||
    if (!target_vec)
 | 
					        errno = 0;
 | 
				
			||||||
        return -TARGET_EFAULT;
 | 
					        return NULL;
 | 
				
			||||||
    for(i = 0;i < count; i++) {
 | 
					 | 
				
			||||||
        base = tswapal(target_vec[i].iov_base);
 | 
					 | 
				
			||||||
        vec[i].iov_len = tswapal(target_vec[i].iov_len);
 | 
					 | 
				
			||||||
        if (vec[i].iov_len != 0) {
 | 
					 | 
				
			||||||
            vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy);
 | 
					 | 
				
			||||||
            /* Don't check lock_user return value. We must call writev even
 | 
					 | 
				
			||||||
               if a element has invalid base address. */
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            /* zero length pointer is ignored */
 | 
					 | 
				
			||||||
            vec[i].iov_base = NULL;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    unlock_user (target_vec, target_addr, 0);
 | 
					    if (count > IOV_MAX) {
 | 
				
			||||||
    return 0;
 | 
					        errno = EINVAL;
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vec = calloc(count, sizeof(struct iovec));
 | 
				
			||||||
 | 
					    if (vec == NULL) {
 | 
				
			||||||
 | 
					        errno = ENOMEM;
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    target_vec = lock_user(VERIFY_READ, target_addr,
 | 
				
			||||||
 | 
					                           count * sizeof(struct target_iovec), 1);
 | 
				
			||||||
 | 
					    if (target_vec == NULL) {
 | 
				
			||||||
 | 
					        errno = EFAULT;
 | 
				
			||||||
 | 
					        goto fail2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* ??? If host page size > target page size, this will result in a
 | 
				
			||||||
 | 
					       value larger than what we can actually support.  */
 | 
				
			||||||
 | 
					    max_len = 0x7fffffff & TARGET_PAGE_MASK;
 | 
				
			||||||
 | 
					    total_len = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (i = 0; i < count; i++) {
 | 
				
			||||||
 | 
					        abi_ulong base = tswapal(target_vec[i].iov_base);
 | 
				
			||||||
 | 
					        abi_long len = tswapal(target_vec[i].iov_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (len < 0) {
 | 
				
			||||||
 | 
					            errno = EINVAL;
 | 
				
			||||||
 | 
					            goto fail;
 | 
				
			||||||
 | 
					        } else if (len == 0) {
 | 
				
			||||||
 | 
					            /* Zero length pointer is ignored.  */
 | 
				
			||||||
 | 
					            vec[i].iov_base = 0;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            vec[i].iov_base = lock_user(type, base, len, copy);
 | 
				
			||||||
 | 
					            if (!vec[i].iov_base) {
 | 
				
			||||||
 | 
					                errno = EFAULT;
 | 
				
			||||||
 | 
					                goto fail;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (len > max_len - total_len) {
 | 
				
			||||||
 | 
					                len = max_len - total_len;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        vec[i].iov_len = len;
 | 
				
			||||||
 | 
					        total_len += len;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unlock_user(target_vec, target_addr, 0);
 | 
				
			||||||
 | 
					    return vec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 fail:
 | 
				
			||||||
 | 
					    free(vec);
 | 
				
			||||||
 | 
					 fail2:
 | 
				
			||||||
 | 
					    unlock_user(target_vec, target_addr, 0);
 | 
				
			||||||
 | 
					    return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr,
 | 
					static void unlock_iovec(struct iovec *vec, abi_ulong target_addr,
 | 
				
			||||||
                             int count, int copy)
 | 
					                         int count, int copy)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    struct target_iovec *target_vec;
 | 
					    struct target_iovec *target_vec;
 | 
				
			||||||
    abi_ulong base;
 | 
					 | 
				
			||||||
    int i;
 | 
					    int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
 | 
					    target_vec = lock_user(VERIFY_READ, target_addr,
 | 
				
			||||||
    if (!target_vec)
 | 
					                           count * sizeof(struct target_iovec), 1);
 | 
				
			||||||
        return -TARGET_EFAULT;
 | 
					    if (target_vec) {
 | 
				
			||||||
    for(i = 0;i < count; i++) {
 | 
					        for (i = 0; i < count; i++) {
 | 
				
			||||||
        if (target_vec[i].iov_base) {
 | 
					            abi_ulong base = tswapal(target_vec[i].iov_base);
 | 
				
			||||||
            base = tswapal(target_vec[i].iov_base);
 | 
					            abi_long len = tswapal(target_vec[i].iov_base);
 | 
				
			||||||
 | 
					            if (len < 0) {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
 | 
					            unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        unlock_user(target_vec, target_addr, 0);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    unlock_user (target_vec, target_addr, 0);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 0;
 | 
					    free(vec);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* do_socket() Must return target values and target errnos. */
 | 
					/* do_socket() Must return target values and target errnos. */
 | 
				
			||||||
@ -1888,8 +1929,7 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
 | 
				
			|||||||
        ret = target_to_host_sockaddr(msg.msg_name, tswapal(msgp->msg_name),
 | 
					        ret = target_to_host_sockaddr(msg.msg_name, tswapal(msgp->msg_name),
 | 
				
			||||||
                                msg.msg_namelen);
 | 
					                                msg.msg_namelen);
 | 
				
			||||||
        if (ret) {
 | 
					        if (ret) {
 | 
				
			||||||
            unlock_user_struct(msgp, target_msg, send ? 0 : 1);
 | 
					            goto out2;
 | 
				
			||||||
            return ret;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        msg.msg_name = NULL;
 | 
					        msg.msg_name = NULL;
 | 
				
			||||||
@ -1900,9 +1940,13 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
 | 
				
			|||||||
    msg.msg_flags = tswap32(msgp->msg_flags);
 | 
					    msg.msg_flags = tswap32(msgp->msg_flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    count = tswapal(msgp->msg_iovlen);
 | 
					    count = tswapal(msgp->msg_iovlen);
 | 
				
			||||||
    vec = alloca(count * sizeof(struct iovec));
 | 
					 | 
				
			||||||
    target_vec = tswapal(msgp->msg_iov);
 | 
					    target_vec = tswapal(msgp->msg_iov);
 | 
				
			||||||
    lock_iovec(send ? VERIFY_READ : VERIFY_WRITE, vec, target_vec, count, send);
 | 
					    vec = lock_iovec(send ? VERIFY_READ : VERIFY_WRITE,
 | 
				
			||||||
 | 
					                     target_vec, count, send);
 | 
				
			||||||
 | 
					    if (vec == NULL) {
 | 
				
			||||||
 | 
					        ret = -host_to_target_errno(errno);
 | 
				
			||||||
 | 
					        goto out2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    msg.msg_iovlen = count;
 | 
					    msg.msg_iovlen = count;
 | 
				
			||||||
    msg.msg_iov = vec;
 | 
					    msg.msg_iov = vec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1932,6 +1976,7 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
    unlock_iovec(vec, target_vec, count, !send);
 | 
					    unlock_iovec(vec, target_vec, count, !send);
 | 
				
			||||||
 | 
					out2:
 | 
				
			||||||
    unlock_user_struct(msgp, target_msg, send ? 0 : 1);
 | 
					    unlock_user_struct(msgp, target_msg, send ? 0 : 1);
 | 
				
			||||||
    return ret;
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -7188,26 +7233,24 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 | 
				
			|||||||
        break;
 | 
					        break;
 | 
				
			||||||
    case TARGET_NR_readv:
 | 
					    case TARGET_NR_readv:
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            int count = arg3;
 | 
					            struct iovec *vec = lock_iovec(VERIFY_WRITE, arg2, arg3, 0);
 | 
				
			||||||
            struct iovec *vec;
 | 
					            if (vec != NULL) {
 | 
				
			||||||
 | 
					                ret = get_errno(readv(arg1, vec, arg3));
 | 
				
			||||||
            vec = alloca(count * sizeof(struct iovec));
 | 
					                unlock_iovec(vec, arg2, arg3, 1);
 | 
				
			||||||
            if (lock_iovec(VERIFY_WRITE, vec, arg2, count, 0) < 0)
 | 
					            } else {
 | 
				
			||||||
                goto efault;
 | 
					                ret = -host_to_target_errno(errno);
 | 
				
			||||||
            ret = get_errno(readv(arg1, vec, count));
 | 
					            }
 | 
				
			||||||
            unlock_iovec(vec, arg2, count, 1);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    case TARGET_NR_writev:
 | 
					    case TARGET_NR_writev:
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            int count = arg3;
 | 
					            struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1);
 | 
				
			||||||
            struct iovec *vec;
 | 
					            if (vec != NULL) {
 | 
				
			||||||
 | 
					                ret = get_errno(writev(arg1, vec, arg3));
 | 
				
			||||||
            vec = alloca(count * sizeof(struct iovec));
 | 
					                unlock_iovec(vec, arg2, arg3, 0);
 | 
				
			||||||
            if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
 | 
					            } else {
 | 
				
			||||||
                goto efault;
 | 
					                ret = -host_to_target_errno(errno);
 | 
				
			||||||
            ret = get_errno(writev(arg1, vec, count));
 | 
					            }
 | 
				
			||||||
            unlock_iovec(vec, arg2, count, 0);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    case TARGET_NR_getsid:
 | 
					    case TARGET_NR_getsid:
 | 
				
			||||||
@ -8632,14 +8675,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 | 
				
			|||||||
#ifdef TARGET_NR_vmsplice
 | 
					#ifdef TARGET_NR_vmsplice
 | 
				
			||||||
	case TARGET_NR_vmsplice:
 | 
						case TARGET_NR_vmsplice:
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            int count = arg3;
 | 
					            struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1);
 | 
				
			||||||
            struct iovec *vec;
 | 
					            if (vec != NULL) {
 | 
				
			||||||
 | 
					                ret = get_errno(vmsplice(arg1, vec, arg3, arg4));
 | 
				
			||||||
            vec = alloca(count * sizeof(struct iovec));
 | 
					                unlock_iovec(vec, arg2, arg3, 0);
 | 
				
			||||||
            if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
 | 
					            } else {
 | 
				
			||||||
                goto efault;
 | 
					                ret = -host_to_target_errno(errno);
 | 
				
			||||||
            ret = get_errno(vmsplice(arg1, vec, count, arg4));
 | 
					            }
 | 
				
			||||||
            unlock_iovec(vec, arg2, count, 0);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user