Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Fix: compat_rw_copy_check_uvector() misuse in aio, readv, writev, and security keys

Looking at mm/process_vm_access.c:process_vm_rw() and comparing it to
compat_process_vm_rw() shows that the compatibility code requires an
explicit "access_ok()" check before calling
compat_rw_copy_check_uvector(). The same difference seems to appear when
we compare fs/read_write.c:do_readv_writev() to
fs/compat.c:compat_do_readv_writev().

This subtle difference between the compat and non-compat requirements
should probably be debated, as it seems to be error-prone. In fact,
there are two others sites that use this function in the Linux kernel,
and they both seem to get it wrong:

Now shifting our attention to fs/aio.c, we see that aio_setup_iocb()
also ends up calling compat_rw_copy_check_uvector() through
aio_setup_vectored_rw(). Unfortunately, the access_ok() check appears to
be missing. Same situation for
security/keys/compat.c:compat_keyctl_instantiate_key_iov().

I propose that we add the access_ok() check directly into
compat_rw_copy_check_uvector(), so callers don't have to worry about it,
and it therefore makes the compat call code similar to its non-compat
counterpart. Place the access_ok() check in the same location where
copy_from_user() can trigger a -EFAULT error in the non-compat code, so
the ABI behaviors are alike on both compat and non-compat.

While we are here, fix compat_do_readv_writev() so it checks for
compat_rw_copy_check_uvector() negative return values.

And also, fix a memory leak in compat_keyctl_instantiate_key_iov() error
handling.

Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Mathieu Desnoyers and committed by
Linus Torvalds
8aec0f5d c39ac49f

+9 -18
+7 -8
fs/compat.c
··· 558 558 } 559 559 *ret_pointer = iov; 560 560 561 + ret = -EFAULT; 562 + if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector))) 563 + goto out; 564 + 561 565 /* 562 566 * Single unix specification: 563 567 * We should -EINVAL if an element length is not >= 0 and fitting an ··· 1084 1080 if (!file->f_op) 1085 1081 goto out; 1086 1082 1087 - ret = -EFAULT; 1088 - if (!access_ok(VERIFY_READ, uvector, nr_segs*sizeof(*uvector))) 1089 - goto out; 1090 - 1091 - tot_len = compat_rw_copy_check_uvector(type, uvector, nr_segs, 1083 + ret = compat_rw_copy_check_uvector(type, uvector, nr_segs, 1092 1084 UIO_FASTIOV, iovstack, &iov); 1093 - if (tot_len == 0) { 1094 - ret = 0; 1085 + if (ret <= 0) 1095 1086 goto out; 1096 - } 1097 1087 1088 + tot_len = ret; 1098 1089 ret = rw_verify_area(type, file, pos, tot_len); 1099 1090 if (ret < 0) 1100 1091 goto out;
-8
mm/process_vm_access.c
··· 429 429 if (flags != 0) 430 430 return -EINVAL; 431 431 432 - if (!access_ok(VERIFY_READ, lvec, liovcnt * sizeof(*lvec))) 433 - goto out; 434 - 435 - if (!access_ok(VERIFY_READ, rvec, riovcnt * sizeof(*rvec))) 436 - goto out; 437 - 438 432 if (vm_write) 439 433 rc = compat_rw_copy_check_uvector(WRITE, lvec, liovcnt, 440 434 UIO_FASTIOV, iovstack_l, ··· 453 459 kfree(iov_r); 454 460 if (iov_l != iovstack_l) 455 461 kfree(iov_l); 456 - 457 - out: 458 462 return rc; 459 463 } 460 464
+2 -2
security/keys/compat.c
··· 40 40 ARRAY_SIZE(iovstack), 41 41 iovstack, &iov); 42 42 if (ret < 0) 43 - return ret; 43 + goto err; 44 44 if (ret == 0) 45 45 goto no_payload_free; 46 46 47 47 ret = keyctl_instantiate_key_common(id, iov, ioc, ret, ringid); 48 - 48 + err: 49 49 if (iov != iovstack) 50 50 kfree(iov); 51 51 return ret;