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

[Xen-devel] [PATCH 3/3] tools/cpu-policy: Add unit tests and a fuzzing harness



The AFL harness currently notices that there are cases where we optimse the
serialised stream by omitting data beyond the various maximum leaves.

Both sets of tests will be extended with further libx86 work.

Fix the sorting of the CPUID_GUEST_NR_* constants, noticed while writing the
unit tests.

Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
---
CC: Jan Beulich <JBeulich@xxxxxxxx>
CC: Wei Liu <wei.liu2@xxxxxxxxxx>
CC: Roger Pau Monné <roger.pau@xxxxxxxxxx>
CC: Sergey Dyasli <sergey.dyasli@xxxxxxxxxx>
---
 tools/fuzz/cpu-policy/.gitignore          |   1 +
 tools/fuzz/cpu-policy/Makefile            |  27 ++++
 tools/fuzz/cpu-policy/afl-policy-fuzzer.c | 117 ++++++++++++++
 tools/tests/Makefile                      |   1 +
 tools/tests/cpu-policy/.gitignore         |   1 +
 tools/tests/cpu-policy/Makefile           |  27 ++++
 tools/tests/cpu-policy/test-cpu-policy.c  | 247 ++++++++++++++++++++++++++++++
 xen/include/xen/lib/x86/cpuid.h           |   2 +-
 8 files changed, 422 insertions(+), 1 deletion(-)
 create mode 100644 tools/fuzz/cpu-policy/.gitignore
 create mode 100644 tools/fuzz/cpu-policy/Makefile
 create mode 100644 tools/fuzz/cpu-policy/afl-policy-fuzzer.c
 create mode 100644 tools/tests/cpu-policy/.gitignore
 create mode 100644 tools/tests/cpu-policy/Makefile
 create mode 100644 tools/tests/cpu-policy/test-cpu-policy.c

diff --git a/tools/fuzz/cpu-policy/.gitignore b/tools/fuzz/cpu-policy/.gitignore
new file mode 100644
index 0000000..b0e0bdf
--- /dev/null
+++ b/tools/fuzz/cpu-policy/.gitignore
@@ -0,0 +1 @@
+afl-policy-fuzzer
diff --git a/tools/fuzz/cpu-policy/Makefile b/tools/fuzz/cpu-policy/Makefile
new file mode 100644
index 0000000..a25778b
--- /dev/null
+++ b/tools/fuzz/cpu-policy/Makefile
@@ -0,0 +1,27 @@
+XEN_ROOT = $(CURDIR)/../../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+.PHONY: all
+all: afl-policy-fuzzer
+
+.PHONY: clean
+clean:
+       $(RM) -f -- *.o .*.d .*.d2 afl-policy-fuzzer
+
+.PHONY: distclean
+distclean: clean
+       $(RM) -f -- *~
+
+.PHONY: install
+install: all
+
+.PHONY: uninstall
+
+CFLAGS += -Werror $(CFLAGS_xeninclude) -D__XEN_TOOLS__
+
+vpath %.c ../../../xen/lib/x86
+
+afl-policy-fuzzer: afl-policy-fuzzer.o msr.o cpuid.o
+       $(CC) $(CFLAGS) $^ -o $@
+
+-include $(DEPS_INCLUDE)
diff --git a/tools/fuzz/cpu-policy/afl-policy-fuzzer.c 
b/tools/fuzz/cpu-policy/afl-policy-fuzzer.c
new file mode 100644
index 0000000..557df81
--- /dev/null
+++ b/tools/fuzz/cpu-policy/afl-policy-fuzzer.c
@@ -0,0 +1,117 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xen-tools/libs.h>
+#include <xen/lib/x86/cpuid.h>
+#include <xen/lib/x86/msr.h>
+#include <xen/domctl.h>
+
+void check_cpuid(const struct cpuid_policy *cp)
+{
+    struct cpuid_policy new = {};
+    xen_cpuid_leaf_t *leaves = malloc(CPUID_MAX_SERIALISED_LEAVES *
+                                      sizeof(xen_cpuid_leaf_t));
+    unsigned int nr = CPUID_MAX_SERIALISED_LEAVES;
+    int rc;
+
+    if ( !leaves )
+        return;
+
+    rc = x86_cpuid_copy_to_buffer(cp, leaves, &nr);
+    assert(rc == 0);
+    assert(nr <= CPUID_MAX_SERIALISED_LEAVES);
+
+    rc = x86_cpuid_copy_from_buffer(&new, leaves, nr, NULL, NULL);
+    assert(rc == 0);
+    assert(memcmp(cp, &new, sizeof(*cp)) == 0);
+
+    free(leaves);
+}
+
+void check_msr(const struct msr_policy *mp)
+{
+    struct msr_policy new = {};
+    xen_msr_entry_t *msrs = malloc(MSR_MAX_SERIALISED_ENTRIES *
+                                   sizeof(xen_msr_entry_t));
+    unsigned int nr = MSR_MAX_SERIALISED_ENTRIES;
+    int rc;
+
+    if ( !msrs )
+        return;
+
+    rc = x86_msr_copy_to_buffer(mp, msrs, &nr);
+    assert(rc == 0);
+    assert(nr <= MSR_MAX_SERIALISED_ENTRIES);
+
+    rc = x86_msr_copy_from_buffer(&new, msrs, nr, NULL);
+    assert(rc == 0);
+    assert(memcmp(mp, &new, sizeof(*mp)) == 0);
+
+    free(msrs);
+}
+
+int main(int argc, char **argv)
+{
+    FILE *fp = NULL;
+    struct cpuid_policy *cp = NULL;
+    struct msr_policy *mp = NULL;
+
+    setbuf(stdin, NULL);
+    setbuf(stdout, NULL);
+
+    if ( argc == 1 )
+    {
+        printf("Using stdin\n");
+        fp = stdin;
+    }
+
+#ifdef __AFL_HAVE_MANUAL_CONTROL
+    __AFL_INIT();
+    while ( __AFL_LOOP(1000) )
+#endif
+    {
+        if ( fp != stdin )
+        {
+            printf("Opening file '%s'\n", argv[1]);
+            fp = fopen(argv[1], "rb");
+
+            if ( !fp )
+            {
+                perror("fopen");
+                exit(-1);
+            }
+        }
+
+        cp = calloc(1, sizeof(*cp));
+        mp = calloc(1, sizeof(*mp));
+        if ( !cp || !mp )
+            goto skip;
+
+        fread(cp, sizeof(*cp), 1, fp);
+        fread(mp, sizeof(*mp), 1, fp);
+
+        if ( !feof(fp) )
+            goto skip;
+
+        check_cpuid(cp);
+        check_msr(mp);
+
+    skip:
+        free(cp);
+        cp = NULL;
+        free(mp);
+        mp = NULL;
+
+        if ( fp != stdin )
+        {
+            fclose(fp);
+            fp = NULL;
+        }
+    }
+
+    return 0;
+}
diff --git a/tools/tests/Makefile b/tools/tests/Makefile
index a9fc50d..067a380 100644
--- a/tools/tests/Makefile
+++ b/tools/tests/Makefile
@@ -5,6 +5,7 @@ CFLAGS  += $(CFLAGS_libxenctrl)
 LDLIBS += $(LDLIBS_libxenctrl)
 
 SUBDIRS-y :=
+SUBDIRS-$(CONFIG_X86) += cpu-policy
 SUBDIRS-$(CONFIG_X86) += mce-test
 SUBDIRS-y += mem-sharing
 ifeq ($(XEN_TARGET_ARCH),__fixme__)
diff --git a/tools/tests/cpu-policy/.gitignore 
b/tools/tests/cpu-policy/.gitignore
new file mode 100644
index 0000000..83bdb6b
--- /dev/null
+++ b/tools/tests/cpu-policy/.gitignore
@@ -0,0 +1 @@
+test-cpu-policy
diff --git a/tools/tests/cpu-policy/Makefile b/tools/tests/cpu-policy/Makefile
new file mode 100644
index 0000000..a029165
--- /dev/null
+++ b/tools/tests/cpu-policy/Makefile
@@ -0,0 +1,27 @@
+XEN_ROOT = $(CURDIR)/../../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+.PHONY: all
+all: test-cpu-policy
+
+.PHONY: clean
+clean:
+       $(RM) -f -- *.o .*.d .*.d2 test-cpu-policy
+
+.PHONY: distclean
+distclean: clean
+       $(RM) -f -- *~
+
+.PHONY: install
+install: all
+
+.PHONY: uninstall
+
+CFLAGS += -Werror $(CFLAGS_xeninclude) -D__XEN_TOOLS__ -O3
+
+vpath %.c ../../../xen/lib/x86
+
+test-cpu-policy: test-cpu-policy.o msr.o cpuid.o
+       $(CC) $(CFLAGS) $^ -o $@
+
+-include $(DEPS_INCLUDE)
diff --git a/tools/tests/cpu-policy/test-cpu-policy.c 
b/tools/tests/cpu-policy/test-cpu-policy.c
new file mode 100644
index 0000000..7414ac7
--- /dev/null
+++ b/tools/tests/cpu-policy/test-cpu-policy.c
@@ -0,0 +1,247 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xen-tools/libs.h>
+#include <xen/lib/x86/cpuid.h>
+#include <xen/lib/x86/msr.h>
+#include <xen/domctl.h>
+
+static void test_cpuid_serialise_success(void)
+{
+    static const struct test {
+        struct cpuid_policy p;
+        const char *name;
+        unsigned int nr_leaves;
+    } tests[] = {
+        {
+            .name = "empty policy",
+            .nr_leaves = 4,
+        },
+    };
+    unsigned int i;
+
+    printf("Testing CPUID serialise success:\n");
+
+    for ( i = 0; i < ARRAY_SIZE(tests); ++i )
+    {
+        const struct test *t = &tests[i];
+        unsigned int nr = t->nr_leaves;
+        xen_cpuid_leaf_t *leaves = malloc(nr * sizeof(*leaves));
+        int rc;
+
+        if ( !leaves )
+            goto test_done;
+
+        rc = x86_cpuid_copy_to_buffer(&t->p, leaves, &nr);
+
+        if ( rc != 0 )
+        {
+            printf("  Test %s, expected rc 0, got %d\n",
+                   t->name, rc);
+            goto test_done;
+        }
+
+        if ( nr != t->nr_leaves )
+        {
+            printf("  Test %s, expected %u leaves, got %u\n",
+                   t->name, t->nr_leaves, nr);
+            goto test_done;
+        }
+
+    test_done:
+        free(leaves);
+    }
+}
+
+static void test_msr_serialise_success(void)
+{
+    static const struct test {
+        struct msr_policy p;
+        const char *name;
+        unsigned int nr_msrs;
+    } tests[] = {
+        {
+            .name = "empty policy",
+            .nr_msrs = MSR_MAX_SERIALISED_ENTRIES,
+        },
+    };
+    unsigned int i;
+
+    printf("Testing MSR serialise success:\n");
+
+    for ( i = 0; i < ARRAY_SIZE(tests); ++i )
+    {
+        const struct test *t = &tests[i];
+        unsigned int nr = t->nr_msrs;
+        xen_msr_entry_t *msrs = malloc(nr * sizeof(*msrs));
+        int rc;
+
+        if ( !msrs )
+            goto test_done;
+
+        rc = x86_msr_copy_to_buffer(&t->p, msrs, &nr);
+
+        if ( rc != 0 )
+        {
+            printf("  Test %s, expected rc 0, got %d\n",
+                   t->name, rc);
+            goto test_done;
+        }
+
+        if ( nr != t->nr_msrs )
+        {
+            printf("  Test %s, expected %u msrs, got %u\n",
+                   t->name, t->nr_msrs, nr);
+            goto test_done;
+        }
+
+    test_done:
+        free(msrs);
+    }
+}
+
+static void test_cpuid_deserialise_failure(void)
+{
+    static const struct test {
+        const char *name;
+        xen_cpuid_leaf_t leaf;
+    } tests[] = {
+        {
+            .name = "incorrect basic subleaf",
+            .leaf = { .leaf = 0, .subleaf = 0 },
+        },
+        {
+            .name = "incorrect hv1 subleaf",
+            .leaf = { .leaf = 0x40000000, .subleaf = 0 },
+        },
+        {
+            .name = "incorrect hv2 subleaf",
+            .leaf = { .leaf = 0x40000100, .subleaf = 0 },
+        },
+        {
+            .name = "incorrect extd subleaf",
+            .leaf = { .leaf = 0x80000000, .subleaf = 0 },
+        },
+        {
+            .name = "OoB basic leaf",
+            .leaf = { .leaf = CPUID_GUEST_NR_BASIC },
+        },
+        {
+            .name = "OoB cache leaf",
+            .leaf = { .leaf = 0x4, .subleaf = CPUID_GUEST_NR_CACHE },
+        },
+        {
+            .name = "OoB feat leaf",
+            .leaf = { .leaf = 0x7, .subleaf = CPUID_GUEST_NR_FEAT },
+        },
+        {
+            .name = "OoB topo leaf",
+            .leaf = { .leaf = 0xb, .subleaf = CPUID_GUEST_NR_TOPO },
+        },
+        {
+            .name = "OoB xstate leaf",
+            .leaf = { .leaf = 0xd, .subleaf = CPUID_GUEST_NR_XSTATE },
+        },
+        {
+            .name = "OoB extd leaf",
+            .leaf = { .leaf = 0x80000000 | CPUID_GUEST_NR_EXTD },
+        },
+    };
+    unsigned int i;
+
+    printf("Testing CPUID deserialise failure:\n");
+
+    for ( i = 0; i < ARRAY_SIZE(tests); ++i )
+    {
+        const struct test *t = &tests[i];
+        uint32_t err_leaf = ~0u, err_subleaf = ~0u;
+        int rc;
+
+        rc = x86_cpuid_copy_from_buffer(NULL, &t->leaf, 1,
+                                        &err_leaf, &err_subleaf);
+
+        if ( rc != -ERANGE )
+        {
+            printf("  Test %s, expected rc %d, got %d\n",
+                   t->name, -ERANGE, rc);
+            continue;
+        }
+
+        if ( err_leaf != t->leaf.leaf || err_subleaf != t->leaf.subleaf )
+        {
+            printf("  Test %s, expected err %08x:%08x, got %08x:%08x\n",
+                   t->name, t->leaf.leaf, t->leaf.subleaf,
+                   err_leaf, err_subleaf);
+            continue;
+        }
+    }
+}
+
+static void test_msr_deserialise_failure(void)
+{
+    static const struct test {
+        const char *name;
+        xen_msr_entry_t msr;
+        int rc;
+    } tests[] = {
+        {
+            .name = "bad msr index",
+            .msr = { .idx = 0xdeadc0de },
+            .rc = -ERANGE,
+        },
+        {
+            .name = "nonzero flags",
+            .msr = { .idx = 0xce, .flags = 1 },
+            .rc = -EINVAL,
+        },
+        {
+            .name = "truncated val",
+            .msr = { .idx = 0xce, .val = ~0ull },
+            .rc = -EOVERFLOW,
+        },
+    };
+    unsigned int i;
+
+    printf("Testing MSR deserialise failure:\n");
+
+    for ( i = 0; i < ARRAY_SIZE(tests); ++i )
+    {
+        const struct test *t = &tests[i];
+        uint32_t err_msr = ~0u;
+        int rc;
+
+        rc = x86_msr_copy_from_buffer(NULL, &t->msr, 1, &err_msr);
+
+        if ( rc != t->rc )
+        {
+            printf("  Test %s, expected rc %d, got %d\n",
+                   t->name, t->rc, rc);
+            continue;
+        }
+
+        if ( err_msr != t->msr.idx )
+        {
+            printf("  Test %s, expected err_msr %#x, got %#x\n",
+                   t->name, t->msr.idx, err_msr);
+            continue;
+        }
+    }
+}
+
+int main(int argc, char **argv)
+{
+    printf("CPU Policy unit tests\n");
+
+    test_cpuid_serialise_success();
+    test_msr_serialise_success();
+
+    test_cpuid_deserialise_failure();
+    test_msr_deserialise_failure();
+
+    return 0;
+}
diff --git a/xen/include/xen/lib/x86/cpuid.h b/xen/include/xen/lib/x86/cpuid.h
index 767a33b..95b37b6 100644
--- a/xen/include/xen/lib/x86/cpuid.h
+++ b/xen/include/xen/lib/x86/cpuid.h
@@ -66,8 +66,8 @@ static inline void cpuid_count_leaf(
 #undef XCHG
 
 #define CPUID_GUEST_NR_BASIC      (0xdu + 1)
-#define CPUID_GUEST_NR_FEAT       (0u + 1)
 #define CPUID_GUEST_NR_CACHE      (5u + 1)
+#define CPUID_GUEST_NR_FEAT       (0u + 1)
 #define CPUID_GUEST_NR_TOPO       (1u + 1)
 #define CPUID_GUEST_NR_XSTATE     (62u + 1)
 #define CPUID_GUEST_NR_EXTD_INTEL (0x8u + 1)
-- 
2.1.4


_______________________________________________
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®.