Do proper copyin of control message data in the Linux sendmsg syscall.

Instead of calling m_append with a user address, allocate an mbuf cluster
and copy data into it using copyin.  For the SCM_CREDS case, instead of
zeroing a stack variable and appending that to the mbuf, zero part of the
mbuf cluster directly.  One mbuf cluster is also the size limit used by
the FreeBSD sendmsg syscall (uipc_syscalls.c:sockargs()).

PR:		217901
Reviewed by:	kib
MFC after:	3 days
This commit is contained in:
Tijl Coosemans 2018-11-19 15:31:54 +00:00
parent 90acd1d139
commit e3b385fc95
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=340631

View File

@ -1085,7 +1085,6 @@ linux_sendmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
l_uint flags)
{
struct cmsghdr *cmsg;
struct cmsgcred cmcred;
struct mbuf *control;
struct msghdr msg;
struct l_cmsghdr linux_cmsg;
@ -1096,6 +1095,7 @@ linux_sendmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
struct sockaddr *sa;
sa_family_t sa_family;
void *data;
l_size_t len;
int error;
error = copyin(msghdr, &linux_msg, sizeof(linux_msg));
@ -1126,7 +1126,6 @@ linux_sendmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
return (error);
control = NULL;
cmsg = NULL;
if ((ptr_cmsg = LINUX_CMSG_FIRSTHDR(&linux_msg)) != NULL) {
error = kern_getsockname(td, s, &sa, &datalen);
@ -1136,8 +1135,10 @@ linux_sendmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
free(sa, M_SONAME);
error = ENOBUFS;
cmsg = malloc(CMSG_HDRSZ, M_LINUX, M_WAITOK|M_ZERO);
control = m_get(M_WAITOK, MT_CONTROL);
MCLGET(control, M_WAITOK);
data = mtod(control, void *);
datalen = 0;
do {
error = copyin(ptr_cmsg, &linux_cmsg,
@ -1149,10 +1150,14 @@ linux_sendmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
if (linux_cmsg.cmsg_len < sizeof(struct l_cmsghdr))
goto bad;
if (datalen + CMSG_HDRSZ > MCLBYTES)
goto bad;
/*
* Now we support only SCM_RIGHTS and SCM_CRED,
* so return EINVAL in any other cmsg_type
*/
cmsg = data;
cmsg->cmsg_type =
linux_to_bsd_cmsg_type(linux_cmsg.cmsg_type);
cmsg->cmsg_level =
@ -1170,35 +1175,34 @@ linux_sendmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
if (sa_family != AF_UNIX)
continue;
data = LINUX_CMSG_DATA(ptr_cmsg);
datalen = linux_cmsg.cmsg_len - L_CMSG_HDRSZ;
switch (cmsg->cmsg_type)
{
case SCM_RIGHTS:
break;
case SCM_CREDS:
data = &cmcred;
datalen = sizeof(cmcred);
if (cmsg->cmsg_type == SCM_CREDS) {
len = sizeof(struct cmsgcred);
if (datalen + CMSG_SPACE(len) > MCLBYTES)
goto bad;
/*
* The lower levels will fill in the structure
*/
bzero(data, datalen);
break;
memset(CMSG_DATA(data), 0, len);
} else {
len = linux_cmsg.cmsg_len - L_CMSG_HDRSZ;
if (datalen + CMSG_SPACE(len) < datalen ||
datalen + CMSG_SPACE(len) > MCLBYTES)
goto bad;
error = copyin(LINUX_CMSG_DATA(ptr_cmsg),
CMSG_DATA(data), len);
if (error != 0)
goto bad;
}
cmsg->cmsg_len = CMSG_LEN(datalen);
error = ENOBUFS;
if (!m_append(control, CMSG_HDRSZ, (c_caddr_t)cmsg))
goto bad;
if (!m_append(control, datalen, (c_caddr_t)data))
goto bad;
cmsg->cmsg_len = CMSG_LEN(len);
data = (char *)data + CMSG_SPACE(len);
datalen += CMSG_SPACE(len);
} while ((ptr_cmsg = LINUX_CMSG_NXTHDR(&linux_msg, ptr_cmsg)));
if (m_length(control, NULL) == 0) {
control->m_len = datalen;
if (datalen == 0) {
m_freem(control);
control = NULL;
}
@ -1212,8 +1216,6 @@ linux_sendmsg_common(struct thread *td, l_int s, struct l_msghdr *msghdr,
bad:
m_freem(control);
free(iov, M_IOV);
if (cmsg)
free(cmsg, M_LINUX);
return (error);
}