3.9.4 Use-Time Semantics

Every operation on an open descriptor is a mask check against the granted mask, with one exception: execveat(AT_EMPTY_PATH) uses a live AccessCheck.

3.9.4.1 Data operations #

OperationRequired right
ReadFILE_READ_DATA
Sequential write, no append intentFILE_WRITE_DATA
Append-intent write (O_APPEND descriptor or RWF_APPEND)FILE_APPEND_DATA or FILE_WRITE_DATA
Positioned write, or a no-append overrideFILE_WRITE_DATA — denied on append-only descriptors
Directory listingFILE_LIST_DIRECTORY
ftruncateFILE_WRITE_DATA
fallocate allocation (ALLOCATE_RANGE, with or without KEEP_SIZE)FILE_APPEND_DATA or FILE_WRITE_DATA
fallocate mutation (PUNCH_HOLE, ZERO_RANGE, COLLAPSE_RANGE, INSERT_RANGE, UNSHARE_RANGE, WRITE_ZEROES)FILE_WRITE_DATA
mmap PROT_READFILE_READ_DATA
mmap PROT_WRITE | MAP_SHAREDFILE_WRITE_DATAFILE_APPEND_DATA alone is insufficient
mmap PROT_WRITE | MAP_PRIVATEFILE_READ_DATA — copy-on-write, no write to the file
mmap PROT_EXECFILE_EXECUTE
mprotectAs mmap, for the new protection flags
flock LOCK_SH / F_RDLCKFILE_READ_DATA
flock LOCK_EX / F_WRLCKFILE_WRITE_DATA or FILE_APPEND_DATA
fsync / fdatasyncSYNCHRONIZE

A fallocate mode outside the supported set fails closed, and PUNCH_HOLE additionally requires KEEP_SIZE.

3.9.4.2 Metadata operations #

OperationRequired right
stat / lstat / path statxFILE_READ_ATTRIBUTES
fstat / descriptor statxFILE_READ_ATTRIBUTES
fstatfsFILE_READ_ATTRIBUTES
path and descriptor file_getattrFILE_READ_ATTRIBUTES
path and descriptor file_setattrFILE_WRITE_ATTRIBUTES
truncate by pathnameFILE_WRITE_DATA
chmod / fchmodat / fchmodWRITE_DAC
chown / lchown / fchownat / fchownWRITE_OWNER
utimensat / utimes / futimensFILE_WRITE_ATTRIBUTES
getxattr / lgetxattr / fgetxattrFILE_READ_EA
setxattr / lsetxattr / removexattr / fsetxattr / fremovexattrFILE_WRITE_EA
listxattr / llistxattr / flistxattrnone
access / faccessat F_OKFILE_READ_ATTRIBUTES
access / faccessat R_OKFILE_READ_DATA
access / faccessat W_OKFILE_WRITE_DATA
access / faccessat X_OKFILE_EXECUTE

Reads and writes of the canonical descriptor xattr are denied unconditionally through the xattr hooks — security.peios.sd, or system.ntfs_security on NTFS. All descriptor access goes through kacs_get_sd and kacs_set_sd. POSIX ACL xattr writes are denied unconditionally too, with EOPNOTSUPP rather than EACCES so that probe-then-tolerate callers behave sensibly.

3.9.4.3 Directory traversal #

Path resolution checks FILE_TRAVERSE on managed directory components. A token holding SeChangeNotifyPrivilege bypasses the intermediate checks, including on directories whose descriptor is missing.

Explicit changes of the current or root directory are not intermediate resolution: chdir() and chroot() take a live FILE_TRAVERSE check on the final directory, and the privilege bypass does not apply (§3.4.2). An ordinary fchdir() checks the descriptor's cached mask; an O_PATH fchdir() runs live.

3.9.4.4 Append-only enforcement #

A handle carrying FILE_APPEND_DATA but not FILE_WRITE_DATA allows only true append-intent writes. Append intent means the effective write position is forced to end-of-file by O_APPEND or per-I/O RWF_APPEND, and is not negated by RWF_NOAPPEND on the same operation. Requesting both RWF_APPEND and RWF_NOAPPEND together fails with EACCES.

Denied on such a handle: positioned writes without effective append intent — pwrite64, pwritev, pwritev2 with an explicit offset, io_uring writes with an explicit offset, and AIO writes with an offset; any write using RWF_NOAPPEND, since it can negate append semantics inherited from O_APPEND; shared writable mmap and mprotect upgrades to PROT_WRITE; and the fallocate mutation modes.

3.9.4.5 fcntl #

For F_SETFL, KACS evaluates the mutable status flags Linux accepts — O_APPEND, O_NONBLOCK/O_NDELAY, O_DIRECT, O_NOATIME. Clearing O_APPEND is denied on a handle with FILE_APPEND_DATA but not FILE_WRITE_DATA; setting it is always allowed, being a privilege reduction. Adding O_NOATIME requires FILE_WRITE_ATTRIBUTES and clearing it is always allowed. Changing only O_NONBLOCK, O_NDELAY or O_DIRECT needs no KACS right, though ordinary Linux validation still applies.

These commands are descriptor-local and require no KACS right, and none of them widens the cached mask: F_CREATED_QUERY; F_DUPFD, F_DUPFD_CLOEXEC and F_DUPFD_QUERY, which preserve the same file description and mask; F_GETFD and F_SETFD; F_GETFL; and the async-notification set F_GETOWN, F_GETOWN_EX, F_GETOWNER_UIDS, F_GETSIG, F_SETOWN, F_SETOWN_EX and F_SETSIG, where Linux pid and signal validation still applies.

These are object-state queries or mutations, checked against the cached mask before Linux-specific validation:

CommandRequired right
F_GETLK / F_GETLK64 / F_OFD_GETLKAny data right
F_GETLEASE / F_GETDELEGFILE_READ_ATTRIBUTES
F_GETPIPE_SZFILE_READ_ATTRIBUTES
F_SETPIPE_SZFILE_WRITE_ATTRIBUTES
F_GET_SEALSFILE_READ_ATTRIBUTES
F_ADD_SEALSFILE_WRITE_ATTRIBUTES
F_GET_RW_HINT / F_GET_FILE_RW_HINTFILE_READ_ATTRIBUTES
F_SET_RW_HINT / F_SET_FILE_RW_HINTFILE_WRITE_ATTRIBUTES

Lock, lease and delegation commands — F_SETLK, F_SETLKW, F_SETLK64, F_SETLKW64, F_OFD_SETLK, F_OFD_SETLKW, F_SETLEASE, F_SETDELEG — pass through the fcntl hook so that the later file-lock hook can enforce them against normalised F_RDLCK, F_WRLCK or F_UNLCK values. An unknown lock type fails closed there.

For F_NOTIFY, removing a watch — a zero event mask, ignoring DN_MULTISHOT — requires nothing. Installing one with any known DN_* event requires FILE_LIST_DIRECTORY. Unknown DN_* bits on a managed descriptor fail closed.

Unmanaged descriptors sit outside the handle check entirely, and an unknown fcntl command on a managed one fails closed.

3.9.4.6 ioctl #

Known ioctls are classified by required right; an unclassified one is allowed if the descriptor carries at least one data right. The 32-bit compat aliases take the same right as their native command, including FS_IOC32_GETFLAGS, FS_IOC32_SETFLAGS, FS_IOC32_GETVERSION, FS_IOC32_SETVERSION and the compat preallocation commands.

Descriptor-local, requiring nothing: FIOCLEX and FIONCLEX, which change close-on-exec state; FIONBIO, which changes nonblocking state; and FIOASYNC, which changes async notification state with Linux and fops validation still applying.

Common VFS:

ioctlRequired right
FIBMAPFILE_READ_DATA
FIGETBSZFILE_READ_ATTRIBUTES
FIFREEZE / FITHAWFILE_WRITE_ATTRIBUTES
FITRIMFILE_WRITE_ATTRIBUTES
FS_IOC_GETFSUUIDFILE_READ_ATTRIBUTES
FS_IOC_GETFSSYSFSPATHFILE_READ_ATTRIBUTES
FS_IOC_GETLBMD_CAPFILE_READ_ATTRIBUTES

FIFREEZE, FITHAW and FITRIM mutate filesystem operational state, and Linux's own CAP_SYS_ADMIN checks still apply on top.

File and object:

ioctlRequired right
FS_IOC_FIEMAPFILE_READ_DATA
FIONREADFILE_READ_DATA on a regular file; any data right otherwise
FS_IOC_GETFLAGSFILE_READ_ATTRIBUTES
FS_IOC_SETFLAGSFILE_WRITE_ATTRIBUTES
FS_IOC_GETVERSIONFILE_READ_ATTRIBUTES
FS_IOC_SETVERSIONFILE_WRITE_ATTRIBUTES
FS_IOC_RESVSP / FS_IOC_RESVSP64FILE_APPEND_DATA or FILE_WRITE_DATA
FS_IOC_UNRESVSP / FS_IOC_UNRESVSP64FILE_WRITE_DATA
FS_IOC_ZERO_RANGEFILE_WRITE_DATA
FICLONE / FICLONERANGEFILE_WRITE_DATA
FIDEDUPERANGEFILE_WRITE_DATA
FIOQSIZEFILE_READ_ATTRIBUTES
FS_IOC_FSGETXATTRFILE_READ_ATTRIBUTES
FS_IOC_FSSETXATTRFILE_WRITE_ATTRIBUTES
FS_IOC_GETFSLABELFILE_READ_ATTRIBUTES
FS_IOC_SETFSLABELFILE_WRITE_ATTRIBUTES
FS_IOC_GET_ENCRYPTION_PWSALTFILE_READ_ATTRIBUTES
FS_IOC_GET_ENCRYPTION_POLICYFILE_READ_ATTRIBUTES
FS_IOC_GET_ENCRYPTION_POLICY_EXFILE_READ_ATTRIBUTES
FS_IOC_SET_ENCRYPTION_POLICYFILE_WRITE_ATTRIBUTES
FS_IOC_ADD_ENCRYPTION_KEYFILE_WRITE_ATTRIBUTES
FS_IOC_REMOVE_ENCRYPTION_KEYFILE_WRITE_ATTRIBUTES
FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERSFILE_WRITE_ATTRIBUTES
FS_IOC_GET_ENCRYPTION_KEY_STATUSFILE_READ_ATTRIBUTES
BLKGETSIZE64FILE_READ_ATTRIBUTES
BLKFLSBUFFILE_WRITE_DATA

On directories, FS_IOC_GETFLAGS and FS_IOC_SETFLAGS take the same rights as on files.

Anything unclassified is allowed on any data right. For device nodes, pipes and sockets, device-specific ioctl semantics are outside FACS scope: the node's descriptor is the authorization boundary, and Linux's device-specific validation may still deny.

A pinned inode (§3.6) narrows all of this. Every content-, range- or allocation-mutating ioctl is rejected on one, and so is every ioctl the classifier does not recognise — unknown ioctls fail closed there rather than falling back to the data-right rule.

3.9.4.7 Execution #

Execution is nominally a two-layer check. The mode execute bit is the prerequisite meaning "this file is a program", set by package managers and chmod +x, applying to execve and execveat but not to mmap(PROT_EXEC). The descriptor's FILE_EXECUTE is the access control, gating both.

KACS enforces only the second. Nothing in the kernel module tests the execute mode bit; the prerequisite survives because Linux's own generic_permission() refuses MAY_EXEC on a file with no execute bit set, by way of capable_wrt_inode_uidgid(CAP_DAC_OVERRIDE). The +x requirement is therefore a Linux DAC property that KACS inherits rather than a FACS rule, and it is one of the few decisions where mode bits still matter (§3.10.2).

For descriptor-based exec — execveat with AT_EMPTY_PATH, including on O_PATH handles — a live AccessCheck for FILE_EXECUTE runs against the re-opened file rather than the cached mask being consulted.

Edit this page