โฌข DragonFlyBSD Kernel Audit
DF-4989 / tws_getlock.c
โ† back to finding โ†“ download raw
/*
 * DF-4989 PoC โ€” TWS_IOCTL_GET_LOCK result is dead-stored.
 *
 * sys/dev/raid/tws/tws_user.c:280-294:
 *   memcpy(&lpkt, ubuf->data_buf, sizeof(struct tws_lock_packet));   [read]
 *   ... lpkt.time_remaining_msec computed in BOTH branches ...
 *   ... and never copied back to ubuf->data_buf โ€” the computed value and
 *       the granted/denied state are lost.  The SUCCESS return (335) means
 *       the ioctl "succeeded" while returning nothing.
 *
 * Consequence: a userland management tool can never observe the remaining
 * lock time or whether the lock was even granted, so the advisory ioctl
 * serialization between management agents is unenforceable client-side.
 *
 * Requires /dev/tws0; without the controller exits ENOENT.
 */
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

struct tws_driver_packet {
    unsigned int control_code, status, unique_id, sequence_id,
                 os_status, buffer_length;
} __attribute__((packed));

struct tws_lock_packet {
    unsigned int timeout_msec;
    unsigned int time_remaining_msec;
    unsigned int force_flag;
} __attribute__((packed));

struct tws_command_header {
    unsigned char sense_data[18];
    struct { unsigned short srcnum; unsigned char reserved;
             unsigned char status; unsigned short error;
             unsigned char res__srcid; unsigned char res__severity; } __attribute__((packed)) status_block;
    unsigned char err_specific_desc[98];
    struct { unsigned char size_header; unsigned short request_id;
             unsigned char size_sense; } __attribute__((packed)) header_desc;
} __attribute__((packed));

struct tws_command_packet {
    struct tws_command_header hdr;
    unsigned char cmd[896];
} __attribute__((packed));

struct tws_ioctl_with_payload {
    struct tws_driver_packet driver_pkt;
    char           padding[488];
    struct tws_command_packet cmd_pkt;
    union {
        struct tws_lock_packet lock_pkt;
        char                   data_buf[136];
    } payload;
} __attribute__((packed));

#define TWS_IOCTL_GET_LOCK   _IOWR('T', 207, struct tws_ioctl_with_payload)
#define TWS_IOCTL_RELEASE_LOCK _IOWR('T', 208, struct tws_ioctl_with_payload)
#define TWS_IOCTL_LOCK_ALREADY_HELD 0x1002

int
main(void)
{
    static struct tws_ioctl_with_payload buf, buf2;
    int fd, error;

    fd = open("/dev/tws0", O_RDWR);
    if (fd < 0) {
        printf("open(/dev/tws0): %s โ€” no 3ware controller (hardware-gated)\n",
            strerror(errno));
        return (2);
    }

    /* ask for the lock for 10 minutes */
    buf.payload.lock_pkt.timeout_msec = 10u * 60u * 1000u;
    buf.payload.lock_pkt.force_flag = 0;
    error = ioctl(fd, TWS_IOCTL_GET_LOCK, &buf);
    printf("DF4989: GET_LOCK rc=%d errno=%s driver_pkt.status=0x%x "
           "time_remaining_msec=0x%x (unchanged 0x0 => result never "
           "copied back)\n",
           error, strerror(errno), buf.driver_pkt.status,
           buf.payload.lock_pkt.time_remaining_msec);

    /* second request while held: driver takes the ALREADY_HELD branch and
     * computes the remaining time ... which is again never returned */
    buf2.payload.lock_pkt.timeout_msec = 10u * 60u * 1000u;
    error = ioctl(fd, TWS_IOCTL_GET_LOCK, &buf2);
    printf("DF4989: GET_LOCK#2 rc=%d errno=%s driver_pkt.status=0x%x "
           "(expect 0x%x) time_remaining_msec=0x%x (still 0 => dead store)\n",
           error, strerror(errno), buf2.driver_pkt.status,
           TWS_IOCTL_LOCK_ALREADY_HELD, buf2.payload.lock_pkt.time_remaining_msec);

    ioctl(fd, TWS_IOCTL_RELEASE_LOCK, &buf);
    close(fd);
    return (0);
}