summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux-user/syscall.c16
1 files changed, 14 insertions, 2 deletions
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 1da216e2fb..ebdc70e4ca 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -1803,6 +1803,7 @@ static struct iovec *lock_iovec(int type, abi_ulong target_addr,
abi_ulong total_len, max_len;
int i;
int err = 0;
+ bool bad_address = false;
if (count == 0) {
errno = 0;
@@ -1843,9 +1844,20 @@ static struct iovec *lock_iovec(int type, abi_ulong target_addr,
vec[i].iov_base = 0;
} else {
vec[i].iov_base = lock_user(type, base, len, copy);
+ /* If the first buffer pointer is bad, this is a fault. But
+ * subsequent bad buffers will result in a partial write; this
+ * is realized by filling the vector with null pointers and
+ * zero lengths. */
if (!vec[i].iov_base) {
- err = EFAULT;
- goto fail;
+ if (i == 0) {
+ err = EFAULT;
+ goto fail;
+ } else {
+ bad_address = true;
+ }
+ }
+ if (bad_address) {
+ len = 0;
}
if (len > max_len - total_len) {
len = max_len - total_len;