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;
|
||||
}
|
||||
|
||||
/* FIXME
|
||||
* lock_iovec()/unlock_iovec() have a return code of 0 for success where
|
||||
* 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)
|
||||
static struct iovec *lock_iovec(int type, abi_ulong target_addr,
|
||||
int count, int copy)
|
||||
{
|
||||
struct target_iovec *target_vec;
|
||||
abi_ulong base;
|
||||
struct iovec *vec;
|
||||
abi_ulong total_len, max_len;
|
||||
int i;
|
||||
|
||||
target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
|
||||
if (!target_vec)
|
||||
return -TARGET_EFAULT;
|
||||
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;
|
||||
}
|
||||
if (count == 0) {
|
||||
errno = 0;
|
||||
return NULL;
|
||||
}
|
||||
unlock_user (target_vec, target_addr, 0);
|
||||
return 0;
|
||||
if (count > IOV_MAX) {
|
||||
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,
|
||||
int count, int copy)
|
||||
static void unlock_iovec(struct iovec *vec, abi_ulong target_addr,
|
||||
int count, int copy)
|
||||
{
|
||||
struct target_iovec *target_vec;
|
||||
abi_ulong base;
|
||||
int i;
|
||||
|
||||
target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
|
||||
if (!target_vec)
|
||||
return -TARGET_EFAULT;
|
||||
for(i = 0;i < count; i++) {
|
||||
if (target_vec[i].iov_base) {
|
||||
base = tswapal(target_vec[i].iov_base);
|
||||
target_vec = lock_user(VERIFY_READ, target_addr,
|
||||
count * sizeof(struct target_iovec), 1);
|
||||
if (target_vec) {
|
||||
for (i = 0; i < count; i++) {
|
||||
abi_ulong 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(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. */
|
||||
@ -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),
|
||||
msg.msg_namelen);
|
||||
if (ret) {
|
||||
unlock_user_struct(msgp, target_msg, send ? 0 : 1);
|
||||
return ret;
|
||||
goto out2;
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
|
||||
count = tswapal(msgp->msg_iovlen);
|
||||
vec = alloca(count * sizeof(struct iovec));
|
||||
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_iov = vec;
|
||||
|
||||
@ -1932,6 +1976,7 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
|
||||
|
||||
out:
|
||||
unlock_iovec(vec, target_vec, count, !send);
|
||||
out2:
|
||||
unlock_user_struct(msgp, target_msg, send ? 0 : 1);
|
||||
return ret;
|
||||
}
|
||||
@ -7188,26 +7233,24 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
break;
|
||||
case TARGET_NR_readv:
|
||||
{
|
||||
int count = arg3;
|
||||
struct iovec *vec;
|
||||
|
||||
vec = alloca(count * sizeof(struct iovec));
|
||||
if (lock_iovec(VERIFY_WRITE, vec, arg2, count, 0) < 0)
|
||||
goto efault;
|
||||
ret = get_errno(readv(arg1, vec, count));
|
||||
unlock_iovec(vec, arg2, count, 1);
|
||||
struct iovec *vec = lock_iovec(VERIFY_WRITE, arg2, arg3, 0);
|
||||
if (vec != NULL) {
|
||||
ret = get_errno(readv(arg1, vec, arg3));
|
||||
unlock_iovec(vec, arg2, arg3, 1);
|
||||
} else {
|
||||
ret = -host_to_target_errno(errno);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TARGET_NR_writev:
|
||||
{
|
||||
int count = arg3;
|
||||
struct iovec *vec;
|
||||
|
||||
vec = alloca(count * sizeof(struct iovec));
|
||||
if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
|
||||
goto efault;
|
||||
ret = get_errno(writev(arg1, vec, count));
|
||||
unlock_iovec(vec, arg2, count, 0);
|
||||
struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1);
|
||||
if (vec != NULL) {
|
||||
ret = get_errno(writev(arg1, vec, arg3));
|
||||
unlock_iovec(vec, arg2, arg3, 0);
|
||||
} else {
|
||||
ret = -host_to_target_errno(errno);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TARGET_NR_getsid:
|
||||
@ -8632,14 +8675,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
#ifdef TARGET_NR_vmsplice
|
||||
case TARGET_NR_vmsplice:
|
||||
{
|
||||
int count = arg3;
|
||||
struct iovec *vec;
|
||||
|
||||
vec = alloca(count * sizeof(struct iovec));
|
||||
if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
|
||||
goto efault;
|
||||
ret = get_errno(vmsplice(arg1, vec, count, arg4));
|
||||
unlock_iovec(vec, arg2, count, 0);
|
||||
struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1);
|
||||
if (vec != NULL) {
|
||||
ret = get_errno(vmsplice(arg1, vec, arg3, arg4));
|
||||
unlock_iovec(vec, arg2, arg3, 0);
|
||||
} else {
|
||||
ret = -host_to_target_errno(errno);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user