| // Copyright 2017 syzkaller project authors. All rights reserved. |
| // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. |
| |
| #if GOOS_linux && (GOARCH_amd64 || GOARCH_ppc64 || GOARCH_ppc64le || GOARCH_arm64) |
| #include "test_linux.h" |
| #endif |
| |
| #include <algorithm> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| static int test_copyin() |
| { |
| static uint16 buf[3]; |
| STORE_BY_BITMASK(uint16, htole16, &buf[1], 0x1234, 0, 16); |
| unsigned char x[sizeof(buf)]; |
| memcpy(x, buf, sizeof(x)); |
| if (x[0] != 0 || x[1] != 0 || |
| x[2] != 0x34 || x[3] != 0x12 || |
| x[4] != 0 || x[5] != 0) { |
| printf("bad result of STORE_BY_BITMASK(le16, 0x1234, 0, 16): %x %x %x %x %x %x\n", |
| x[0], x[1], x[2], x[3], x[4], x[5]); |
| return 1; |
| } |
| STORE_BY_BITMASK(uint16, htole16, &buf[1], 0x555a, 5, 4); |
| memcpy(x, buf, sizeof(x)); |
| if (x[0] != 0 || x[1] != 0 || |
| x[2] != 0x54 || x[3] != 0x13 || |
| x[4] != 0 || x[5] != 0) { |
| printf("bad result of STORE_BY_BITMASK(le16, 0x555a, 5, 4): %x %x %x %x %x %x\n", |
| x[0], x[1], x[2], x[3], x[4], x[5]); |
| return 1; |
| } |
| STORE_BY_BITMASK(uint16, htobe16, &buf[1], 0x4567, 13, 3); |
| memcpy(x, buf, sizeof(x)); |
| if (x[0] != 0 || x[1] != 0 || |
| x[2] != 0xf4 || x[3] != 0x13 || |
| x[4] != 0 || x[5] != 0) { |
| printf("bad result of STORE_BY_BITMASK(be16, 0x4567, 13, 3): %x %x %x %x %x %x\n", |
| x[0], x[1], x[2], x[3], x[4], x[5]); |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int test_csum_inet() |
| { |
| struct csum_inet_test { |
| const char* data; |
| size_t length; |
| uint16 csum; |
| }; |
| struct csum_inet_test tests[] = { |
| {// 0 |
| "", |
| 0, |
| le16toh(0xffff)}, |
| { |
| // 1 |
| "\x00", |
| 1, |
| le16toh(0xffff), |
| }, |
| { |
| // 2 |
| "\x00\x00", |
| 2, |
| le16toh(0xffff), |
| }, |
| { |
| // 3 |
| "\x00\x00\xff\xff", |
| 4, |
| le16toh(0x0000), |
| }, |
| { |
| // 4 |
| "\xfc", |
| 1, |
| le16toh(0xff03), |
| }, |
| { |
| // 5 |
| "\xfc\x12", |
| 2, |
| le16toh(0xed03), |
| }, |
| { |
| // 6 |
| "\xfc\x12\x3e", |
| 3, |
| le16toh(0xecc5), |
| }, |
| { |
| // 7 |
| "\xfc\x12\x3e\x00\xc5\xec", |
| 6, |
| le16toh(0x0000), |
| }, |
| { |
| // 8 |
| "\x42\x00\x00\x43\x44\x00\x00\x00\x45\x00\x00\x00\xba\xaa\xbb\xcc\xdd", |
| 17, |
| le16toh(0x43e1), |
| }, |
| { |
| // 9 |
| "\x42\x00\x00\x43\x44\x00\x00\x00\x45\x00\x00\x00\xba\xaa\xbb\xcc\xdd\x00", |
| 18, |
| le16toh(0x43e1), |
| }, |
| { |
| // 10 |
| "\x00\x00\x42\x00\x00\x43\x44\x00\x00\x00\x45\x00\x00\x00\xba\xaa\xbb\xcc\xdd", |
| 19, |
| le16toh(0x43e1), |
| }, |
| { |
| // 11 |
| "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\xab\xcd", |
| 15, |
| le16toh(0x5032), |
| }, |
| { |
| // 12 |
| "\x00\x00\x12\x34\x56\x78", |
| 6, |
| le16toh(0x5397), |
| }, |
| { |
| // 13 |
| "\x00\x00\x12\x34\x00\x00\x56\x78\x00\x06\x00\x04\xab\xcd", |
| 14, |
| le16toh(0x7beb), |
| }, |
| { |
| // 14 |
| "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x77\x66\x55\x44\x33\x22\x11\x00\x00\x00\x00\x04\x00\x00\x00\x06\x00\x00\xab\xcd", |
| 44, |
| le16toh(0x2854), |
| }, |
| { |
| // 15 |
| "\x00\x00\x12\x34\x00\x00\x56\x78\x00\x11\x00\x04\xab\xcd", |
| 14, |
| le16toh(0x70eb), |
| }, |
| { |
| // 16 |
| "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x77\x66\x55\x44\x33\x22\x11\x00\x00\x00\x00\x04\x00\x00\x00\x11\x00\x00\xab\xcd", |
| 44, |
| le16toh(0x1d54), |
| }, |
| { |
| // 17 |
| "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x77\x66\x55\x44\x33\x22\x11\x00\x00\x00\x00\x04\x00\x00\x00\x3a\x00\x00\xab\xcd", |
| 44, |
| le16toh(0xf453), |
| }}; |
| |
| for (unsigned i = 0; i < ARRAY_SIZE(tests); i++) { |
| struct csum_inet csum; |
| csum_inet_init(&csum); |
| csum_inet_update(&csum, (const uint8*)tests[i].data, tests[i].length); |
| if (csum_inet_digest(&csum) != tests[i].csum) { |
| fprintf(stderr, "bad checksum in test #%u, want: %hx, got: %hx\n", i, tests[i].csum, csum_inet_digest(&csum)); |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int rand_int_range(int start, int end) |
| { |
| return rand() % (end + 1 - start) + start; |
| } |
| |
| static int test_csum_inet_acc() |
| { |
| uint8 buffer[128]; |
| |
| for (int test = 0; test < 256; test++) { |
| int size = rand_int_range(1, 128); |
| int step = rand_int_range(1, 8) * 2; |
| |
| for (int i = 0; i < size; i++) |
| buffer[i] = rand_int_range(0, 255); |
| |
| struct csum_inet csum_acc; |
| csum_inet_init(&csum_acc); |
| |
| for (int i = 0; i < size / step; i++) |
| csum_inet_update(&csum_acc, &buffer[i * step], step); |
| if (size % step != 0) |
| csum_inet_update(&csum_acc, &buffer[size - size % step], size % step); |
| |
| struct csum_inet csum; |
| csum_inet_init(&csum); |
| csum_inet_update(&csum, &buffer[0], size); |
| |
| if (csum_inet_digest(&csum_acc) != csum_inet_digest(&csum)) |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int test_cover_filter() |
| { |
| CoverFilter filter; |
| CoverFilter child(filter.FD()); |
| |
| std::vector<uint64> pcs = { |
| 100, |
| 111, |
| 200, |
| (1 << 20) - 1, |
| 1 << 20, |
| (1 << 30) - 1, |
| 100ull << 30, |
| (100ull << 30) + 100, |
| 200ull << 30, |
| (1ull << 62) + 100, |
| }; |
| |
| // These we don't insert, but they are also present due to truncation of low 3 bits. |
| std::vector<uint64> also_contain = { |
| 96, |
| 103, |
| 104, |
| 207, |
| (1 << 20) - 7, |
| (1 << 20) + 7, |
| (1ull << 62) + 96, |
| (1ull << 62) + 103, |
| }; |
| |
| std::vector<uint64> dont_contain = { |
| 0, |
| 1, |
| 95, |
| 112, |
| 199, |
| 208, |
| 100 << 10, |
| (1 << 20) - 9, |
| (1 << 20) + 8, |
| (2ull << 30) - 1, |
| 2ull << 30, |
| (2ull << 30) + 1, |
| (100ull << 30) + 108, |
| 150ull << 30, |
| 1ull << 40, |
| 1ull << 63, |
| ~0ull, |
| }; |
| |
| int ret = 0; |
| for (auto pc : pcs) |
| filter.Insert(pc); |
| pcs.insert(pcs.end(), also_contain.begin(), also_contain.end()); |
| for (auto pc : pcs) { |
| if (!filter.Contains(pc) || !child.Contains(pc)) { |
| printf("filter doesn't contain %llu (0x%llx)\n", pc, pc); |
| ret = 1; |
| } |
| } |
| for (auto pc : dont_contain) { |
| if (filter.Contains(pc) || child.Contains(pc)) { |
| printf("filter contains %llu (0x%llx)\n", pc, pc); |
| ret = 1; |
| } |
| } |
| return ret; |
| } |
| |
| static bool test_one_glob(const char* pattern, std::vector<std::string> want) |
| { |
| std::vector<std::string> got = Glob(pattern); |
| std::sort(want.begin(), want.end()); |
| std::sort(got.begin(), got.end()); |
| if (got == want) |
| return true; |
| printf("pattern '%s', want %zu files:\n", pattern, want.size()); |
| for (const auto& f : want) |
| printf("\t'%s'\n", f.c_str()); |
| printf("got %zu files:\n", got.size()); |
| for (const auto& f : got) |
| printf("\t'%s'\n", f.c_str()); |
| return false; |
| } |
| |
| static void must_mkdir(const char* dir) |
| { |
| if (mkdir(dir, 0700)) |
| failmsg("mkdir failed", "dir=%s", dir); |
| } |
| |
| static void must_creat(const char* file) |
| { |
| int fd = open(file, O_CREAT | O_EXCL, 0700); |
| if (fd == -1) |
| failmsg("open failed", "file=%s", file); |
| close(fd); |
| } |
| |
| static void must_link(const char* oldpath, const char* linkpath) |
| { |
| if (link(oldpath, linkpath)) |
| failmsg("link failed", "oldpath=%s linkpath=%s", oldpath, linkpath); |
| } |
| |
| static void must_symlink(const char* oldpath, const char* linkpath) |
| { |
| if (symlink(oldpath, linkpath)) |
| failmsg("symlink failed", "oldpath=%s linkpath=%s", oldpath, linkpath); |
| } |
| |
| static int test_glob() |
| { |
| #if GOARCH_arm |
| // When running a 32-bit ARM binary on a 64-bit system under QEMU, readdir() fails |
| // with EOVERFLOW, resulting in Glob() returning 0 files. |
| // Tracking QEMU bug: https://gitlab.com/qemu-project/qemu/-/issues/263. |
| return -1; |
| #endif |
| // Note: pkg/runtest.TestExecutor creates a temp dir for the test, |
| // so we create files in cwd and don't clean up. |
| if (!test_one_glob("glob/*", {})) |
| return 1; |
| must_mkdir("glob"); |
| if (!test_one_glob("glob/*", {})) |
| return 1; |
| must_mkdir("glob/dir1"); |
| must_creat("glob/file1"); |
| must_mkdir("glob/dir2"); |
| must_creat("glob/dir2/file21"); |
| must_mkdir("glob/dir3"); |
| must_creat("glob/dir3/file31"); |
| must_link("glob/dir3/file31", "glob/dir3/file32"); |
| must_symlink("file31", "glob/dir3/file33"); |
| must_symlink("deadlink", "glob/dir3/file34"); |
| must_symlink("../../glob", "glob/dir3/dir31"); |
| must_mkdir("glob/dir4"); |
| must_mkdir("glob/dir4/dir41"); |
| must_creat("glob/dir4/dir41/file411"); |
| must_symlink("dir4", "glob/dir5"); |
| must_mkdir("glob/dir6"); |
| must_mkdir("glob/dir6/dir61"); |
| must_creat("glob/dir6/dir61/file611"); |
| must_symlink("dir6/dir61", "glob/self"); |
| // Directories are not includes + not recursive (yet). |
| if (!test_one_glob("glob/*", { |
| "glob/file1", |
| })) |
| return 1; |
| if (!test_one_glob("glob/*/*", { |
| "glob/dir2/file21", |
| "glob/dir3/file31", |
| "glob/dir3/file32", // hard links are included |
| "glob/self/file611", // symlinks via name "self" are included |
| })) |
| return 1; |
| return 0; |
| } |
| |
| static struct { |
| const char* name; |
| int (*f)(); |
| } tests[] = { |
| {"test_copyin", test_copyin}, |
| {"test_csum_inet", test_csum_inet}, |
| {"test_csum_inet_acc", test_csum_inet_acc}, |
| #if GOOS_linux && (GOARCH_amd64 || GOARCH_ppc64 || GOARCH_ppc64le || GOARCH_arm64) |
| {"test_kvm", test_kvm}, |
| #endif |
| #if GOOS_linux && GOARCH_arm64 |
| {"test_syzos", test_syzos}, |
| #endif |
| {"test_cover_filter", test_cover_filter}, |
| {"test_glob", test_glob}, |
| }; |
| |
| static int run_tests(const char* test) |
| { |
| int ret = 0; |
| for (size_t i = 0; i < ARRAY_SIZE(tests); i++) { |
| const char* name = tests[i].name; |
| if (test && strcmp(test, name)) |
| continue; |
| printf("=== RUN %s\n", name); |
| int res = tests[i].f(); |
| ret |= res > 0; |
| const char* strres = res < 0 ? "SKIP" : (res > 0 ? "FAIL" : "OK"); |
| printf("--- %-4s %s\n", strres, name); |
| } |
| return ret; |
| } |