update
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/extattr.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int
|
||||
fail(const char *message)
|
||||
{
|
||||
fprintf(stderr, "%s\n", message);
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
get_repeat(const char *path, const char *name, size_t expected_size)
|
||||
{
|
||||
uint8_t first[1024], second[1024];
|
||||
ssize_t first_size, second_size;
|
||||
|
||||
if (expected_size > sizeof(first))
|
||||
return (fail("xattr size exceeds probe buffer"));
|
||||
memset(first, 0xa5, sizeof(first));
|
||||
memset(second, 0x5a, sizeof(second));
|
||||
errno = 0;
|
||||
first_size = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name,
|
||||
first, sizeof(first));
|
||||
if (first_size != (ssize_t)expected_size || errno != 0)
|
||||
return (fail("first xattr read returned the wrong size or errno"));
|
||||
errno = 0;
|
||||
second_size = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name,
|
||||
second, sizeof(second));
|
||||
if (second_size != first_size || errno != 0 ||
|
||||
memcmp(first, second, expected_size) != 0)
|
||||
return (fail("repeated xattr read changed its result"));
|
||||
for (size_t offset = expected_size; offset < sizeof(first); offset++) {
|
||||
if (first[offset] != 0xa5 || second[offset] != 0x5a)
|
||||
return (fail("xattr read wrote beyond its value"));
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
get_errno(const char *path, const char *name, int expected_errno)
|
||||
{
|
||||
uint8_t value[32];
|
||||
ssize_t size;
|
||||
|
||||
memset(value, 0xa5, sizeof(value));
|
||||
errno = 0;
|
||||
size = extattr_get_file(path, EXTATTR_NAMESPACE_USER, name, value,
|
||||
sizeof(value));
|
||||
if (size != -1 || errno != expected_errno) {
|
||||
fprintf(stderr, "xattr errno mismatch: size=%zd errno=%d expected=%d\n",
|
||||
size, errno, expected_errno);
|
||||
return (fail("xattr returned the wrong positive errno"));
|
||||
}
|
||||
for (size_t offset = 0; offset < sizeof(value); offset++) {
|
||||
if (value[offset] != 0xa5)
|
||||
return (fail("failed xattr lookup modified the output"));
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
list_user(const char *path)
|
||||
{
|
||||
uint8_t *buffer;
|
||||
ssize_t size;
|
||||
size_t offset;
|
||||
unsigned int seen;
|
||||
|
||||
errno = 0;
|
||||
size = extattr_list_file(path, EXTATTR_NAMESPACE_USER, NULL, 0);
|
||||
if (size <= 0 || errno != 0)
|
||||
return (fail("xattr list sizing failed"));
|
||||
buffer = malloc((size_t)size);
|
||||
if (buffer == NULL)
|
||||
return (fail("xattr list allocation failed"));
|
||||
if (extattr_list_file(path, EXTATTR_NAMESPACE_USER, buffer,
|
||||
(size_t)size) != size) {
|
||||
free(buffer);
|
||||
return (fail("xattr list transfer failed"));
|
||||
}
|
||||
seen = 0;
|
||||
for (offset = 0; offset < (size_t)size;) {
|
||||
const char *name;
|
||||
uint8_t name_size;
|
||||
|
||||
name_size = buffer[offset++];
|
||||
if (name_size == 0 || name_size > (size_t)size - offset) {
|
||||
free(buffer);
|
||||
return (fail("xattr list record is malformed"));
|
||||
}
|
||||
name = (const char *)(buffer + offset);
|
||||
if (name_size == sizeof("inline") - 1 &&
|
||||
memcmp(name, "inline", name_size) == 0) {
|
||||
if ((seen & 1U) != 0) {
|
||||
free(buffer);
|
||||
return (fail("xattr list duplicated inline"));
|
||||
}
|
||||
seen |= 1U;
|
||||
} else if (name_size == sizeof("shared") - 1 &&
|
||||
memcmp(name, "shared", name_size) == 0) {
|
||||
if ((seen & 2U) != 0) {
|
||||
free(buffer);
|
||||
return (fail("xattr list duplicated shared"));
|
||||
}
|
||||
seen |= 2U;
|
||||
} else {
|
||||
free(buffer);
|
||||
return (fail("xattr list returned an unexpected name"));
|
||||
}
|
||||
offset += name_size;
|
||||
}
|
||||
free(buffer);
|
||||
return (seen == 3U ? 0 : fail("xattr list omitted a name"));
|
||||
}
|
||||
|
||||
static int
|
||||
valid(const char *path)
|
||||
{
|
||||
if (get_repeat(path, "inline", 127) != 0 ||
|
||||
get_repeat(path, "shared", 511) != 0 ||
|
||||
list_user(path) != 0 ||
|
||||
get_errno(path, "missing", ENOATTR) != 0)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
corrupt(const char *path)
|
||||
{
|
||||
return (get_errno(path, "inline", EINTEGRITY));
|
||||
}
|
||||
|
||||
static int
|
||||
concurrent(const char *path, long workers, long loops)
|
||||
{
|
||||
int status;
|
||||
pid_t child;
|
||||
|
||||
for (long worker = 0; worker < workers; worker++) {
|
||||
child = fork();
|
||||
if (child < 0)
|
||||
return (fail("xattr worker fork failed"));
|
||||
if (child == 0) {
|
||||
for (long iteration = 0; iteration < loops; iteration++) {
|
||||
if (valid(path) != 0)
|
||||
_exit(1);
|
||||
}
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
for (long worker = 0; worker < workers; worker++) {
|
||||
if (wait(&status) < 0 || !WIFEXITED(status) ||
|
||||
WEXITSTATUS(status) != 0)
|
||||
return (fail("xattr worker failed"));
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *end;
|
||||
long workers, loops;
|
||||
|
||||
if (argc == 3 && strcmp(argv[1], "valid") == 0)
|
||||
return (valid(argv[2]));
|
||||
if (argc == 3 && strcmp(argv[1], "corrupt") == 0)
|
||||
return (corrupt(argv[2]));
|
||||
if (argc == 5 && strcmp(argv[1], "concurrent") == 0) {
|
||||
errno = 0;
|
||||
workers = strtol(argv[3], &end, 10);
|
||||
if (errno != 0 || end[0] != '\0' || workers < 1 || workers > 64)
|
||||
return (fail("invalid worker count"));
|
||||
errno = 0;
|
||||
loops = strtol(argv[4], &end, 10);
|
||||
if (errno != 0 || end[0] != '\0' || loops < 1 || loops > 100)
|
||||
return (fail("invalid loop count"));
|
||||
return (concurrent(argv[2], workers, loops));
|
||||
}
|
||||
return (fail("usage: B19a-xattr-probe valid|corrupt|concurrent ..."));
|
||||
}
|
||||
Reference in New Issue
Block a user