Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id ; Fri, 3 Nov 2000 15:46:01 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id ; Fri, 3 Nov 2000 15:45:51 -0500 Received: from tantale.fifi.org ([216.15.47.52]:12936 "EHLO tantale.fifi.org") by vger.kernel.org with ESMTP id ; Fri, 3 Nov 2000 15:45:37 -0500 To: linux-kernel@vger.kernel.org Cc: Alan Cox Subject: 2.2.x BUG & PATCH: recvmsg() does not check msg_controllen correctly MIME-Version: 1.0 (generated by SEMI 1.12.1 - "[JR] Nonoichi") Content-Type: multipart/mixed; boundary="Multipart_Fri_Nov__3_12:45:29_2000-1" From: Philippe Troin Date: 03 Nov 2000 12:45:29 -0800 Message-ID: <87n1fgvl7a.fsf@tantale.fifi.org> Lines: 202 User-Agent: Semi-gnus/6.10.12 SEMI/1.12.1 ([JR] Nonoichi) FLIM/1.12.7 (Y?zaki) Emacs/20.7 (i386-debian-linux-gnu) MULE/4.0 (HANANOEN) Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org --Multipart_Fri_Nov__3_12:45:29_2000-1 Content-Type: text/plain; charset=US-ASCII 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. --Multipart_Fri_Nov__3_12:45:29_2000-1 Content-Type: application/octet-stream; type=patch Content-Disposition: attachment; filename="linux-2.2.17-8-scmrights.patch" Content-Transfer-Encoding: quoted-printable 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 =3D 0, i; =20 + /* Handle the case when we don't even have enough room for the header. + Fixed Oct 29 2000 by Philippe Troin */ + if (msg->msg_controllen < sizeof(struct cmsghdr)) { + msg->msg_flags |=3D MSG_CTRUNC; + goto out; + } =20 + if (fdnum < fdmax) fdmax =3D fdnum; =20 @@ -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); } =20 --Multipart_Fri_Nov__3_12:45:29_2000-1 Content-Type: application/octet-stream Content-Disposition: attachment; filename="check-anc.c" Content-Transfer-Encoding: quoted-printable /*=20 * Check that fd does not get created when the ancillary data buffer * gets truncated... * Philippe Troin * This snippet is licensed under the GNU General Public License. */ #include #include #include #include #include #include #include #include #include #include #include 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 =3D 0; if ((devnull =3D open("/dev/null", O_RDWR))<0) perror("child: open /dev/null"), exit(1); msg.msg_name =3D NULL; msg.msg_namelen =3D 0; msg.msg_iov =3D iov; msg.msg_iovlen =3D 1; msg.msg_control =3D ancdata; msg.msg_controllen =3D sizeof(ancdata); msg.msg_flags =3D 0; iov[0].iov_base =3D &zero; iov[0].iov_len =3D sizeof(zero); cmsg =3D CMSG_FIRSTHDR(&msg); cmsg->cmsg_len =3D CMSG_LEN(sizeof(devnull)); cmsg->cmsg_level =3D SOL_SOCKET; cmsg->cmsg_type =3D SCM_RIGHTS; *((int*)CMSG_DATA(cmsg)) =3D 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[]=3D"f00bar"; union { struct { char ancdata[2];=20 char padding[6]; } a; char safety[CMSG_SPACE(sizeof(int))]; } ancs; struct iovec iov[1]; /**/ if (sock !=3D 3) fprintf(stderr, "expected parent socket at fd 3, got %d\n", sock), exit(1); if (close(4)>=3D0 || errno !=3D EBADF) fprintf(stderr, "already got a descriptor at fd 4, why ?\n"), exit(1); msg.msg_name =3D NULL; msg.msg_namelen =3D 0; msg.msg_iov =3D iov; msg.msg_iovlen =3D 1; msg.msg_control =3D ancs.a.ancdata; msg.msg_controllen =3D sizeof(ancs.a.ancdata); msg.msg_flags =3D 0; iov[0].iov_base =3D &data; iov[0].iov_len =3D sizeof(data); memset(&ancs, 0, sizeof(ancs)); memcpy(ancs.a.padding, padvalue, strlen(padvalue)); fprintf(stderr, "calling recvmsg with controllen =3D %d\n",=20 msg.msg_controllen); if (recvmsg(sock, &msg, 0)<0) perror("parent: recvmsg"), exit(1); fprintf(stderr, "recvmsg returned with controllen =3D %d\n",=20 msg.msg_controllen); if (memcmp(ancs.a.padding, padvalue, strlen(padvalue)) !=3D 0) fprintf(stderr, "recvmsg smashed the stack !\n"); if (close(4)>=3D0 || errno !=3D 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=3Dfork())<0) perror("fork"), exit(1); if (childpid=3D=3D0) { /* 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 !=3D 0) fprintf(stderr, "child exited with status %d\n", status), exit(1); } exit(0); } --Multipart_Fri_Nov__3_12:45:29_2000-1-- - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org Please read the FAQ at http://www.tux.org/lkml/