root/tests/lib/libc/sys/t_ptrace_bytetransfer_wait.h
/*      $NetBSD: t_ptrace_bytetransfer_wait.h,v 1.2 2025/05/02 02:24:32 riastradh Exp $ */

/*-
 * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc.
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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.
 */

enum bytes_transfer_type {
        BYTES_TRANSFER_DATA,
        BYTES_TRANSFER_DATAIO,
        BYTES_TRANSFER_TEXT,
        BYTES_TRANSFER_TEXTIO,
        BYTES_TRANSFER_AUXV
};

static int __used
bytes_transfer_dummy(int a, int b, int c, int d)
{
        int e, f, g, h;

        a *= 4;
        b += 3;
        c -= 2;
        d /= 1;

        e = strtol("10", NULL, 10);
        f = strtol("20", NULL, 10);
        g = strtol("30", NULL, 10);
        h = strtol("40", NULL, 10);

        return (a + b * c - d) + (e * f - g / h);
}

static void
bytes_transfer(int operation, size_t size, enum bytes_transfer_type type)
{
        const int exitval = 5;
        const int sigval = SIGSTOP;
        pid_t child, wpid;
        bool skip = false;

        int lookup_me = 0;
        uint8_t lookup_me8 = 0;
        uint16_t lookup_me16 = 0;
        uint32_t lookup_me32 = 0;
        uint64_t lookup_me64 = 0;

        int magic = 0x13579246;
        uint8_t magic8 = 0xab;
        uint16_t magic16 = 0x1234;
        uint32_t magic32 = 0x98765432;
        uint64_t magic64 = 0xabcdef0123456789;

        struct ptrace_io_desc io;
#if defined(TWAIT_HAVE_STATUS)
        int status;
#endif
        /* 513 is just enough, for the purposes of ATF it's good enough */
        AuxInfo ai[513], *aip;

        ATF_REQUIRE(size < sizeof(ai));

        /* Prepare variables for .TEXT transfers */
        switch (type) {
        case BYTES_TRANSFER_TEXT:
                memcpy(&magic, bytes_transfer_dummy, sizeof(magic));
                break;
        case BYTES_TRANSFER_TEXTIO:
                switch (size) {
                case 8:
                        memcpy(&magic8, bytes_transfer_dummy, sizeof(magic8));
                        break;
                case 16:
                        memcpy(&magic16, bytes_transfer_dummy, sizeof(magic16));
                        break;
                case 32:
                        memcpy(&magic32, bytes_transfer_dummy, sizeof(magic32));
                        break;
                case 64:
                        memcpy(&magic64, bytes_transfer_dummy, sizeof(magic64));
                        break;
                }
                break;
        default:
                break;
        }

        /* Prepare variables for PIOD and AUXV transfers */
        switch (type) {
        case BYTES_TRANSFER_TEXTIO:
        case BYTES_TRANSFER_DATAIO:
                io.piod_op = operation;
                switch (size) {
                case 8:
                        io.piod_offs = (type == BYTES_TRANSFER_TEXTIO) ?
                                       (void *)bytes_transfer_dummy :
                                       &lookup_me8;
                        io.piod_addr = &lookup_me8;
                        io.piod_len = sizeof(lookup_me8);
                        break;
                case 16:
                        io.piod_offs = (type == BYTES_TRANSFER_TEXTIO) ?
                                       (void *)bytes_transfer_dummy :
                                       &lookup_me16;
                        io.piod_addr = &lookup_me16;
                        io.piod_len = sizeof(lookup_me16);
                        break;
                case 32:
                        io.piod_offs = (type == BYTES_TRANSFER_TEXTIO) ?
                                       (void *)bytes_transfer_dummy :
                                       &lookup_me32;
                        io.piod_addr = &lookup_me32;
                        io.piod_len = sizeof(lookup_me32);
                        break;
                case 64:
                        io.piod_offs = (type == BYTES_TRANSFER_TEXTIO) ?
                                       (void *)bytes_transfer_dummy :
                                       &lookup_me64;
                        io.piod_addr = &lookup_me64;
                        io.piod_len = sizeof(lookup_me64);
                        break;
                default:
                        break;
                }
                break;
        case BYTES_TRANSFER_AUXV:
                io.piod_op = operation;
                io.piod_offs = 0;
                io.piod_addr = ai;
                io.piod_len = size;
                break;
        default:
                break;
        }

        DPRINTF("Before forking process PID=%d\n", getpid());
        SYSCALL_REQUIRE((child = fork()) != -1);
        if (child == 0) {
                DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
                FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);

                switch (type) {
                case BYTES_TRANSFER_DATA:
                        switch (operation) {
                        case PT_READ_D:
                        case PT_READ_I:
                                lookup_me = magic;
                                break;
                        default:
                                break;
                        }
                        break;
                case BYTES_TRANSFER_DATAIO:
                        switch (operation) {
                        case PIOD_READ_D:
                        case PIOD_READ_I:
                                switch (size) {
                                case 8:
                                        lookup_me8 = magic8;
                                        break;
                                case 16:
                                        lookup_me16 = magic16;
                                        break;
                                case 32:
                                        lookup_me32 = magic32;
                                        break;
                                case 64:
                                        lookup_me64 = magic64;
                                        break;
                                default:
                                        break;
                                }
                                break;
                        default:
                                break;
                        }
                default:
                        break;
                }

                DPRINTF("Before raising %s from child\n", strsignal(sigval));
                FORKEE_ASSERT(raise(sigval) == 0);

                /* Handle PIOD and PT separately as operation values overlap */
                switch (type) {
                case BYTES_TRANSFER_DATA:
                        switch (operation) {
                        case PT_WRITE_D:
                        case PT_WRITE_I:
                                FORKEE_ASSERT_EQ(lookup_me, magic);
                                break;
                        default:
                                break;
                        }
                        break;
                case BYTES_TRANSFER_DATAIO:
                        switch (operation) {
                        case PIOD_WRITE_D:
                        case PIOD_WRITE_I:
                                switch (size) {
                                case 8:
                                        FORKEE_ASSERT_EQ(lookup_me8, magic8);
                                        break;
                                case 16:
                                        FORKEE_ASSERT_EQ(lookup_me16, magic16);
                                        break;
                                case 32:
                                        FORKEE_ASSERT_EQ(lookup_me32, magic32);
                                        break;
                                case 64:
                                        FORKEE_ASSERT_EQ(lookup_me64, magic64);
                                        break;
                                default:
                                        break;
                                }
                                break;
                        default:
                                break;
                        }
                        break;
                case BYTES_TRANSFER_TEXT:
                        FORKEE_ASSERT(memcmp(&magic, bytes_transfer_dummy,
                                             sizeof(magic)) == 0);
                        break;
                case BYTES_TRANSFER_TEXTIO:
                        switch (size) {
                        case 8:
                                FORKEE_ASSERT(memcmp(&magic8,
                                                     bytes_transfer_dummy,
                                                     sizeof(magic8)) == 0);
                                break;
                        case 16:
                                FORKEE_ASSERT(memcmp(&magic16,
                                                     bytes_transfer_dummy,
                                                     sizeof(magic16)) == 0);
                                break;
                        case 32:
                                FORKEE_ASSERT(memcmp(&magic32,
                                                     bytes_transfer_dummy,
                                                     sizeof(magic32)) == 0);
                                break;
                        case 64:
                                FORKEE_ASSERT(memcmp(&magic64,
                                                     bytes_transfer_dummy,
                                                     sizeof(magic64)) == 0);
                                break;
                        }
                        break;
                default:
                        break;
                }

                DPRINTF("Before exiting of the child process\n");
                _exit(exitval);
        }
        DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child);

        DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
        TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);

        validate_status_stopped(status, sigval);

        /* Check PaX MPROTECT */
        if (!can_we_write_to_text(child)) {
                switch (type) {
                case BYTES_TRANSFER_TEXTIO:
                        switch (operation) {
                        case PIOD_WRITE_D:
                        case PIOD_WRITE_I:
                                skip = true;
                                break;
                        default:
                                break;
                        }
                        break;
                case BYTES_TRANSFER_TEXT:
                        switch (operation) {
                        case PT_WRITE_D:
                        case PT_WRITE_I:
                                skip = true;
                                break;
                        default:
                                break;
                        }
                        break;
                default:
                        break;
                }
        }

        /* Bailout cleanly killing the child process */
        if (skip) {
                SYSCALL_REQUIRE(ptrace(PT_KILL, child, (void *)1, 0) != -1);
                DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
                TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0),
                                      child);

                validate_status_signaled(status, SIGKILL, 0);

                atf_tc_skip("PaX MPROTECT setup prevents writes to .text");
        }

        DPRINTF("Calling operation to transfer bytes between child=%d and "
               "parent=%d\n", child, getpid());

        switch (type) {
        case BYTES_TRANSFER_TEXTIO:
        case BYTES_TRANSFER_DATAIO:
        case BYTES_TRANSFER_AUXV:
                switch (operation) {
                case PIOD_WRITE_D:
                case PIOD_WRITE_I:
                        switch (size) {
                        case 8:
                                lookup_me8 = magic8;
                                break;
                        case 16:
                                lookup_me16 = magic16;
                                break;
                        case 32:
                                lookup_me32 = magic32;
                                break;
                        case 64:
                                lookup_me64 = magic64;
                                break;
                        default:
                                break;
                        }
                        break;
                default:
                        break;
                }
                SYSCALL_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
                switch (operation) {
                case PIOD_READ_D:
                case PIOD_READ_I:
                        switch (size) {
                        case 8:
                                ATF_REQUIRE_EQ(lookup_me8, magic8);
                                break;
                        case 16:
                                ATF_REQUIRE_EQ(lookup_me16, magic16);
                                break;
                        case 32:
                                ATF_REQUIRE_EQ(lookup_me32, magic32);
                                break;
                        case 64:
                                ATF_REQUIRE_EQ(lookup_me64, magic64);
                                break;
                        default:
                                break;
                        }
                        break;
                case PIOD_READ_AUXV:
                        DPRINTF("Asserting that AUXV length (%zu) is > 0\n",
                                io.piod_len);
                        ATF_REQUIRE(io.piod_len > 0);
                        for (aip = ai; aip->a_type != AT_NULL; aip++)
                                DPRINTF("a_type=%#llx a_v=%#llx\n",
                                    (long long int)aip->a_type,
                                    (long long int)aip->a_v);
                        break;
                default:
                        break;
                }
                break;
        case BYTES_TRANSFER_TEXT:
                switch (operation) {
                case PT_READ_D:
                case PT_READ_I:
                        errno = 0;
                        lookup_me = ptrace(operation, child,
                                           bytes_transfer_dummy, 0);
                        ATF_REQUIRE_EQ(lookup_me, magic);
                        SYSCALL_REQUIRE_ERRNO(errno, 0);
                        break;
                case PT_WRITE_D:
                case PT_WRITE_I:
                        SYSCALL_REQUIRE(ptrace(operation, child,
                                               bytes_transfer_dummy, magic)
                                        != -1);
                        break;
                default:
                        break;
                }
                break;
        case BYTES_TRANSFER_DATA:
                switch (operation) {
                case PT_READ_D:
                case PT_READ_I:
                        errno = 0;
                        lookup_me = ptrace(operation, child, &lookup_me, 0);
                        ATF_REQUIRE_EQ(lookup_me, magic);
                        SYSCALL_REQUIRE_ERRNO(errno, 0);
                        break;
                case PT_WRITE_D:
                case PT_WRITE_I:
                        lookup_me = magic;
                        SYSCALL_REQUIRE(ptrace(operation, child, &lookup_me,
                                               magic) != -1);
                        break;
                default:
                        break;
                }
                break;
        default:
                break;
        }

        DPRINTF("Before resuming the child process where it left off and "
            "without signal to be sent\n");
        SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);

        DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
        TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);

        validate_status_exited(status, exitval);

        DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
        TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
}

#define BYTES_TRANSFER(test, operation, size, type)                     \
ATF_TC(test);                                                           \
ATF_TC_HEAD(test, tc)                                                   \
{                                                                       \
        atf_tc_set_md_var(tc, "descr",                                  \
            "Verify bytes transfer operation" #operation " and size " #size \
            " of type " #type);                                         \
}                                                                       \
                                                                        \
ATF_TC_BODY(test, tc)                                                   \
{                                                                       \
                                                                        \
        bytes_transfer(operation, size, BYTES_TRANSFER_##type);         \
}

// DATA

BYTES_TRANSFER(bytes_transfer_piod_read_d_8, PIOD_READ_D, 8, DATAIO)
BYTES_TRANSFER(bytes_transfer_piod_read_d_16, PIOD_READ_D, 16, DATAIO)
BYTES_TRANSFER(bytes_transfer_piod_read_d_32, PIOD_READ_D, 32, DATAIO)
BYTES_TRANSFER(bytes_transfer_piod_read_d_64, PIOD_READ_D, 64, DATAIO)

BYTES_TRANSFER(bytes_transfer_piod_read_i_8, PIOD_READ_I, 8, DATAIO)
BYTES_TRANSFER(bytes_transfer_piod_read_i_16, PIOD_READ_I, 16, DATAIO)
BYTES_TRANSFER(bytes_transfer_piod_read_i_32, PIOD_READ_I, 32, DATAIO)
BYTES_TRANSFER(bytes_transfer_piod_read_i_64, PIOD_READ_I, 64, DATAIO)

BYTES_TRANSFER(bytes_transfer_piod_write_d_8, PIOD_WRITE_D, 8, DATAIO)
BYTES_TRANSFER(bytes_transfer_piod_write_d_16, PIOD_WRITE_D, 16, DATAIO)
BYTES_TRANSFER(bytes_transfer_piod_write_d_32, PIOD_WRITE_D, 32, DATAIO)
BYTES_TRANSFER(bytes_transfer_piod_write_d_64, PIOD_WRITE_D, 64, DATAIO)

BYTES_TRANSFER(bytes_transfer_piod_write_i_8, PIOD_WRITE_I, 8, DATAIO)
BYTES_TRANSFER(bytes_transfer_piod_write_i_16, PIOD_WRITE_I, 16, DATAIO)
BYTES_TRANSFER(bytes_transfer_piod_write_i_32, PIOD_WRITE_I, 32, DATAIO)
BYTES_TRANSFER(bytes_transfer_piod_write_i_64, PIOD_WRITE_I, 64, DATAIO)

BYTES_TRANSFER(bytes_transfer_read_d, PT_READ_D, 32, DATA)
BYTES_TRANSFER(bytes_transfer_read_i, PT_READ_I, 32, DATA)
BYTES_TRANSFER(bytes_transfer_write_d, PT_WRITE_D, 32, DATA)
BYTES_TRANSFER(bytes_transfer_write_i, PT_WRITE_I, 32, DATA)

// TEXT

BYTES_TRANSFER(bytes_transfer_piod_read_d_8_text, PIOD_READ_D, 8, TEXTIO)
BYTES_TRANSFER(bytes_transfer_piod_read_d_16_text, PIOD_READ_D, 16, TEXTIO)
BYTES_TRANSFER(bytes_transfer_piod_read_d_32_text, PIOD_READ_D, 32, TEXTIO)
BYTES_TRANSFER(bytes_transfer_piod_read_d_64_text, PIOD_READ_D, 64, TEXTIO)

BYTES_TRANSFER(bytes_transfer_piod_read_i_8_text, PIOD_READ_I, 8, TEXTIO)
BYTES_TRANSFER(bytes_transfer_piod_read_i_16_text, PIOD_READ_I, 16, TEXTIO)
BYTES_TRANSFER(bytes_transfer_piod_read_i_32_text, PIOD_READ_I, 32, TEXTIO)
BYTES_TRANSFER(bytes_transfer_piod_read_i_64_text, PIOD_READ_I, 64, TEXTIO)

BYTES_TRANSFER(bytes_transfer_piod_write_d_8_text, PIOD_WRITE_D, 8, TEXTIO)
BYTES_TRANSFER(bytes_transfer_piod_write_d_16_text, PIOD_WRITE_D, 16, TEXTIO)
BYTES_TRANSFER(bytes_transfer_piod_write_d_32_text, PIOD_WRITE_D, 32, TEXTIO)
BYTES_TRANSFER(bytes_transfer_piod_write_d_64_text, PIOD_WRITE_D, 64, TEXTIO)

BYTES_TRANSFER(bytes_transfer_piod_write_i_8_text, PIOD_WRITE_I, 8, TEXTIO)
BYTES_TRANSFER(bytes_transfer_piod_write_i_16_text, PIOD_WRITE_I, 16, TEXTIO)
BYTES_TRANSFER(bytes_transfer_piod_write_i_32_text, PIOD_WRITE_I, 32, TEXTIO)
BYTES_TRANSFER(bytes_transfer_piod_write_i_64_text, PIOD_WRITE_I, 64, TEXTIO)

BYTES_TRANSFER(bytes_transfer_read_d_text, PT_READ_D, 32, TEXT)
BYTES_TRANSFER(bytes_transfer_read_i_text, PT_READ_I, 32, TEXT)
BYTES_TRANSFER(bytes_transfer_write_d_text, PT_WRITE_D, 32, TEXT)
BYTES_TRANSFER(bytes_transfer_write_i_text, PT_WRITE_I, 32, TEXT)

// AUXV

BYTES_TRANSFER(bytes_transfer_piod_read_auxv, PIOD_READ_AUXV, 4096, AUXV)

/// ----------------------------------------------------------------------------

static void
bytes_transfer_alignment(const char *operation)
{
        const int exitval = 5;
        const int sigval = SIGSTOP;
        pid_t child, wpid;
#if defined(TWAIT_HAVE_STATUS)
        int status;
#endif
        char *buffer;
        int vector;
        size_t len;
        size_t i;
        int op;

        struct ptrace_io_desc io;
        struct ptrace_siginfo info;

        memset(&io, 0, sizeof(io));
        memset(&info, 0, sizeof(info));

        /* Testing misaligned byte transfer crossing page boundaries */
        len = sysconf(_SC_PAGESIZE) * 2;
        buffer = malloc(len);
        ATF_REQUIRE(buffer != NULL);

        /* Initialize the buffer with random data */
        for (i = 0; i < len; i++)
                buffer[i] = i & 0xff;

        DPRINTF("Before forking process PID=%d\n", getpid());
        SYSCALL_REQUIRE((child = fork()) != -1);
        if (child == 0) {
                DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
                FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);

                DPRINTF("Before raising %s from child\n", strsignal(sigval));
                FORKEE_ASSERT(raise(sigval) == 0);

                DPRINTF("Before exiting of the child process\n");
                _exit(exitval);
        }
        DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child);

        DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
        TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);

        validate_status_stopped(status, sigval);

        DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
        SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info))
                != -1);

        DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid);
        DPRINTF("Signal properties: si_signo=%#x si_code=%#x "
                "si_errno=%#x\n",
                info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
                info.psi_siginfo.si_errno);

        ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval);
        ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP);

        if (strcmp(operation, "PT_READ_I") == 0 ||
            strcmp(operation, "PT_READ_D") == 0) {
                if (strcmp(operation, "PT_READ_I"))
                        op = PT_READ_I;
                else
                        op = PT_READ_D;

                for (i = 0; i <= (len - sizeof(int)); i++) {
                        errno = 0;
                        vector = ptrace(op, child, buffer + i, 0);
                        ATF_REQUIRE_EQ(errno, 0);
                        ATF_REQUIRE(!memcmp(&vector, buffer + i, sizeof(int)));
                }
        } else if (strcmp(operation, "PT_WRITE_I") == 0 ||
                   strcmp(operation, "PT_WRITE_D") == 0) {
                if (strcmp(operation, "PT_WRITE_I"))
                        op = PT_WRITE_I;
                else
                        op = PT_WRITE_D;

                for (i = 0; i <= (len - sizeof(int)); i++) {
                        memcpy(&vector, buffer + i, sizeof(int));
                        SYSCALL_REQUIRE(ptrace(op, child, buffer + 1, vector)
                            != -1);
                }
        } else if (strcmp(operation, "PIOD_READ_I") == 0 ||
                   strcmp(operation, "PIOD_READ_D") == 0) {
                if (strcmp(operation, "PIOD_READ_I"))
                        op = PIOD_READ_I;
                else
                        op = PIOD_READ_D;

                io.piod_op = op;
                io.piod_addr = &vector;
                io.piod_len = sizeof(int);

                for (i = 0; i <= (len - sizeof(int)); i++) {
                        io.piod_offs = buffer + i;

                        SYSCALL_REQUIRE(ptrace(PT_IO, child, &io, sizeof(io))
                                        != -1);
                        ATF_REQUIRE(!memcmp(&vector, buffer + i, sizeof(int)));
                }
        } else if (strcmp(operation, "PIOD_WRITE_I") == 0 ||
                   strcmp(operation, "PIOD_WRITE_D") == 0) {
                if (strcmp(operation, "PIOD_WRITE_I"))
                        op = PIOD_WRITE_I;
                else
                        op = PIOD_WRITE_D;

                io.piod_op = op;
                io.piod_addr = &vector;
                io.piod_len = sizeof(int);

                for (i = 0; i <= (len - sizeof(int)); i++) {
                        io.piod_offs = buffer + i;

                        SYSCALL_REQUIRE(ptrace(PT_IO, child, &io, sizeof(io))
                                        != -1);
                }
        } else if (strcmp(operation, "PIOD_READ_AUXV") == 0) {
                io.piod_op = PIOD_READ_AUXV;
                io.piod_addr = &vector;
                io.piod_len = sizeof(int);

                errno = 0;
                i = 0;
                /* Read the whole AUXV vector, it has no clear length */
                while (io.piod_len > 0) {
                        io.piod_offs = (void *)(intptr_t)i;
                        SYSCALL_REQUIRE(ptrace(PT_IO, child, &io, sizeof(io))
                                        != -1 || (io.piod_len == 0 && i > 0));
                        ++i;
                }
        }

        DPRINTF("Before resuming the child process where it left off "
            "and without signal to be sent\n");
        SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);

        DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
        TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0),
            child);

        DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
        TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
}

#define BYTES_TRANSFER_ALIGNMENT(test, operation)                       \
ATF_TC(test);                                                           \
ATF_TC_HEAD(test, tc)                                                   \
{                                                                       \
        atf_tc_set_md_var(tc, "descr",                                  \
            "Verify bytes transfer for potentially misaligned "         \
            "operation " operation);                                    \
}                                                                       \
                                                                        \
ATF_TC_BODY(test, tc)                                                   \
{                                                                       \
                                                                        \
        bytes_transfer_alignment(operation);                            \
}

BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_pt_read_i, "PT_READ_I")
BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_pt_read_d, "PT_READ_D")
BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_pt_write_i, "PT_WRITE_I")
BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_pt_write_d, "PT_WRITE_D")

BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_piod_read_i, "PIOD_READ_I")
BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_piod_read_d, "PIOD_READ_D")
BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_piod_write_i, "PIOD_WRITE_I")
BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_piod_write_d, "PIOD_WRITE_D")

BYTES_TRANSFER_ALIGNMENT(bytes_transfer_alignment_piod_read_auxv, "PIOD_READ_AUXV")

/// ----------------------------------------------------------------------------

static void
bytes_transfer_eof(const char *operation)
{
        const int exitval = 5;
        const int sigval = SIGSTOP;
        pid_t child, wpid;
#if defined(TWAIT_HAVE_STATUS)
        int status;
#endif
        FILE *fp;
        char *p;
        int vector;
        int op;

        struct ptrace_io_desc io;
        struct ptrace_siginfo info;

        memset(&io, 0, sizeof(io));
        memset(&info, 0, sizeof(info));

        vector = 0;

        fp = tmpfile();
        ATF_REQUIRE(fp != NULL);

        p = mmap(0, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(fp), 0);
        ATF_REQUIRE(p != MAP_FAILED);

        DPRINTF("Before forking process PID=%d\n", getpid());
        SYSCALL_REQUIRE((child = fork()) != -1);
        if (child == 0) {
                DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
                FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);

                DPRINTF("Before raising %s from child\n", strsignal(sigval));
                FORKEE_ASSERT(raise(sigval) == 0);

                DPRINTF("Before exiting of the child process\n");
                _exit(exitval);
        }
        DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child);

        DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
        TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);

        validate_status_stopped(status, sigval);

        DPRINTF("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
        SYSCALL_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info))
                != -1);

        DPRINTF("Signal traced to lwpid=%d\n", info.psi_lwpid);
        DPRINTF("Signal properties: si_signo=%#x si_code=%#x "
                "si_errno=%#x\n",
                info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
                info.psi_siginfo.si_errno);

        ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval);
        ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP);

        if (strcmp(operation, "PT_READ_I") == 0 ||
            strcmp(operation, "PT_READ_D") == 0) {
                if (strcmp(operation, "PT_READ_I"))
                        op = PT_READ_I;
                else
                        op = PT_READ_D;

                errno = 0;
                SYSCALL_REQUIRE(ptrace(op, child, p, 0) == -1);
                ATF_REQUIRE_EQ(errno, EINVAL);
        } else if (strcmp(operation, "PT_WRITE_I") == 0 ||
                   strcmp(operation, "PT_WRITE_D") == 0) {
                if (strcmp(operation, "PT_WRITE_I"))
                        op = PT_WRITE_I;
                else
                        op = PT_WRITE_D;

                errno = 0;
                SYSCALL_REQUIRE(ptrace(op, child, p, vector) == -1);
                ATF_REQUIRE_EQ(errno, EINVAL);
        } else if (strcmp(operation, "PIOD_READ_I") == 0 ||
                   strcmp(operation, "PIOD_READ_D") == 0) {
                if (strcmp(operation, "PIOD_READ_I"))
                        op = PIOD_READ_I;
                else
                        op = PIOD_READ_D;

                io.piod_op = op;
                io.piod_addr = &vector;
                io.piod_len = sizeof(int);
                io.piod_offs = p;

                errno = 0;
                SYSCALL_REQUIRE(ptrace(PT_IO, child, &io, sizeof(io)) == -1);
                ATF_REQUIRE_EQ(errno, EINVAL);
        } else if (strcmp(operation, "PIOD_WRITE_I") == 0 ||
                   strcmp(operation, "PIOD_WRITE_D") == 0) {
                if (strcmp(operation, "PIOD_WRITE_I"))
                        op = PIOD_WRITE_I;
                else
                        op = PIOD_WRITE_D;

                io.piod_op = op;
                io.piod_addr = &vector;
                io.piod_len = sizeof(int);
                io.piod_offs = p;

                errno = 0;
                SYSCALL_REQUIRE(ptrace(PT_IO, child, &io, sizeof(io)) == -1);
                ATF_REQUIRE_EQ(errno, EINVAL);
        }

        DPRINTF("Before resuming the child process where it left off "
            "and without signal to be sent\n");
        SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);

        DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
        TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0),
            child);

        DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
        TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
}

#define BYTES_TRANSFER_EOF(test, operation)                             \
ATF_TC(test);                                                           \
ATF_TC_HEAD(test, tc)                                                   \
{                                                                       \
        atf_tc_set_md_var(tc, "descr",                                  \
            "Verify bytes EOF byte transfer for the " operation         \
            " operation");                                              \
}                                                                       \
                                                                        \
ATF_TC_BODY(test, tc)                                                   \
{                                                                       \
                                                                        \
        bytes_transfer_eof(operation);                                  \
}

BYTES_TRANSFER_EOF(bytes_transfer_eof_pt_read_i, "PT_READ_I")
BYTES_TRANSFER_EOF(bytes_transfer_eof_pt_read_d, "PT_READ_D")
BYTES_TRANSFER_EOF(bytes_transfer_eof_pt_write_i, "PT_WRITE_I")
BYTES_TRANSFER_EOF(bytes_transfer_eof_pt_write_d, "PT_WRITE_D")

BYTES_TRANSFER_EOF(bytes_transfer_eof_piod_read_i, "PIOD_READ_I")
BYTES_TRANSFER_EOF(bytes_transfer_eof_piod_read_d, "PIOD_READ_D")
BYTES_TRANSFER_EOF(bytes_transfer_eof_piod_write_i, "PIOD_WRITE_I")
BYTES_TRANSFER_EOF(bytes_transfer_eof_piod_write_d, "PIOD_WRITE_D")

#define ATF_TP_ADD_TCS_PTRACE_WAIT_BYTETRANSFER() \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_8); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_16); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_32); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_64); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_8); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_16); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_32); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_64); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_8); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_16); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_32); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_64); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_8); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_16); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_32); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_64); \
        ATF_TP_ADD_TC(tp, bytes_transfer_read_d); \
        ATF_TP_ADD_TC(tp, bytes_transfer_read_i); \
        ATF_TP_ADD_TC(tp, bytes_transfer_write_d); \
        ATF_TP_ADD_TC(tp, bytes_transfer_write_i); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_8_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_16_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_32_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_d_64_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_8_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_16_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_32_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_i_64_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_8_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_16_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_32_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_d_64_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_8_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_16_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_32_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_write_i_64_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_read_d_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_read_i_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_write_d_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_write_i_text); \
        ATF_TP_ADD_TC(tp, bytes_transfer_piod_read_auxv); \
        ATF_TP_ADD_TC(tp, bytes_transfer_alignment_pt_read_i); \
        ATF_TP_ADD_TC(tp, bytes_transfer_alignment_pt_read_d); \
        ATF_TP_ADD_TC(tp, bytes_transfer_alignment_pt_write_i); \
        ATF_TP_ADD_TC(tp, bytes_transfer_alignment_pt_write_d); \
        ATF_TP_ADD_TC(tp, bytes_transfer_alignment_piod_read_i); \
        ATF_TP_ADD_TC(tp, bytes_transfer_alignment_piod_read_d); \
        ATF_TP_ADD_TC(tp, bytes_transfer_alignment_piod_write_i); \
        ATF_TP_ADD_TC(tp, bytes_transfer_alignment_piod_write_d); \
        ATF_TP_ADD_TC(tp, bytes_transfer_alignment_piod_read_auxv); \
        ATF_TP_ADD_TC(tp, bytes_transfer_eof_pt_read_i); \
        ATF_TP_ADD_TC(tp, bytes_transfer_eof_pt_read_d); \
        ATF_TP_ADD_TC(tp, bytes_transfer_eof_pt_write_i); \
        ATF_TP_ADD_TC(tp, bytes_transfer_eof_pt_write_d); \
        ATF_TP_ADD_TC(tp, bytes_transfer_eof_piod_read_i); \
        ATF_TP_ADD_TC(tp, bytes_transfer_eof_piod_read_d); \
        ATF_TP_ADD_TC(tp, bytes_transfer_eof_piod_write_i); \
        ATF_TP_ADD_TC(tp, bytes_transfer_eof_piod_write_d);