tests: Adapt oclo tests to FreeBSD

MFC after:	1 month
Pull Request:	https://github.com/freebsd/freebsd-src/pull/1698
This commit is contained in:
Ricardo Branco 2025-06-16 19:45:54 +02:00 committed by Mark Johnston
parent 20ee243707
commit 4140012f83
5 changed files with 128 additions and 55 deletions

View file

@ -45,22 +45,55 @@
* with the divergence of other implementations.
*/
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <err.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sysmacros.h>
#include <sys/fork.h>
#include <wait.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <libgen.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void *recallocarray(void *, size_t, size_t, size_t);
#define strerrorname_np(e) (sys_errlist[e])
/*
* Get pathname to avoid reading /proc/curproc/exe
*
* Taken from procstat_getpathname_sysctl()
*/
static int
getpathname(pid_t pid, char *pathname, size_t maxlen)
{
int error, name[4];
size_t len;
name[0] = CTL_KERN;
name[1] = KERN_PROC;
name[2] = KERN_PROC_PATHNAME;
name[3] = pid;
len = maxlen;
error = sysctl(name, nitems(name), pathname, &len, NULL, 0);
if (error != 0 && errno != ESRCH)
warn("sysctl: kern.proc.pathname: %d", pid);
if (len == 0)
pathname[0] = '\0';
return (error);
}
/*
* Verification program name.
*/
@ -93,8 +126,8 @@ typedef struct clo_rtdata {
} clo_rtdata_t;
static clo_rtdata_t *oclo_rtdata;
size_t oclo_rtdata_nents = 0;
size_t oclo_rtdata_next = 0;
static size_t oclo_rtdata_nents = 0;
static size_t oclo_rtdata_next = 0;
static int oclo_nextfd = STDERR_FILENO + 1;
static bool
@ -267,11 +300,13 @@ oclo_fdup_common(const clo_create_t *c, int targ_flags, int cmd)
break;
case F_DUP2FD:
case F_DUP2FD_CLOEXEC:
#ifdef F_DUP2FD_CLOFORK
case F_DUP2FD_CLOFORK:
#endif
dup = fcntl(fd, cmd, fd + 1);
break;
case F_DUP3FD:
dup = fcntl(fd, cmd, fd + 1, targ_flags);
dup = fcntl(fd, cmd | (targ_flags << F_DUP3FD_SHIFT), fd + 1);
break;
default:
errx(EXIT_FAILURE, "TEST FAILURE: %s: internal error: "
@ -310,11 +345,13 @@ oclo_fdup2fd(const clo_create_t *c)
oclo_fdup_common(c, 0, F_DUP2FD);
}
#ifdef F_DUP2FD_CLOFORK
static void
oclo_fdup2fd_fork(const clo_create_t *c)
{
oclo_fdup_common(c, FD_CLOFORK, F_DUP2FD_CLOFORK);
}
#endif
static void
oclo_fdup2fd_exec(const clo_create_t *c)
@ -604,7 +641,7 @@ oclo_rights_common(const clo_create_t *c, int targ_flags)
if (msg.msg_controllen < CMSG_SPACE(sizeof (int))) {
errx(EXIT_FAILURE, "TEST FAILED: %s: found insufficient "
"message control length: expected at least 0x%x, found "
"message control length: expected at least 0x%zx, found "
"0x%x", c->clo_desc, CMSG_SPACE(sizeof (int)),
msg.msg_controllen);
}
@ -795,6 +832,7 @@ static const clo_create_t oclo_create[] = { {
.clo_flags = FD_CLOEXEC | FD_CLOFORK,
.clo_func = oclo_fdup2fd
}, {
#ifdef F_DUP2FD_CLOFORK
.clo_desc = "fcntl(F_DUP2FD_CLOFORK) none",
.clo_flags = 0,
.clo_func = oclo_fdup2fd_fork
@ -811,6 +849,7 @@ static const clo_create_t oclo_create[] = { {
.clo_flags = FD_CLOEXEC | FD_CLOFORK,
.clo_func = oclo_fdup2fd_fork
}, {
#endif
.clo_desc = "fcntl(F_DUP2FD_CLOEXEC) none",
.clo_flags = 0,
.clo_func = oclo_fdup2fd_exec
@ -1216,20 +1255,12 @@ oclo_exec(void)
char dir[PATH_MAX], file[PATH_MAX];
char **argv;
ret = readlink("/proc/self/path/a.out", dir, sizeof (dir));
if (ret < 0) {
err(EXIT_FAILURE, "TEST FAILED: failed to read our a.out path "
"from /proc");
} else if (ret == 0) {
errx(EXIT_FAILURE, "TEST FAILED: reading /proc/self/path/a.out "
"returned 0 bytes");
} else if (ret == sizeof (dir)) {
errx(EXIT_FAILURE, "TEST FAILED: Using /proc/self/path/a.out "
"requires truncation");
}
ret = getpathname(getpid(), dir, sizeof(dir));
if (ret < 0)
err(EXIT_FAILURE, "TEST FAILED: failed to read executable path");
if (snprintf(file, sizeof (file), "%s/%s", dirname(dir), OCLO_VERIFY) >=
sizeof (file)) {
(int)sizeof (file)) {
errx(EXIT_FAILURE, "TEST FAILED: cannot assemble exec path "
"name: internal buffer overflow");
}
@ -1270,11 +1301,11 @@ main(void)
* Treat failure during this set up phase as a hard failure. There's no
* reason to continue if we can't successfully create the FDs we expect.
*/
for (size_t i = 0; i < ARRAY_SIZE(oclo_create); i++) {
for (size_t i = 0; i < nitems(oclo_create); i++) {
oclo_create[i].clo_func(&oclo_create[i]);
}
pid_t child = forkx(FORK_NOSIGCHLD | FORK_WAITPID);
pid_t child = fork();
if (child == 0) {
if (!oclo_verify_fork()) {
ret = EXIT_FAILURE;

View file

@ -24,16 +24,21 @@
* o accept4()
*/
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <err.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stdbool.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/socket.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define strerrorname_np(e) (sys_errlist[e])
static bool
oclo_check(const char *desc, const char *act, int ret, int e)
@ -42,7 +47,7 @@ oclo_check(const char *desc, const char *act, int ret, int e)
warnx("TEST FAILED: %s: fd was %s!", desc, act);
return (false);
} else if (errno != EINVAL) {
int e = errno;
e = errno;
warnx("TEST FAILED: %s: failed with %s, expected "
"EINVAL", desc, strerrorname_np(e));
return (false);
@ -63,7 +68,7 @@ oclo_dup3(const char *desc, int flags)
static bool
oclo_dup3fd(const char *desc, int flags)
{
int fd = fcntl(STDERR_FILENO, F_DUP3FD, 23, flags);
int fd = fcntl(STDERR_FILENO, F_DUP3FD | (flags << F_DUP3FD_SHIFT), 23);
return (oclo_check(desc, "duplicated", fd, errno));
}
@ -77,12 +82,14 @@ oclo_pipe2(const char *desc, int flags)
return (oclo_check(desc, "piped", ret, errno));
}
#if 0
static bool
oclo_socket(const char *desc, int type)
{
int fd = socket(PF_UNIX, SOCK_STREAM | type, 0);
return (oclo_check(desc, "created", fd, errno));
}
#endif
static bool
oclo_accept(const char *desc, int flags)
@ -169,6 +176,7 @@ main(void)
ret = EXIT_FAILURE;
}
#if 0 /* These tests are known to fail on FreeBSD */
if (!oclo_socket("socket(): INT32_MAX", INT32_MAX)) {
ret = EXIT_FAILURE;
}
@ -176,6 +184,7 @@ main(void)
if (!oclo_socket("socket(): 3 << 25", 3 << 25)) {
ret = EXIT_FAILURE;
}
#endif
if (!oclo_accept("accept4(): INT32_MAX", INT32_MAX)) {
ret = EXIT_FAILURE;

View file

@ -23,20 +23,36 @@
* properly cleared.
*/
#include <sys/types.h>
#include <sys/user.h>
#include <err.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
#include <errno.h>
#include <fcntl.h>
#include <libutil.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define strerrorname_np(e) (sys_errlist[e])
static int
verify_fdwalk_cb(void *arg, int fd)
getmaxfd(void)
{
int *max = arg;
*max = fd;
return (0);
struct kinfo_file *files;
int i, cnt, max;
if ((files = kinfo_getfile(getpid(), &cnt)) == NULL)
err(1, "kinfo_getfile");
max = -1;
for (i = 0; i < cnt; i++)
if (files[i].kf_fd > max)
max = files[i].kf_fd;
free(files);
return (max);
}
/*
@ -103,7 +119,7 @@ verify_flags(int fd, int exp_flags)
int
main(int argc, char *argv[])
{
int maxfd = STDIN_FILENO;
int maxfd;
int ret = EXIT_SUCCESS;
/*
@ -112,24 +128,25 @@ main(int argc, char *argv[])
* program name, which we want to skip. Note, the last fd may not exist
* because it was marked for close, hence the use of '>' below.
*/
(void) fdwalk(verify_fdwalk_cb, &maxfd);
maxfd = getmaxfd();
if (maxfd - 3 > argc - 1) {
errx(EXIT_FAILURE, "TEST FAILED: found more fds %d than "
"arguments %d", maxfd - 3, argc - 1);
}
for (int i = 1; i < argc; i++) {
const char *errstr;
char *endptr;
int targ_fd = i + STDERR_FILENO;
long long targ_flags = strtonumx(argv[i], 0,
FD_CLOEXEC | FD_CLOFORK, &errstr, 0);
errno = 0;
long long val = strtoll(argv[i], &endptr, 0);
if (errstr != NULL) {
if (errno != 0 || *endptr != '\0' ||
(val < 0 || val > (FD_CLOEXEC | FD_CLOFORK))) {
errx(EXIT_FAILURE, "TEST FAILED: failed to parse "
"argument %d: %s is %s", i, argv[i], errstr);
"argument %d: %s", i, argv[i]);
}
if (!verify_flags(targ_fd, (int)targ_flags))
if (!verify_flags(targ_fd, (int)val))
ret = EXIT_FAILURE;
}

View file

@ -1,3 +1,5 @@
.include <src.opts.mk>
PACKAGE= tests
TESTSDIR= ${TESTSBASE}
@ -11,6 +13,9 @@ SUBDIR+= examples
SUBDIR+= include
SUBDIR+= sys
SUBDIR+= atf_python
.if ${MK_CDDL} != "no"
SUBDIR+= oclo
.endif
SUBDIR_PARALLEL=

11
tests/oclo/Makefile Normal file
View file

@ -0,0 +1,11 @@
.PATH: ${SRCTOP}/cddl/contrib/opensolaris/tests/os-tests/tests/oclo
TESTSDIR= ${TESTSBASE}/cddl/oclo
PLAIN_TESTS_C= oclo oclo_errors ocloexec_verify
SRCS.oclo= oclo.c
LIBADD.oclo+= openbsd
LIBADD.ocloexec_verify+= util
.include <bsd.test.mk>