DragonFlyBSD Kernel Audit
DF-0835 / trigger.c
← back to finding ↓ download raw
/*
 * DF-0835 trigger — smbfs_advlock unconditional lock-type overwrite.
 *
 * Bug: sys/vfs/smbfs/smbfs_vnops.c:943 does
 *         lkop = SMB_LOCK_EXCL;
 *     AFTER the inner switch (lines 927-939) has correctly mapped
 *         F_WRLCK -> SMB_LOCK_EXCL, F_RDLCK -> SMB_LOCK_SHARED,
 *         F_UNLCK -> SMB_LOCK_RELEASE.
 *     The unconditional overwrite makes every F_SETLK call sent to the
 *     SMB server an EXCLUSIVE lock (SMB_LOCKING_ANDX_SHARED_LOCK never set,
 *     unlock-count=0 / lock-count=1 even for F_UNLCK).
 *
 * This trigger issues fcntl(F_SETLK, F_RDLCK) on a file that lives on an
 * smbfs mount.  With the bug, the SMB client sends SMB_COM_LOCKING_ANDX
 * WITHOUT the SMB_LOCKING_ANDX_SHARED_LOCK flag (i.e. an exclusive lock),
 * so a second reader is blocked — violating POSIX shared-lock semantics.
 *
 * FULL runtime proof requires:
 *   - an SMB server exporting a share (the audit guest has none), and
 *   - a root-mounted smbfs filesystem: mount_smbfs //user@server/share /mnt
 *   - then:  ./trigger /mnt/somefile
 *
 * Without a live SMB share the trigger still validates the syscall surface
 * (it will get ENOENT/EINVAL on a non-smbfs path, confirming the fcntl path
 * is exercised).  The PRIMARY evidence for this finding is the deterministic
 * source trace in VERDICT.md (the overwrite at smbfs_vnops.c:943 is
 * unambiguous dead-code-making-the-switch-moot).
 *
 * Build:  cc -o trigger trigger.c
 * Run:    ./trigger <file-on-smbfs-mount>
 */

#include <sys/fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    const char *path;
    int fd, rc;
    struct flock fl;

    if (argc != 2) {
        fprintf(stderr, "usage: %s <file-on-smbfs>\n", argv[0]);
        return 2;
    }
    path = argv[1];

    fd = open(path, O_RDWR);
    if (fd < 0) {
        fprintf(stderr, "open(%s) failed: %s\n", path, strerror(errno));
        return 2;
    }

    /* Request a SHARED (read) byte-range lock.  With the bug the SMB client
     * forwards this to the server as an EXCLUSIVE lock. */
    memset(&fl, 0, sizeof(fl));
    fl.l_type = F_RDLCK;
    fl.l_whence = SEEK_SET;
    fl.l_start = 0;
    fl.l_len = 1;               /* lock 1 byte */

    rc = fcntl(fd, F_SETLK, &fl);
    if (rc < 0) {
        fprintf(stderr, "fcntl(F_SETLK, F_RDLCK) failed: %s\n", strerror(errno));
        close(fd);
        return 1;
    }
    printf("F_RDLCK issued on %s (rc=%d)\n", path, rc);
    printf("On an UNPATCHED kernel the server received SMB_LOCK_EXCL\n");
    printf("  => a second reader would be blocked (shared-lock semantics broken)\n");
    printf("On a PATCHED kernel the server received SMB_LOCK_SHARED\n");

    /* cleanup: unlock */
    fl.l_type = F_UNLCK;
    fcntl(fd, F_SETLK, &fl);
    close(fd);
    return 0;
}