freebsd32: Fix a double copyin in sendmsg() and recvmsg()

freebsd32_sendmsg() and freebsd32_recvmsg() both copyin the message
header twice, once directly and once in freebsd32_copyinmsghdr().  The
iovec length from the former is used when copying in msg_iov, but the
rest of the kernel uses the iovec length from the latter.  When
kern_sendit() and kern_recvit() iterate over the iovec to compute the
residual for I/O, they can therefore end up walking past the end of the
copied in iovec, either resulting in a system call error, userspace
memory corruption from uiomove() with invalid iovecs, or a kernel page
fault if the copied-in iovec is followed by an unmapped KVA region.

Reported by:	syzbot+7cc64cd0c49605acd421@syzkaller.appspotmail.com
Reviewed by:	kib, emaste
MFC after:	1 week
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D32010
This commit is contained in:
Mark Johnston 2021-09-19 13:45:09 -04:00
parent 4bda16ff18
commit fea1a98ead

View File

@ -1414,19 +1414,15 @@ int
freebsd32_recvmsg(struct thread *td, struct freebsd32_recvmsg_args *uap)
{
struct msghdr msg;
struct msghdr32 m32;
struct iovec *uiov, *iov;
struct mbuf *control = NULL;
struct mbuf **controlp;
int error;
error = copyin(uap->msg, &m32, sizeof(m32));
if (error)
return (error);
error = freebsd32_copyinmsghdr(uap->msg, &msg);
if (error)
return (error);
error = freebsd32_copyiniov(PTRIN(m32.msg_iov), m32.msg_iovlen, &iov,
error = freebsd32_copyiniov((void *)msg.msg_iov, msg.msg_iovlen, &iov,
EMSGSIZE);
if (error)
return (error);
@ -1559,19 +1555,15 @@ int
freebsd32_sendmsg(struct thread *td, struct freebsd32_sendmsg_args *uap)
{
struct msghdr msg;
struct msghdr32 m32;
struct iovec *iov;
struct mbuf *control = NULL;
struct sockaddr *to = NULL;
int error;
error = copyin(uap->msg, &m32, sizeof(m32));
if (error)
return (error);
error = freebsd32_copyinmsghdr(uap->msg, &msg);
if (error)
return (error);
error = freebsd32_copyiniov(PTRIN(m32.msg_iov), m32.msg_iovlen, &iov,
error = freebsd32_copyiniov((void *)msg.msg_iov, msg.msg_iovlen, &iov,
EMSGSIZE);
if (error)
return (error);