159 lines
4.1 KiB
C
159 lines
4.1 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#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..."));
|
|
}
|