| /* |
| * Copyright (c) 2016 Eugene Syromiatnikov <evgsyr@gmail.com> |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #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"); |
| } |
| |
| # 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, |
| sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(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, |
| sprintrc(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, |
| sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(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, |
| sprintrc(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, |
| sprintrc(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, |
| sprintrc(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, sprintrc(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, sprintrc(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, |
| sprintrc(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, sprintrc(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, |
| sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(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, |
| sprintrc(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, |
| sprintrc(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, sprintrc(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, |
| sprintrc(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, sprintrc(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, |
| sprintrc(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, sprintrc(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, sprintrc(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, |
| sprintrc(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, |
| sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(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, sprintrc(rc)); |
| |
| puts("+++ exited with 0 +++"); |
| |
| return 0; |
| } |
| |
| #else |
| |
| SKIP_MAIN_UNDEFINED("__NR_futex") |
| |
| #endif |