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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox