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

Re: [Xen-devel] [PATCH 3/4] tools: xencall, xengnttab, xengntshr: Provide access to internal fds



Andrew Cooper writes ("Re: [Xen-devel] [PATCH 3/4] tools: xencall, xengnttab, 
xengntshr: Provide access to internal fds"):
> These are ABI breakages.

Thanks for the review and sorry to miss that.  You are right.

I have another question, RFC: I have a test C program which links
against Xen libraries and does the actual descriptor auditing.
Current WIP version attached to give you an idea.

Should I submit this for inclusion in xen.git#tools/tests/ ?
Or should I put it in osstest and have osstest build it ?

I think the former is probably better because then it can be used more
widely.

This thing is surrounded by two perl scripts, which grobble around in
/proc.  They contain pathname regexps, some of which are
osstest-specific.  They also have to grobble around in xenstore to
find pids and things.  I'm currently unsure as to whether these
scripts should be in xen.git or osstest.  If they go into xen.git then
they will have to take arguments for the osstest-specific
supplementary regexps, or something, which seems awkward.  So I'm
currently thinking I will put them in osstest.

Opinions welcome.

Ian.

/*
  */

#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#include <err.h>

#include <xenctrl.h>
#include <xencall.h>
#include <xengnttab.h>
#include <xenevtchn.h>

/*
 * Every class needs setup.  setup is called once per class at program
 * startup.
 *
 * Then it can have
 *     open test getfd close
 * In which case the core code will for every fd
 *     open test getfd dup2 test close
 * And test should call blocked or succeeded and then immediately
 * return, or error out
 *
 * Or it can have
 *     check
 * which should call report, or error out
 *
 * Errors: use trouble for simple syscall errors.  Or use err or errx
 * and maybe print fd_desc and test_which, according to the comments
 * in struct classinfo.
 */

static xentoollog_logger *logger;

static int object_fd;
static const char *classname;
static const char *fd_desc;
static const char *test_which;

static const char *test_wh_unrest = "test (unrestricted)";
static const char *test_wh_rest   = "test (restricted)";


static void trouble(const char *what) __attribute__((noreturn));
static void trouble(const char *what) {
    fprintf(stderr,
            "trouble: %s %s %d (%s) %s: %s\n",
            classname, test_which, object_fd, fd_desc, what, strerror(errno));
    exit(-1);
}

static void report(const char *pass_or_fail, const char *what,
                   const char *notes) {
    printf("%s %s %d %s (%s) %s\n",
           classname, pass_or_fail,
           object_fd, what, notes, fd_desc);
    if (ferror(stdout) || fflush(stdout)) err(16,"stdout");
}

static void succeeded(const char *what) {
    if (test_which == test_wh_unrest) {
        /* ok */
        test_which = 0;
    } else if (test_which == test_wh_rest) {
        report("fail",what,"unexpectedly succeeded");
        test_which = 0;
    } else {
        abort();
    }
}

static void blocked(const char *what) {
    if (test_which == test_wh_rest) {
        /* yay */
        report("pass", what,"blocked");
        test_which = 0;
    } else if (test_which == test_wh_unrest) {
        err(4,"test blocked on unrestricted fd: %s {%s}",what,test_which);
    } else {
        abort();
    }
}

/* privcmd */

static xc_interface *xch;
static void setup_privcmd(void) { }
static void open_privcmd(void) {
    xch = xc_interface_open(logger,0,0);
    if (!xch) trouble("xc_interface_open");
}
static void test_privcmd(void) {
    int r = xc_get_online_cpus(xch);
    if (r>0)
        succeeded("xc_get_online_cpus");
    else if (r==0)
        errx(-1,"xc_get_online_cpus{%s, %s}=0", test_which, fd_desc);
    else if (errno==EPERM)
        blocked("xc_get_online_cpus");
    else
        trouble("xc_get_online_cpus");
}
static int getfd_privcmd(void) {
    return xencall_fd(xc_interface_xcall_handle(xch));
}
static void close_privcmd(void) {
    xc_interface_close(xch);
}

/* gntdev */

static xengntshr_handle *xgs;
static uint32_t gntshr_gref;
static xengnttab_handle *xgt;
static void setup_gntdev(void) {
    void *r;
    xgs = xengntshr_open(logger,0);
    if (!xgs) trouble("xengntshr_open");
    r = xengntshr_share_pages(xgs, 0, 1, &gntshr_gref, 1);
    if (!r || r==(void*)-1) trouble("xengntshr_share_pages");
    memset(r, 0x55, XC_PAGE_SIZE);
}
static void open_gntdev(void) {
    xgt = xengnttab_open(logger,0);
    if (!xgt) trouble("xengnttab_open");
}
static void test_gntdev(void) {
    char mybuf[XC_PAGE_SIZE];
    memset(mybuf, 0xaa, XC_PAGE_SIZE);
    xengnttab_grant_copy_segment_t seg;
    seg.source.foreign.ref = gntshr_gref;
    seg.source.foreign.offset = 0;
    seg.source.foreign.domid = 0;
    seg.dest.virt = mybuf;
    seg.len = 1;
    seg.flags = GNTCOPY_source_gref;
    for (;;) {
        seg.status = 0;
        int r = xengnttab_grant_copy(xgt,1,&seg);
        if (r<0) {
            if (errno==EPERM || errno==ENOTTY)
                blocked("xengnttab_grant_copy");
            else
                trouble("xengnttab_grant_copy");
        } else if (r==0) {
            if (seg.status==GNTST_okay)
                succeeded("xengnttab_grant_copy okay");
            else if (seg.status==GNTST_eagain)
                continue;
            else errx(-1,"xengnttab_grant_copy=%d {%s, %s} but .status=%d",
                      r, test_which, fd_desc,(int)seg.status);
        } else {
            errx(-1,"xengnttab_grant_copy=%d {%s, %s}",
                 r, test_which, fd_desc);
        }
        break;
    }
}
static int getfd_gntdev(void) {
    return xengnttab_fd(xgt);
}
static void close_gntdev(void) {
    xengnttab_close(xgt);
}

/* evtchn */

static xenevtchn_handle *xce_recip, *xce;
static xenevtchn_port_or_error_t evtchn_port;
static void setup_evtchn(void) {
    xce_recip = xenevtchn_open(logger, 0);
    if (!xce_recip) err(-1,"xenevtchn_open (donor)");

    evtchn_port = xenevtchn_bind_unbound_port(xce_recip, 0);
    if (evtchn_port < 0) trouble("xenevtchn_bind_unbound_port");
}
static void open_evtchn(void) {
    xce = xenevtchn_open(logger, 0);
    if (!xce) err(-1,"xenevtchn_open");
}
static void test_evtchn(void) {
    /* Ideally xce_recip would be allocated in setup, but the docs are
     * not clear as to how to free a port obtained from
     * xenevtchn_bind_unbound_port.  Closing the fd must suffice. */
    xenevtchn_port_or_error_t r = xenevtchn_notify(xce, evtchn_port);
    if (r>=0)
        succeeded("xenevtchn_notify");
    else if ((errno==EPERM || errno==ENOTTY))
        blocked("xenevtchn_notify");
    else
        trouble("xenevtchn_notify");
}
static int getfd_evtchn(void) {
    return xenevtchn_fd(xce);
}
static void close_evtchn(void) {
    xenevtchn_close(xce);
}

#define CHECK_FCNTL(openmode)                           \
    int r = fcntl(object_fd, F_GETFL);                  \
    if (r < 0) trouble("fcntl F_GETFL");                \
    int m = r & (O_RDONLY | O_WRONLY | O_RDWR);         \
                                                        \
    char mbuf[100 + 30*3];                              \
    snprintf(mbuf,sizeof(mbuf),                         \
             "F_GETFL=%#o m=%#o " #openmode "=%#o",     \
             r,m,(int)openmode);                        \
                                                        \
    if (m != openmode) {                                \
        report("fail", #openmode, mbuf);                \
        return;                                         \
    }

static void setup_readonly(void) { }
static void check_readonly(void) {
    CHECK_FCNTL(O_RDONLY);
    report("pass", "fcntl", mbuf);
}

static void setup_appendonly(void) { }
static void check_appendonly(void) {
    CHECK_FCNTL(O_WRONLY);
    if (!(r & O_APPEND)) {
        report("fail", "O_APPEND", mbuf);
        return;
    }
    report("pass", "fcntl", mbuf);
}

#define DEFCLASS(cl) \
    { #cl, setup_##cl, 0, open_##cl, test_##cl, getfd_##cl, close_##cl }
#define DEFCHECK(meth) \
    { #meth, setup_##meth, check_##meth }

static const struct classinfo {
    const char *name;     /* errors: print fd_desc   test_which */
    void (*setup)(void);  /*               best not   best not  */
    void (*check)(void);  /*               must       may       */
    void (*open)(void);   /*               must       may       */
    void (*test)(void);   /*               must       must      */
    int (*getfd)(void);   /*               must       may       */
    void (*close)(void);  /*               must       may       */
} classinfos[] = {
    DEFCLASS(privcmd),
    DEFCLASS(gntdev),
//  DEFCLASS(evtchn),   has side effects when it fails!
    DEFCHECK(readonly),
    DEFCHECK(appendonly),
    { 0 }
};

int main(int argc, char **argv) {
    const struct classinfo *cli;
    int r;

    argv++;

    logger = (xentoollog_logger*)xtl_createlogger_stdiostream
        (stderr, XTL_NOTICE, XTL_STDIOSTREAM_HIDE_PROGRESS);

    fd_desc = "setup";
    test_which = "setup";
    for (cli = classinfos; cli->name; cli++)
        cli->setup();

    while ((classname = *argv++)) {
        if (!*argv) errx(8,"need fd after class");
        object_fd = atoi(*argv++);

        fd_desc = *argv++;
        if (!fd_desc) errx(8,"need info after fd");

        for (cli = classinfos; cli->name; cli++)
            if (!strcmp(cli->name, classname))
                goto found;
        report("fail","unknown class","");
        continue;

    found:
        if (cli->check) {
            report("checking","check","in progress");
            test_which = "check";
            cli->check();
        } else {
            test_which = "open";
            report("checking","dup-hack","in progress");
                                                  cli->open();

            test_which = test_wh_unrest;          cli->test();
            assert(!test_which);

            test_which = "getfd"; int intern_fd = cli->getfd();
            r = dup2(object_fd, intern_fd);
            if (r != intern_fd) err(-1, "dup2");

            test_which = test_wh_rest;             cli->test();
            assert(!test_which);

            test_which = "close";                  cli->close();
        }
    }

    return 0;
}

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.