/* DF-0185 — Latent uninitialized-stack leak via vacl_get_acl.
 *
 * sys/kern/kern_acl.c: vacl_get_acl() declares
 *     struct acl inkernelacl;          // NOT zeroed (line 92)
 * and after VOP_GETACL writes acl_cnt entries, copyout() copies the
 * ENTIRE struct acl (line 99) -- including entries [acl_cnt..ACL_MAX_ENTRIES]
 * which remain uninitialized kernel stack.
 *
 * LATENT: no in-tree filesystem overrides VOP_GETACL.  vfs_default.c:89
 * sets .vop_getacl = vop_eopnotsupp, which returns EOPNOTSUPP and the
 * `if (error == 0) copyout(...)` guard at kern_acl.c:98 prevents the
 * leak from firing.  The bug becomes live the moment an ACL-capable
 * filesystem is added.
 *
 * This PoC simply demonstrates the latent state: acl_get_file returns
 * EOPNOTSUPP on every filesystem on this kernel (no FS implements
 * VOP_GETACL).  The fix is to zero `inkernelacl` before the VOP call.
 */

#include <sys/types.h>
#include <sys/acl.h>
#include <sys/syscall.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

int
main(void)
{
    struct acl a;
    int rc, e;

    memset(&a, 0, sizeof a);

    /* sys___acl_get_file(path, type, aclp) -- the direct syscall,
       which is what acl_get_file() in libacl wraps. */
    rc = syscall(SYS___acl_get_file, "/etc/passwd", ACL_TYPE_ACCESS, &a);
    e = errno;
    printf("DF-0185: __acl_get_file(/etc/passwd, ACL_TYPE_ACCESS) = %d errno=%d (%s)\n",
           rc, e, strerror(e));

    rc = syscall(SYS___acl_get_file, "/tmp", ACL_TYPE_DEFAULT, &a);
    e = errno;
    printf("DF-0185: __acl_get_file(/tmp, ACL_TYPE_DEFAULT) = %d errno=%d (%s)\n",
           rc, e, strerror(e));

    if (e == EOPNOTSUPP) {
        printf("DF-0185: VOP_GETACL returned EOPNOTSUPP (vfs_default.c:89) --\n"
               "         copyout at kern_acl.c:99 NOT reached; leak is LATENT.\n"
               "         Fix is still warranted (zero `inkernelacl` before the call).\n");
    } else if (rc == 0) {
        printf("DF-0185: WARNING -- VOP_GETACL succeeded!  Leak path is LIVE.\n");
        printf("         struct acl returned: acl_cnt=%d\n", a.acl_cnt);
    }
    return 0;
}
