From: Philippe Troin <phil@fifi.org>
To: linux-kernel@vger.kernel.org
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Subject: 2.2.x BUG & PATCH: recvmsg() does not check msg_controllen correctly
Date: 03 Nov 2000 12:45:29 -0800 [thread overview]
Message-ID: <87n1fgvl7a.fsf@tantale.fifi.org> (raw)
[-- Attachment #1: Type: text/plain, Size: 391 bytes --]
I found this in all 2.2.x kernels, and it might possibly be present in
2.4.x too...
When receiving file descriptors via recvmsg(), scm_detach_fds() in
net/core/scm.c can overflow user space data at msg_control if
msg_controllen is less than sizeof(struct cmsghdr).
This is a security problem.
Attached is a patch to fix the problem and a little program to
demonstrate the problem.
Phil.
[-- Attachment #2: linux-2.2.17-8-scmrights.patch --]
[-- Type: application/octet-stream, Size: 707 bytes --]
diff -ruN linux.orig/net/core/scm.c linux/net/core/scm.c
--- linux.orig/net/core/scm.c Thu Apr 22 19:45:19 1999
+++ linux/net/core/scm.c Sun Oct 29 15:13:14 2000
@@ -216,6 +216,13 @@
int *cmfptr;
int err = 0, i;
+ /* Handle the case when we don't even have enough room for the header.
+ Fixed Oct 29 2000 by Philippe Troin <phil@fifi.org> */
+ if (msg->msg_controllen < sizeof(struct cmsghdr)) {
+ msg->msg_flags |= MSG_CTRUNC;
+ goto out;
+ }
+
if (fdnum < fdmax)
fdmax = fdnum;
@@ -258,6 +265,7 @@
* All of the files that fit in the message have had their
* usage counts incremented, so we just free the list.
*/
+out:
__scm_destroy(scm);
}
[-- Attachment #3: check-anc.c --]
[-- Type: application/octet-stream, Size: 3452 bytes --]
/*
* Check that fd does not get created when the ancillary data buffer
* gets truncated...
* Philippe Troin <phil@fifi.org>
* This snippet is licensed under the GNU General Public License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
void
child_proc(int sock)
{
struct msghdr msg;
int zero;
int devnull;
char ancdata[CMSG_SPACE(sizeof(devnull))];
struct iovec iov[1];
struct cmsghdr *cmsg;
/**/
zero = 0;
if ((devnull = open("/dev/null", O_RDWR))<0)
perror("child: open /dev/null"), exit(1);
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = ancdata;
msg.msg_controllen = sizeof(ancdata);
msg.msg_flags = 0;
iov[0].iov_base = &zero;
iov[0].iov_len = sizeof(zero);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(devnull));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*((int*)CMSG_DATA(cmsg)) = devnull;
if (sendmsg(sock, &msg, 0)<0)
perror("child: sendmsg"), exit(1);
}
void
parent_proc(int sock)
{
struct msghdr msg;
int data;
const char padvalue[]="f00bar";
union {
struct {
char ancdata[2];
char padding[6];
} a;
char safety[CMSG_SPACE(sizeof(int))];
} ancs;
struct iovec iov[1];
/**/
if (sock != 3)
fprintf(stderr, "expected parent socket at fd 3, got %d\n", sock),
exit(1);
if (close(4)>=0 || errno != EBADF)
fprintf(stderr, "already got a descriptor at fd 4, why ?\n"),
exit(1);
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = ancs.a.ancdata;
msg.msg_controllen = sizeof(ancs.a.ancdata);
msg.msg_flags = 0;
iov[0].iov_base = &data;
iov[0].iov_len = sizeof(data);
memset(&ancs, 0, sizeof(ancs));
memcpy(ancs.a.padding, padvalue, strlen(padvalue));
fprintf(stderr, "calling recvmsg with controllen = %d\n",
msg.msg_controllen);
if (recvmsg(sock, &msg, 0)<0)
perror("parent: recvmsg"), exit(1);
fprintf(stderr, "recvmsg returned with controllen = %d\n",
msg.msg_controllen);
if (memcmp(ancs.a.padding, padvalue, strlen(padvalue)) != 0)
fprintf(stderr, "recvmsg smashed the stack !\n");
if (close(4)>=0 || errno != EBADF)
fprintf(stderr, "fd 4 was passed, even it had no room to be put in !\n");
}
int
main(int argc, char *argv[])
{
int fds[2];
pid_t childpid;
/**/
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, fds)<0)
perror("socketpair"), exit(1);
if ((childpid=fork())<0)
perror("fork"), exit(1);
if (childpid==0)
{
/* Child */
if (close(fds[0])<0)
perror("child: close"), exit(1);
child_proc(fds[1]);
}
else
{
/* Parent */
int status;
/**/
if (close(fds[1])<0)
perror("parent: close"), exit(1);
parent_proc(fds[0]);
if (waitpid(childpid, &status, 0)<0)
perror("parent: waitpid"), exit(1);
if (status != 0)
fprintf(stderr, "child exited with status %d\n", status), exit(1);
}
exit(0);
}
next reply other threads:[~2000-11-03 20:46 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2000-11-03 20:45 Philippe Troin [this message]
2000-11-03 22:18 ` 2.2.x BUG & PATCH: recvmsg() does not check msg_controllen correctly David S. Miller
2000-11-04 0:17 ` Philippe Troin
2000-11-04 0:38 ` David S. Miller
2000-11-04 3:53 ` Philippe Troin
2000-11-04 4:51 ` David S. Miller
2000-11-05 2:40 ` Philippe Troin
2000-11-06 3:32 ` David S. Miller
2000-11-06 4:07 ` Philippe Troin
2000-11-06 3:57 ` David S. Miller
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87n1fgvl7a.fsf@tantale.fifi.org \
--to=phil@fifi.org \
--cc=alan@lxorguk.ukuu.org.uk \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.