* tests/futex.c: New file. * tests/futex.test: Likewise. * tests/Makefile.am (check_PROGRAMS): Add futex. (DECODER_TESTS): Add futex.test. --- Changes since v2: * Commands not available on the old kernels (2.6.18 onwards) are wrapped in CHECK_FUTEX_ENOSYS which prevents failure in case errno is ENOSYS and is not expected one. * Checks for PI mutexes do no rely on PID 2 being kthreadd (which is not true when process namespaces are employed, for example). * Printing return code is done via retstr() which utilizes errno2name for errno printing. * All headers except asm/unistd.h moved under ifdef __NR_futex. * <linux/futex.h> inclusion removed since it is proven broken in some distros * Added definitions for FUTEX_PRIVATE_FLAG, FUTEX_CLOCK_REALTIME, FUTEX_CMD_MASK * Skip on EPERM since it is sometimes returned for otherwise perfectly valid commands (WAKE_OP on AArch64) * Minor formatting fixes.
tests/Makefile.am | 2 + tests/futex.c | 692 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/futex.test | 8 + 3 files changed, 702 insertions(+) create mode 100644 tests/futex.c create mode 100755 tests/futex.test diff --git a/tests/Makefile.am b/tests/Makefile.am index 0c28f06..93ed37f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -125,6 +125,7 @@ check_PROGRAMS = \ fsync \ ftruncate \ ftruncate64 \ + futex \ futimesat \ get_mempolicy \ getcwd \ @@ -457,6 +458,7 @@ DECODER_TESTS = \ ftruncate.test \ ftruncate64.test \ futimesat.test \ + futex.test \ get_mempolicy.test \ getcwd.test \ getdents.test \ diff --git a/tests/futex.c b/tests/futex.c new file mode 100644 index 0000000..dd6955e --- /dev/null +++ b/tests/futex.c @@ -0,0 +1,692 @@ +#include "tests.h" + +#include <asm/unistd.h> + +#ifdef __NR_futex + +# include <errno.h> +# include <stdarg.h> +# include <stdbool.h> +# include <stdio.h> +# include <stdint.h> +# include <unistd.h> + +# include <sys/time.h> + +# ifndef FUTEX_PRIVATE_FLAG +# define FUTEX_PRIVATE_FLAG 128 +# endif +# ifndef FUTEX_CLOCK_REALTIME +# define FUTEX_CLOCK_REALTIME 256 +# endif +# ifndef FUTEX_CMD_MASK +# define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) +# endif + +# include "xlat.h" +# include "xlat/futexops.h" +# include "xlat/futexwakeops.h" +# include "xlat/futexwakecmps.h" + +static struct timespec *tmout; + +void futex_error(int *uaddr, int op, unsigned long val, unsigned long timeout, + int *uaddr2, unsigned long val3, int rc) +{ + perror_msg_and_fail("futex(%p, %#x, %#x, %#lx, %p, %#x) = %d", + uaddr, op, (unsigned)val, timeout, uaddr, (unsigned)val3, rc); +} + +# define CHECK_FUTEX_GENERIC(uaddr, op, val, timeout, uaddr2, val3, check, \ + enosys) \ + do { \ + rc = syscall(__NR_futex, (uaddr), (op), (val), (timeout), \ + (uaddr2), (val3)); \ + /* It is here due to EPERM on WAKE_OP on AArch64 */ \ + if ((rc == -1) && (errno == EPERM)) \ + break; \ + if (enosys && (rc == -1) && (errno == ENOSYS)) \ + break; \ + if (!(check)) \ + futex_error((uaddr), (op), (val), \ + (unsigned long)(timeout), (int *)(uaddr2), \ + (val3), rc); \ + } while (0) + +# define CHECK_FUTEX_ENOSYS(uaddr, op, val, timeout, uaddr2, val3, check) \ + CHECK_FUTEX_GENERIC(uaddr, op, val, timeout, uaddr2, val3, check, 1) + +# define CHECK_FUTEX(uaddr, op, val, timeout, uaddr2, val3, check) \ + CHECK_FUTEX_GENERIC(uaddr, op, val, timeout, uaddr2, val3, check, 0) + +enum argmask { + ARG3 = 1 << 0, + ARG4 = 1 << 1, + ARG5 = 1 << 2, + ARG6 = 1 << 3, +}; + +void invalid_op(int *val, int op, uint32_t argmask, ...) +{ + static const unsigned long args[] = { + (unsigned long)0xface1e55deadbee1ULL, + (unsigned long)0xface1e56deadbee2ULL, + (unsigned long)0xface1e57deadbee3ULL, + (unsigned long)0xface1e58deadbee4ULL, + }; + /* Since timeout value is copied before full op check, we should provide + * some valid timeout address or NULL */ + int cmd = op & FUTEX_CMD_MASK; + bool valid_timeout = (cmd == FUTEX_WAIT) || (cmd == FUTEX_LOCK_PI) || + (cmd == FUTEX_WAIT_BITSET) || (cmd == FUTEX_WAIT_REQUEUE_PI); + bool timeout_is_val2 = (cmd == FUTEX_REQUEUE) || + (cmd == FUTEX_CMP_REQUEUE) || (cmd == FUTEX_WAKE_OP) || + (cmd == FUTEX_CMP_REQUEUE_PI); + const char *fmt; + int saved_errno; + int rc; + int i; + va_list ap; + + + CHECK_FUTEX(val, op, args[0], valid_timeout ? 0 : args[1], args[2], + args[3], (rc == -1) && (errno == ENOSYS)); + saved_errno = errno; + printf("futex(%p, %#x /* FUTEX_??? */", val, op); + + va_start(ap, argmask); + + for (i = 0; i < 4; i++) { + if (argmask & (1 << i)) { + fmt = va_arg(ap, const char *); + + printf(", "); + + if (((1 << i) == ARG3) || ((1 << i) == ARG6) || + (((1 << i) == ARG4) && timeout_is_val2)) + printf(fmt, (unsigned)args[i]); + else + printf(fmt, args[i]); + } + } + + va_end(ap); + + errno = saved_errno; + printf(") = -1 ENOSYS (%m)\n"); +} + +const char *retstr(int rc) +{ + enum { RES_BUF_SIZE = 256 }; + static char buf[RES_BUF_SIZE]; + + if (rc == 0) + return "0"; + + snprintf(buf, sizeof(buf), "-1 %s (%m)", errno2name()); + + return buf; +} + +# define CHECK_INVALID_CLOCKRT(op, ...) \ + do { \ + invalid_op(uaddr, FUTEX_CLOCK_REALTIME | (op), __VA_ARGS__); \ + invalid_op(uaddr, FUTEX_CLOCK_REALTIME | FUTEX_PRIVATE_FLAG | \ + (op), __VA_ARGS__); \ + } while (0) + +/* Value which differs from one stored in int *val */ +# define VAL ((unsigned long)0xbadda7a0facefeedLLU) +# define VAL_PR ((unsigned)VAL) + +# define VAL2 ((unsigned long)0xbadda7a0ca7b100dLLU) +# define VAL2_PR ((unsigned)VAL2) + +# define VAL3 ((unsigned long)0xbadda7a09caffee1LLU) +# define VAL3_PR ((unsigned)VAL3) + +int +main(int argc, char *argv[]) +{ + int *uaddr = tail_alloc(sizeof(*uaddr)); + int *uaddr2 = tail_alloc(sizeof(*uaddr2)); + int rc; + unsigned i; + unsigned j; + + uaddr[0] = 0x1deadead; + uaddr2[0] = 0xbadf00d; + + tmout = tail_alloc(sizeof(*tmout)); + tmout->tv_sec = 123; + tmout->tv_nsec = 0xbadc0de; + + /* FUTEX_WAIT - check whether uaddr == val and sleep + * Possible flags: PRIVATE, CLOCK_RT (since 4.5) + * 1. uaddr - futex address + * 2. op - FUTEX_WAIT + * 3. val - expected value + * 4. timeout - address to timespec with timeout + * 5. uaddr2 - not used + * 6. val3 - not used + */ + + /* uaddr is NULL */ + CHECK_FUTEX(NULL, FUTEX_WAIT, VAL, tmout, uaddr2, VAL3, + (rc == -1) && (errno == EFAULT)); + printf("futex(NULL, FUTEX_WAIT, %u, {%jd, %jd}) = %s\n", + VAL_PR, (intmax_t)tmout->tv_sec, (intmax_t)tmout->tv_nsec, + retstr(rc)); + + /* uaddr is faulty */ + CHECK_FUTEX(uaddr + 1, FUTEX_WAIT, VAL, tmout, uaddr2, VAL3, + (rc == -1) && (errno == EFAULT)); + printf("futex(%p, FUTEX_WAIT, %u, {%jd, %jd}) = %s\n", + uaddr + 1, VAL_PR, (intmax_t)tmout->tv_sec, + (intmax_t)tmout->tv_nsec, retstr(rc)); + + /* timeout is faulty */ + CHECK_FUTEX(uaddr, FUTEX_WAIT, VAL, tmout + 1, uaddr2, VAL3, + (rc == -1) && (errno == EFAULT)); + printf("futex(%p, FUTEX_WAIT, %u, %p) = %s\n", + uaddr, 0xfacefeed, tmout + 1, retstr(rc)); + + /* uaddr is not as provided; uaddr2 is faulty but ignored */ + CHECK_FUTEX(uaddr, FUTEX_WAIT, VAL, tmout, uaddr2 + 1, VAL3, + (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_WAIT, %u, {%jd, %jd}) = %s\n", + uaddr, VAL_PR, (intmax_t)tmout->tv_sec, + (intmax_t)tmout->tv_nsec, retstr(rc)); + + /* uaddr is not as provided; uaddr2 is faulty but ignored */ + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_PRIVATE_FLAG | FUTEX_WAIT, VAL, tmout, + uaddr2 + 1, VAL3, (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_WAIT_PRIVATE, %u, {%jd, %jd}) = %s\n", + uaddr, VAL_PR, (intmax_t)tmout->tv_sec, + (intmax_t)tmout->tv_nsec, retstr(rc)); + + /* Next 2 tests are with CLOCKRT bit set */ + + /* Valid after v4.4-rc2-27-g337f130 */ + CHECK_FUTEX_ENOSYS(uaddr, + FUTEX_CLOCK_REALTIME | FUTEX_WAIT, + VAL, tmout, uaddr2, VAL3, (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_WAIT|FUTEX_CLOCK_REALTIME, %u, " + "{%jd, %jd}) = %s\n", uaddr, VAL_PR, + (intmax_t)tmout->tv_sec, (intmax_t)tmout->tv_nsec, retstr(rc)); + + CHECK_FUTEX_ENOSYS(uaddr, + FUTEX_CLOCK_REALTIME | FUTEX_PRIVATE_FLAG | FUTEX_WAIT , + VAL, tmout, uaddr2, 0, (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_WAIT_PRIVATE|FUTEX_CLOCK_REALTIME, %u, " + "{%jd, %jd}) = %s\n", uaddr, VAL_PR, + (intmax_t)tmout->tv_sec, (intmax_t)tmout->tv_nsec, retstr(rc)); + + /* FUTEX_WAIT_BITSET - FUTEX_WAIT which provides additional bitmask + * which should be matched at least in one bit with + * wake mask in order to wake. + * Possible flags: PRIVATE, CLOCKRT + * 1. uaddr - futex address + * 2. op - FUTEX_TRYLOCK_PI + * 3. val - expected value stored in uaddr + * 4. timeout - timeout + * 5. uaddr2 - not used + * 6. val3 - bitmask + */ + + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_WAIT_BITSET, VAL, tmout, uaddr2 + 1, + VAL3, (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_WAIT_BITSET, %u, {%jd, %jd}, %#x) = %s\n", + uaddr, VAL_PR, (intmax_t)tmout->tv_sec, + (intmax_t)tmout->tv_nsec, VAL3_PR, retstr(rc)); + + /* val3 of 0 is invalid */ + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_WAIT_BITSET, VAL, tmout, uaddr2 + 1, 0, + (rc == -1) && (errno == EINVAL)); + printf("futex(%p, FUTEX_WAIT_BITSET, %u, {%jd, %jd}, %#x) = %s\n", + uaddr, VAL_PR, (intmax_t)tmout->tv_sec, + (intmax_t)tmout->tv_nsec, 0, retstr(rc)); + + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_PRIVATE_FLAG | FUTEX_WAIT_BITSET, VAL, + tmout, uaddr2 + 1, VAL3, (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_WAIT_BITSET_PRIVATE, %u, {%jd, %jd}, %#x) = " + "%s\n", uaddr, VAL_PR, (intmax_t)tmout->tv_sec, + (intmax_t)tmout->tv_nsec, VAL3_PR, retstr(rc)); + + /* Next 3 tests are with CLOCKRT bit set */ + + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_CLOCK_REALTIME | FUTEX_WAIT_BITSET, VAL, + tmout, uaddr2 + 1, VAL3, (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_WAIT_BITSET|FUTEX_CLOCK_REALTIME, %u, " + "{%jd, %jd}, %#x) = %s\n", uaddr, VAL_PR, + (intmax_t)tmout->tv_sec, (intmax_t)tmout->tv_nsec, VAL3_PR, + retstr(rc)); + + /* val3 of 0 is invalid */ + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_CLOCK_REALTIME | FUTEX_WAIT_BITSET, VAL, + tmout, uaddr2 + 1, 0, (rc == -1) && (errno == EINVAL)); + printf("futex(%p, FUTEX_WAIT_BITSET|FUTEX_CLOCK_REALTIME, %u, " + "{%jd, %jd}, %#x) = %s\n", uaddr, VAL_PR, + (intmax_t)tmout->tv_sec, (intmax_t)tmout->tv_nsec, 0, + retstr(rc)); + + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_CLOCK_REALTIME | FUTEX_PRIVATE_FLAG | + FUTEX_WAIT_BITSET, VAL, tmout, uaddr2 + 1, VAL3, + (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, %u, " + "{%jd, %jd}, %#x) = %s\n", uaddr, VAL_PR, + (intmax_t)tmout->tv_sec, (intmax_t)tmout->tv_nsec, VAL3_PR, + retstr(rc)); + + /* FUTEX_WAKE - wake val processes waiting for uaddr + * Possible flags: PRIVATE + * 1. uaddr - futex address + * 2. op - FUTEX_WAKE + * 3. val - how many processes to wake + * 4. timeout - not used + * 5. uaddr2 - not used + * 6. val3 - not used + */ + + /* Zero processes to wake is not a good idea, but it should return 0 */ + CHECK_FUTEX(uaddr, FUTEX_WAKE, 0, NULL, NULL, 0, (rc == 0)); + printf("futex(%p, FUTEX_WAKE, %u) = %s\n", uaddr, 0, retstr(rc)); + + /* Trying to wake some processes, but there's nothing to wake */ + CHECK_FUTEX(uaddr, FUTEX_WAKE, 10, NULL, NULL, 0, (rc == 0)); + printf("futex(%p, FUTEX_WAKE, %u) = %s\n", uaddr, 10, retstr(rc)); + + /* Trying to wake some processes, but there's nothing to wake */ + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_PRIVATE_FLAG | FUTEX_WAKE, 10, NULL, + NULL, 0, (rc == 0)); + printf("futex(%p, FUTEX_WAKE_PRIVATE, %u) = %s\n", uaddr, 10, + retstr(rc)); + + CHECK_INVALID_CLOCKRT(FUTEX_WAKE, ARG3, "%u"); + + /* FUTEX_WAKE_BITSET - wake val processes waiting for uaddr which has at + * least one common bit with bitset provided in + * val3. + * Possible flags: PRIVATE + * 1. uaddr - futex address + * 2. op - FUTEX_WAKE + * 3. val - how many processes to wake + * 4. timeout - not used + * 5. uaddr2 - not used + * 6. val3 - bitmask + */ + + /* Trying to wake some processes, but there's nothing to wake */ + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_WAKE_BITSET, 10, NULL, NULL, + VAL3, (rc == 0)); + printf("futex(%p, FUTEX_WAKE_BITSET, %u, %#x) = %s\n", uaddr, 10, + VAL3_PR, retstr(rc)); + + /* bitset 0 is invalid */ + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_WAKE_BITSET, 10, NULL, NULL, 0, + (rc == -1) && (errno == EINVAL)); + printf("futex(%p, FUTEX_WAKE_BITSET, %u, %#x) = %s\n", uaddr, 10, 0, + retstr(rc)); + + /* Trying to wake some processes, but there's nothing to wake */ + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_PRIVATE_FLAG | FUTEX_WAKE_BITSET, 10, + NULL, NULL, VAL3, (rc == 0)); + printf("futex(%p, FUTEX_WAKE_BITSET_PRIVATE, %u, %#x) = %s\n", uaddr, + 10, VAL3_PR, retstr(rc)); + + CHECK_INVALID_CLOCKRT(FUTEX_WAKE_BITSET, ARG3 | ARG6, "%u", "%#x"); + + /* FUTEX_FD - deprecated + * Possible flags: PRIVATE + * 1. uaddr - futex address + * 2. op - FUTEX_FD + * 3. val - signal number + * 4. timeout - not used + * 5. uaddr2 - not used + * 6. val3 - not used + */ + + /* FUTEX_FD is not implemented since 2.6.26 */ + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_FD, VAL, NULL, NULL, VAL3, + (rc == -1) && (errno == EINVAL)); + printf("futex(%p, FUTEX_FD, %u) = %s\n", uaddr, VAL_PR, retstr(rc)); + + /* FUTEX_FD is not implemented since 2.6.26 */ + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_PRIVATE_FLAG | FUTEX_FD, VAL, NULL, + NULL, VAL3, (rc == -1) && (errno == EINVAL)); + printf("futex(%p, FUTEX_FD|FUTEX_PRIVATE_FLAG, %u) = %s\n", uaddr, + VAL_PR, retstr(rc)); + + CHECK_INVALID_CLOCKRT(FUTEX_FD, ARG3, "%u"); + + /* FUTEX_REQUEUE - wake val processes and re-queue rest on uaddr2 + * Possible flags: PRIVATE + * 1. uaddr - futex address + * 2. op - FUTEX_REQUEUE + * 3. val - how many processes to wake + * 4. val2 - amount of processes to re-queue on uadr2 + * 5. uaddr2 - another futex address, to re-queue waiting processes on + * 6. val3 - not used + */ + + /* Trying to re-queue some processes but there's nothing to re-queue */ + CHECK_FUTEX(uaddr, FUTEX_REQUEUE, VAL, VAL2, uaddr2, VAL3, + (rc == 0)); + printf("futex(%p, FUTEX_REQUEUE, %u, %u, %p) = %s\n", + uaddr, VAL_PR, VAL2_PR, uaddr2, retstr(rc)); + + /* Trying to re-queue some processes but there's nothing to re-queue */ + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_PRIVATE_FLAG | FUTEX_REQUEUE, VAL, VAL2, + uaddr2, VAL3, (rc == 0)); + printf("futex(%p, FUTEX_REQUEUE_PRIVATE, %u, %u, %p) = %s\n", + uaddr, VAL_PR, VAL2_PR, uaddr2, retstr(rc)); + + CHECK_INVALID_CLOCKRT(FUTEX_REQUEUE, ARG3 | ARG4 | ARG5, "%u", "%u", + "%#lx"); + + /* FUTEX_CMP_REQUEUE - wake val processes and re-queue rest on uaddr2 + * if uaddr has value val3 + * Possible flags: PRIVATE + * 1. uaddr - futex address + * 2. op - FUTEX_CMP_REQUEUE + * 3. val - how many processes to wake + * 4. val2 - amount of processes to re-queue on uadr2 + * 5. uaddr2 - another futex address, to re-queue waiting processes on + * 6. val3 - expected value stored in uaddr + */ + + /* Comparison re-queue with wrong val value */ + CHECK_FUTEX(uaddr, FUTEX_CMP_REQUEUE, VAL, VAL2, uaddr2, VAL3, + (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_CMP_REQUEUE, %u, %u, %p, %u) = %s\n", + uaddr, VAL_PR, VAL2_PR, uaddr2, VAL3_PR, retstr(rc)); + + /* Successful comparison re-queue */ + CHECK_FUTEX(uaddr, FUTEX_CMP_REQUEUE, VAL, VAL2, uaddr2, *uaddr, + (rc == 0)); + printf("futex(%p, FUTEX_CMP_REQUEUE, %u, %u, %p, %u) = %s\n", + uaddr, VAL_PR, VAL2_PR, uaddr2, *uaddr, retstr(rc)); + + /* Successful comparison re-queue */ + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_PRIVATE_FLAG | FUTEX_CMP_REQUEUE, VAL, + VAL2, uaddr2, *uaddr, (rc == 0)); + printf("futex(%p, FUTEX_CMP_REQUEUE_PRIVATE, %u, %u, %p, %u) = %s\n", + uaddr, VAL_PR, VAL2_PR, uaddr2, *uaddr, retstr(rc)); + + CHECK_INVALID_CLOCKRT(FUTEX_CMP_REQUEUE, ARG3 | ARG4 | ARG5 | ARG6, + "%u", "%u", "%#lx", "%u"); + + /* FUTEX_WAKE_OP - wake val processes waiting for uaddr, additionally + * wake val2 processes waiting for uaddr2 in case + * operation encoded in val3 (change of value at uaddr2 + * and comparison of previous value against provided + * constant) succeedes with value at uaddr2. Operation + * result is written to value of uaddr2 (in any case). + * 1. uaddr - futex address + * 2. op - FUTEX_WAKE_OP + * 3. val - how many processes to wake + * 4. val2 - amount of processes to wake in case operation encoded in + * val3 returns true + * 5. uaddr2 - another futex address, for conditional wake of + * additional processes + * 6. val3 - encoded operation: + * 1. bit 31 - if 1 then value stored in field field 4 + * should be interpreted as power of 2. + * 2. 28..30 - arithmetic operation which should be + * applied to previous value stored in + * uaddr2. Values available (from 2005 up to + * 2016): SET. ADD, OR, ANDN, XOR. + * 3. 24..29 - comparison operation which should be + * applied to the old value stored in uaddr2 + * (before arithmetic operation is applied). + * Possible values: EQ, NE, LT, LE, GT, GE. + * 4. 12..23 - Second operand for arithmetic operation. + * If bit 31 is set, it is interpreted as + * power of 2. + * 5. 00..11 - Value against which old value stored in + * uaddr2 is compared. + */ + + static const struct { + uint32_t val; + const char *str; + int err; + const char *errstr; + } wake_ops[] = { + { 0x00000000, "{FUTEX_OP_SET, 0, FUTEX_OP_CMP_EQ, 0}" }, + { 0x00fff000, "{FUTEX_OP_SET, 4095, FUTEX_OP_CMP_EQ, 0}" }, + { 0x00000fff, "{FUTEX_OP_SET, 0, FUTEX_OP_CMP_EQ, 4095}" }, + { 0x00ffffff, "{FUTEX_OP_SET, 4095, FUTEX_OP_CMP_EQ, 4095}" }, + { 0x10000000, "{FUTEX_OP_ADD, 0, FUTEX_OP_CMP_EQ, 0}" }, + { 0x20000000, "{FUTEX_OP_OR, 0, FUTEX_OP_CMP_EQ, 0}" }, + { 0x30000000, "{FUTEX_OP_ANDN, 0, FUTEX_OP_CMP_EQ, 0}" }, + { 0x40000000, "{FUTEX_OP_XOR, 0, FUTEX_OP_CMP_EQ, 0}" }, + { 0x50000000, "{0x5 /* FUTEX_OP_??? */, 0, FUTEX_OP_CMP_EQ, 0}", + ENOSYS, "ENOSYS" }, + { 0x70000000, "{0x7 /* FUTEX_OP_??? */, 0, FUTEX_OP_CMP_EQ, 0}", + ENOSYS, "ENOSYS" }, + { 0x80000000, "{FUTEX_OP_OPARG_SHIFT|FUTEX_OP_SET, 0, " + "FUTEX_OP_CMP_EQ, 0}" }, + { 0xa0caffee, "{FUTEX_OP_OPARG_SHIFT|FUTEX_OP_OR, 3247, " + "FUTEX_OP_CMP_EQ, 4078}" }, + { 0x01000000, "{FUTEX_OP_SET, 0, FUTEX_OP_CMP_NE, 0}" }, + { 0x01234567, "{FUTEX_OP_SET, 564, FUTEX_OP_CMP_NE, 1383}" }, + { 0x02000000, "{FUTEX_OP_SET, 0, FUTEX_OP_CMP_LT, 0}" }, + { 0x03000000, "{FUTEX_OP_SET, 0, FUTEX_OP_CMP_LE, 0}" }, + { 0x04000000, "{FUTEX_OP_SET, 0, FUTEX_OP_CMP_GT, 0}" }, + { 0x05000000, "{FUTEX_OP_SET, 0, FUTEX_OP_CMP_GE, 0}" }, + { 0x06000000, "{FUTEX_OP_SET, 0, 0x6 /* FUTEX_OP_CMP_??? */, " + "0}", ENOSYS, "ENOSYS" }, + { 0x07000000, "{FUTEX_OP_SET, 0, 0x7 /* FUTEX_OP_CMP_??? */, " + "0}", ENOSYS, "ENOSYS" }, + { 0x08000000, "{FUTEX_OP_SET, 0, 0x8 /* FUTEX_OP_CMP_??? */, " + "0}", ENOSYS, "ENOSYS" }, + { 0x0f000000, "{FUTEX_OP_SET, 0, 0xf /* FUTEX_OP_CMP_??? */, " + "0}", ENOSYS, "ENOSYS" }, + { 0xbadfaced, "{FUTEX_OP_OPARG_SHIFT|FUTEX_OP_ANDN, " + "3578, 0xa /* FUTEX_OP_CMP_??? */, 3309}", ENOSYS, + "ENOSYS" }, + { 0xffffffff, "{FUTEX_OP_OPARG_SHIFT|0x7 /* FUTEX_OP_??? */, " + "4095, 0xf /* FUTEX_OP_CMP_??? */, 4095}", ENOSYS, + "ENOSYS" }, + }; + + for (i = 0; i < ARRAY_SIZE(wake_ops); i++) { + for (j = 0; j < 2; j++) { + CHECK_FUTEX_ENOSYS(uaddr, + j ? FUTEX_WAKE_OP_PRIVATE : FUTEX_WAKE_OP, + VAL, i, uaddr2, wake_ops[i].val, (rc == 0)); + printf("futex(%p, FUTEX_WAKE_OP%s, %u, %u, %p, %s) = " + "%s\n", uaddr, j ? "_PRIVATE" : "", VAL_PR, i, + uaddr2, wake_ops[i].str, retstr(rc)); + } + } + + CHECK_INVALID_CLOCKRT(FUTEX_WAKE_OP, ARG3 | ARG4 | ARG5 | ARG6, + "%u", "%u", "%#lx", + /* Decoding of the 0xdeadbee4 value */ + "{FUTEX_OP_OPARG_SHIFT|0x5 /* FUTEX_OP_??? */, 2779, " + "0xe /* FUTEX_OP_CMP_??? */, 3812}"); + + /* FUTEX_LOCK_PI - slow path for mutex lock with process inheritance + * support. Expect that futex has 0 in unlocked case and + * TID of owning process in locked case. Value can also + * contain FUTEX_WAITERS bit signalling the presence of + * waiters queue. + * Possible flags: PRIVATE + * 1. uaddr - futex address + * 2. op - FUTEX_LOCK_PI + * 3. val - not used + * 4. timeout - timeout + * 5. uaddr2 - not used + * 6. val3 - not used + */ + + *uaddr = getpid(); + + CHECK_FUTEX_ENOSYS(uaddr + 1, FUTEX_LOCK_PI, VAL, tmout, uaddr2 + 1, + VAL3, (rc == -1) && (errno == EFAULT)); + printf("futex(%p, FUTEX_LOCK_PI, {%jd, %jd}) = %s\n", + uaddr + 1, (intmax_t)tmout->tv_sec, (intmax_t)tmout->tv_nsec, + retstr(rc)); + + CHECK_FUTEX_ENOSYS(uaddr + 1, FUTEX_PRIVATE_FLAG | FUTEX_LOCK_PI, VAL, + tmout, uaddr2 + 1, VAL3, (rc == -1) && (errno == EFAULT)); + printf("futex(%p, FUTEX_LOCK_PI_PRIVATE, {%jd, %jd}) = %s\n", + uaddr + 1, (intmax_t)tmout->tv_sec, (intmax_t)tmout->tv_nsec, + retstr(rc)); + + /* NULL is passed by invalid_op() in cases valid timeout address is + * needed */ + CHECK_INVALID_CLOCKRT(FUTEX_LOCK_PI, ARG4, "NULL"); + + /* FUTEX_UNLOCK_PI - slow path for mutex unlock with process inheritance + * support. Expected to be called by process in case + * it failed to execute fast path (it usually means + * that FUTEX_WAITERS flag had been set while the lock + * has been held). + * Possible flags: PRIVATE + * 1. uaddr - futex address + * 2. op - FUTEX_UNLOCK_PI + * 3. val - not used + * 4. timeout - not used + * 5. uaddr2 - not used + * 6. val3 - not used + */ + + CHECK_FUTEX_ENOSYS(uaddr + 1, FUTEX_UNLOCK_PI, VAL, tmout, uaddr2 + 1, + VAL3, (rc == -1) && (errno == EFAULT)); + printf("futex(%p, FUTEX_UNLOCK_PI) = %s\n", uaddr + 1, retstr(rc)); + + CHECK_FUTEX_ENOSYS(uaddr + 1, FUTEX_PRIVATE_FLAG | FUTEX_UNLOCK_PI, VAL, + tmout, uaddr2 + 1, VAL3, (rc == -1) && (errno == EFAULT)); + printf("futex(%p, FUTEX_UNLOCK_PI_PRIVATE) = %s\n", uaddr +1, + retstr(rc)); + + CHECK_INVALID_CLOCKRT(FUTEX_UNLOCK_PI, 0); + + /* FUTEX_TRYLOCK_PI - slow path for mutex trylock with process + * inheritance support. + * Possible flags: PRIVATE + * 1. uaddr - futex address + * 2. op - FUTEX_TRYLOCK_PI + * 3. val - not used + * 4. timeout - not used + * 5. uaddr2 - not used + * 6. val3 - not used + */ + + CHECK_FUTEX_ENOSYS(uaddr + 1, FUTEX_TRYLOCK_PI, VAL, tmout, uaddr2 + 1, + VAL3, (rc == -1) && (errno == EFAULT)); + printf("futex(%p, FUTEX_TRYLOCK_PI) = %s\n", uaddr + 1, retstr(rc)); + + CHECK_FUTEX_ENOSYS(uaddr + 1, FUTEX_PRIVATE_FLAG | FUTEX_TRYLOCK_PI, + VAL, tmout, uaddr2 + 1, VAL3, (rc == -1) && (errno == EFAULT)); + printf("futex(%p, FUTEX_TRYLOCK_PI_PRIVATE) = %s\n", uaddr + 1, + retstr(rc)); + + CHECK_INVALID_CLOCKRT(FUTEX_TRYLOCK_PI, 0); + + /* FUTEX_WAIT_REQUEUE_PI - kernel-side handling of special case when + * processes should be re-queued on PI-aware + * futexes. This is so special since PI futexes + * utilize rt_mutex and it should be at no time + * left free with a wait queue, so this should + * be performed atomically in-kernel. + * Possible flags: PRIVATE, CLOCKRT + * 1. uaddr - futex address + * 2. op - FUTEX_WAIT_REQUEUE_PI + * 3. val - expected value stored in uaddr + * 4. timeout - timeout + * 5. uaddr2 - (PI-aware) futex address to requeue process on + * 6. val3 - not used (in kernel, it always initialized to + * FUTEX_BITSET_MATCH_ANY and passed to + * futex_wait_requeue_pi()) + */ + + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_WAIT_REQUEUE_PI, VAL, tmout, uaddr2, + VAL3, (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_WAIT_REQUEUE_PI, %u, {%jd, %jd}, %p) = %s\n", + uaddr, VAL_PR, (intmax_t)tmout->tv_sec, + (intmax_t)tmout->tv_nsec, uaddr2, retstr(rc)); + + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_PRIVATE_FLAG | FUTEX_WAIT_REQUEUE_PI, + VAL, tmout, uaddr2, VAL3, (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_WAIT_REQUEUE_PI_PRIVATE, %u, {%jd, %jd}, %p) " + "= %s\n", uaddr, VAL_PR, (intmax_t)tmout->tv_sec, + (intmax_t)tmout->tv_nsec, uaddr2, retstr(rc)); + + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_CLOCK_REALTIME | FUTEX_WAIT_REQUEUE_PI, + VAL, tmout, uaddr2, VAL3, (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_WAIT_REQUEUE_PI|FUTEX_CLOCK_REALTIME, %u, " + "{%jd, %jd}, %p) = %s\n", uaddr, VAL_PR, + (intmax_t)tmout->tv_sec, (intmax_t)tmout->tv_nsec, uaddr2, + retstr(rc)); + + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_CLOCK_REALTIME | FUTEX_PRIVATE_FLAG | + FUTEX_WAIT_REQUEUE_PI, VAL, tmout, uaddr2, VAL3, + (rc == -1) && (errno == EAGAIN)); + printf("futex(%p, FUTEX_WAIT_REQUEUE_PI_PRIVATE|FUTEX_CLOCK_REALTIME, " + "%u, {%jd, %jd}, %p) = %s\n", uaddr, VAL_PR, + (intmax_t)tmout->tv_sec, (intmax_t)tmout->tv_nsec, uaddr2, + retstr(rc)); + + /* FUTEX_CMP_REQUEUE_PI - version of FUTEX_CMP_REQUEUE which re-queues + * on PI-aware futex. + * Possible flags: PRIVATE + * 1. uaddr - futex address + * 2. op - FUTEX_CMP_REQUEUE + * 3. val - how many processes to wake + * 4. val2 - amount of processes to re-queue on uadr2 + * 5. uaddr2 - (PI-aware) futex address, to re-queue waiting processes + * on + * 6. val3 - expected value stored in uaddr + */ + + /* All these should fail with EINVAL since we try to re-queue to non-PI + * futex. + */ + + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_CMP_REQUEUE_PI, VAL, VAL2, uaddr2, VAL3, + (rc == -1) && (errno == EINVAL)); + printf("futex(%p, FUTEX_CMP_REQUEUE_PI, %u, %u, %p, %u) = %s\n", + uaddr, VAL_PR, VAL2_PR, uaddr2, VAL3_PR, retstr(rc)); + + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_CMP_REQUEUE_PI, VAL, VAL2, uaddr2, + *uaddr, (rc == -1) && (errno == EINVAL)); + printf("futex(%p, FUTEX_CMP_REQUEUE_PI, %u, %u, %p, %u) = %s\n", + uaddr, VAL_PR, VAL2_PR, uaddr2, *uaddr, retstr(rc)); + + CHECK_FUTEX_ENOSYS(uaddr, FUTEX_PRIVATE_FLAG | FUTEX_CMP_REQUEUE_PI, + VAL, VAL2, uaddr2, *uaddr, (rc == -1) && (errno == EINVAL)); + printf("futex(%p, FUTEX_CMP_REQUEUE_PI_PRIVATE, %u, %u, %p, %u) = %s\n", + uaddr, VAL_PR, VAL2_PR, uaddr2, *uaddr, retstr(rc)); + + CHECK_INVALID_CLOCKRT(FUTEX_CMP_REQUEUE_PI, ARG3 | ARG4 | ARG5 | ARG6, + "%u", "%u", "%#lx", "%u"); + + /* + * Unknown commands + */ + + CHECK_FUTEX(uaddr, 0xd, VAL, tmout + 1, uaddr2 + 1, VAL3, + (rc == -1) && (errno == ENOSYS)); + printf("futex(%p, 0xd /* FUTEX_??? */, %u, %p, %p, %#x) = %s\n", + uaddr, VAL_PR, tmout + 1, uaddr2 + 1, VAL3_PR, retstr(rc)); + + CHECK_FUTEX(uaddr, 0xbefeeded, VAL, tmout + 1, uaddr2, VAL3, + (rc == -1) && (errno == ENOSYS)); + printf("futex(%p, 0xbefeeded /* FUTEX_??? */, %u, %p, %p, %#x) = %s\n", + uaddr, VAL_PR, tmout + 1, uaddr2, VAL3_PR, retstr(rc)); + + puts("+++ exited with 0 +++"); + + return 0; +} + +#else + +SKIP_MAIN_UNDEFINED("__NR_futex") + +#endif diff --git a/tests/futex.test b/tests/futex.test new file mode 100755 index 0000000..8248cf3 --- /dev/null +++ b/tests/futex.test @@ -0,0 +1,8 @@ +#!/bin/sh + +# Check futex syscall decoding. + +. "${srcdir=.}/init.sh" + +run_strace_match_diff -efutex -a1 +run_strace_match_diff -efutex -v -a1 -- 1.7.10.4 ------------------------------------------------------------------------------ _______________________________________________ Strace-devel mailing list Strace-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/strace-devel