#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #define PROBE_BUFFER_SIZE 2048 static const char pattern[] = "p15-019-safe-target/"; static int fail(const char *message) { fprintf(stderr, "%s\n", message); return (1); } static int parse_positive(const char *text, long minimum, long maximum, long *value) { char *end; long parsed; errno = 0; parsed = strtol(text, &end, 10); if (errno != 0 || *text == '\0' || *end != '\0' || parsed < minimum || parsed > maximum) return (-1); *value = parsed; return (0); } static void fill_expected(char *buffer, size_t length) { size_t index; for (index = 0; index < length; index++) buffer[index] = pattern[index % (sizeof(pattern) - 1)]; } static int readlink_pass(const char *path, size_t expected_length) { char actual[PROBE_BUFFER_SIZE]; char expected[PROBE_BUFFER_SIZE]; ssize_t length; if (expected_length + 1 > sizeof(actual)) return (fail("expected length exceeds probe buffer")); memset(actual, 0xa5, sizeof(actual)); fill_expected(expected, expected_length); errno = 0; length = readlink(path, actual, sizeof(actual)); if (length < 0) return (fail("readlink-pass returned an error")); if ((size_t)length != expected_length) return (fail("readlink-pass returned the wrong length")); if (memcmp(actual, expected, expected_length) != 0) return (fail("readlink-pass returned the wrong target")); if ((unsigned char)actual[expected_length] != 0xa5) return (fail("readlink-pass appended or copied an extra byte")); return (0); } static int readlink_errno(const char *path, int expected_errno) { char actual[PROBE_BUFFER_SIZE]; ssize_t length; size_t index; memset(actual, 0xa5, sizeof(actual)); errno = 0; length = readlink(path, actual, sizeof(actual)); if (length != -1 || errno != expected_errno) return (fail("readlink-errno returned the wrong result")); for (index = 0; index < sizeof(actual); index++) { if ((unsigned char)actual[index] != 0xa5) return (fail("failed readlink modified the caller buffer")); } return (0); } static int follow_errno(const char *path, int expected_errno) { struct stat status; errno = 0; if (stat(path, &status) != -1 || errno != expected_errno) return (fail("follow-errno returned the wrong result")); return (0); } static int concurrent_readlink(const char *path, size_t length, long workers, long loops) { long worker; int status; pid_t child; for (worker = 0; worker < workers; worker++) { child = fork(); if (child < 0) return (fail("fork failed")); if (child == 0) { for (long iteration = 0; iteration < loops; iteration++) { if (readlink_pass(path, length) != 0) _exit(1); } _exit(0); } } for (worker = 0; worker < workers; worker++) { if (wait(&status) < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) return (fail("concurrent readlink worker failed")); } return (0); } int main(int argc, char **argv) { long first, second, third; if (argc == 4 && strcmp(argv[1], "readlink-pass") == 0) { if (parse_positive(argv[3], 1, PROBE_BUFFER_SIZE - 1, &first) != 0) return (fail("invalid readlink-pass length")); return (readlink_pass(argv[2], (size_t)first)); } if (argc == 4 && strcmp(argv[1], "readlink-errno") == 0) { if (parse_positive(argv[3], 1, 255, &first) != 0) return (fail("invalid expected errno")); return (readlink_errno(argv[2], (int)first)); } if (argc == 4 && strcmp(argv[1], "follow-errno") == 0) { if (parse_positive(argv[3], 1, 255, &first) != 0) return (fail("invalid expected errno")); return (follow_errno(argv[2], (int)first)); } if (argc == 6 && strcmp(argv[1], "concurrent") == 0) { if (parse_positive(argv[3], 1, PROBE_BUFFER_SIZE - 1, &first) != 0 || parse_positive(argv[4], 1, 64, &second) != 0 || parse_positive(argv[5], 1, 1000, &third) != 0) return (fail("invalid concurrent arguments")); return (concurrent_readlink(argv[2], (size_t)first, second, third)); } return (fail("usage: B16-symlink-probe MODE PATH ARG...")); }