dev_ifname32() fails on 32->64bit calls in copy_in_user().

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello folks,
	I've been using a nice program on ppc32 with a ppc64
system+kernel.  It uses netlink to determine some network interface
information.  Recent kernels cause it to exit at the netlink stage.  At
first, I thought it was a bug in the program.  Now I'm not so sure.
	The situation is specifically the use of SIOCGIFNAME in 32bit
binaries on 64 bit platforms+kernels.  I've tested ppc32 on ppc64 and 
ia32 on amd64.  amd64 on amd64, ia32 on ia32,and ppc64 on ppc64
all work fine.
	I've cooked up a test program to evince the problem.  It just
asks netlink for an interface list, then uses SIOCGIFNAME to get the
interface name.  On kernels before commit 
32da477a5bfe96b6dfc8960e0d22d89ca09fd10a [NET]: Don't implement
dev_ifname32 inline, the test program runs fine.  Kernels after, I get
EFAULT.  This behavior is the same as the original program. 
	Instrumenting the kernel with printks, the EFAULT comes from
the first copy_in_user() at line 325 of fs/compat_ioctl.c (in
dev_ifname32()).  I put some access_ok() checks in, and they do not
trigger (access is ok).  The call never even gets into sys_ioctl().
	I'm assuming at this point that copy_in_user() for ia32/amd64
and ppc32/ppc64 is what is having trouble.  Test program follows.

Joel

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>

#include <net/if.h>
#include <asm/types.h>
#include <linux/rtnetlink.h>

#define NETLINK_BUFSIZE 16384

static void bail(int rc, char *fmt, ...)
{
    va_list args;

    if (fmt) {
        va_start(args, fmt);
        vfprintf(stderr, fmt, args);
        va_end(args);
    }

    exit(rc);
}

int main(int argc, char *argv[])
{
    int fd, ioctl_fd, rc, len;
    struct {
        struct nlmsghdr nlh;
        struct rtgenmsg g;
    } req;
    struct sockaddr_nl nladdr;
    static char rcvbuf[NETLINK_BUFSIZE];
    struct nlmsghdr *h;
    struct iovec iov = {rcvbuf, sizeof(rcvbuf) };
    struct msghdr msg = {
        (void *)&nladdr, sizeof(nladdr),
        &iov, 1,
        NULL, 0,
        0
    };
    struct ifreq ifr;
    struct ifaddrmsg *ifa;

    fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (fd < 0)
        bail(1, "Unable to open netlink socket: %s\n", strerror(errno));

    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)))
        bail(1, "Unable to set socket options: %s\n", strerror(errno));

    memset(&nladdr, 0, sizeof(nladdr));
    nladdr.nl_family = AF_NETLINK;
    req.nlh.nlmsg_len = sizeof(req);
    req.nlh.nlmsg_type = RTM_GETADDR;
    req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
    req.nlh.nlmsg_pid = 0;
    req.nlh.nlmsg_seq = 1;
    req.g.rtgen_family = AF_INET;

    if (sendto(fd, (void *)&req, sizeof(req), 0,
               (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0)
        bail(1, "Unable to send netlink request: %s\n", strerror(errno));

    while (1)
    {
        rc = recvmsg(fd, &msg, 0);
        if (!rc)
            bail(1, "Netlink socket closed\n");

        h = (struct nlmsghdr *)rcvbuf;
        if (h->nlmsg_type == NLMSG_DONE)
            break;

        if (h->nlmsg_type == NLMSG_ERROR)
            bail(1, "Error from netlink socket\n");

        len = rc;
        for (; NLMSG_OK(h, len); h = NLMSG_NEXT(h, len)) {
            if (h->nlmsg_type != RTM_NEWADDR)
                continue;

            ioctl_fd = socket(AF_INET, SOCK_STREAM, 0);
            if (ioctl_fd < 0)
                bail(1, "Unable to create ioctl socket: %s\n",
                     strerror(errno));

            ifa = NLMSG_DATA(h);
            memset(&ifr, 0, sizeof(ifr));
            ifr.ifr_ifindex = ifa->ifa_index;

            rc = ioctl(ioctl_fd, SIOCGIFNAME, &ifr);
            if (rc)
                bail(1, "Unable to get name: %s\n", strerror(errno));

            fprintf(stdout, "Interface %d, %s\n", ifr.ifr_ifindex,
                    ifr.ifr_ifrn.ifrn_name);
        }
    }

    return 0;
}

-- 

"What no boss of a programmer can ever understand is that a programmer
 is working when he's staring out of the window"
	- With apologies to Burton Rascoe

Joel Becker
Principal Software Developer
Oracle
E-mail: [email protected]
Phone: (650) 506-8127
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux