mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-06-21 15:43:21 +02:00
8b82cacad9
This flag indicates the path should be opened if it's a regular file. This is useful to write secure programs that want to avoid being tricked into opening device nodes with special semantics while thinking they operate on regular files. This is a requested feature from the uapi-group[1]. The previously introduced EFTYPE error code is returned when the path doesn't refer to a regular file. For example, if openat2 is called on path /dev/null with OPENAT2_REGULAR in the flag param, it will return -EFTYPE. When used in combination with O_CREAT, either the regular file is created, or if the path already exists, it is opened if it's a regular file. Otherwise, -EFTYPE is returned. When OPENAT2_REGULAR is combined with O_DIRECTORY, -EINVAL is returned as it doesn't make sense to open a path that is both a directory and a regular file. The UAPI bit lives in the upper 32 bits of open_how::flags (((__u64)1 << 32)) so that open(2) and openat(2) -- whose @flags argument is a C int -- cannot physically express it. This is a structural guarantee, not a runtime mask: the bit is unrepresentable in 32 bits. Because the rest of the VFS open path narrows to 32 bits in several places (op->open_flag, f->f_flags, the unsigned open_flag argument of i_op->atomic_open()), build_open_flags() translates OPENAT2_REGULAR into a kernel-internal lower-32-bit carrier __O_REGULAR (bit 4, unused as an O_* on every architecture) before the assignment to op->open_flag. __O_REGULAR then rides through the existing channels exactly like __FMODE_EXEC. do_dentry_open() strips it so it cannot leak back to userspace via fcntl(F_GETFL). Four BUILD_BUG_ON_MSG() invariants in build_open_flags() prevent any future bit collision or accidental low-32 redefinition: - VALID_OPEN_FLAGS fits in 32 bits. - OPENAT2_REGULAR lives in the upper 32 bits. - OPENAT2_REGULAR does not alias any open()/openat() flag. - __O_REGULAR does not alias any user-visible flag. [1]: https://uapi-group.org/kernel-features/#ability-to-only-open-regular-files Christian Brauner <brauner@kernel.org> says: Move OPENAT2_REGULAR to the upper 32 bits of open_how::flags with a kernel-internal __O_REGULAR carrier so that open(2)/openat(2) cannot encode the flag; add BUILD_BUG_ON_MSG() invariants and register __O_REGULAR in the fcntl_init() allocation-uniqueness BUILD_BUG_ON() (bit count 21 -> 22). Signed-off-by: Dorjoy Chowdhury <dorjoychy111@gmail.com> Link: https://patch.msgid.link/20260328172314.45807-2-dorjoychy111@gmail.com Reviewed-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: Aleksa Sarai <aleksa@amutable.com> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
67 lines
2.4 KiB
C
67 lines
2.4 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _LINUX_FCNTL_H
|
|
#define _LINUX_FCNTL_H
|
|
|
|
#include <linux/stat.h>
|
|
#include <uapi/linux/fcntl.h>
|
|
#include <uapi/linux/openat2.h>
|
|
|
|
/* List of all valid flags for the open/openat flags argument: */
|
|
#define VALID_OPEN_FLAGS \
|
|
(O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \
|
|
O_APPEND | O_NDELAY | O_NONBLOCK | __O_SYNC | O_DSYNC | \
|
|
FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \
|
|
O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE | O_EMPTYPATH)
|
|
|
|
/* List of all valid flags for openat2(2)'s how->flags argument. */
|
|
#define VALID_OPENAT2_FLAGS (VALID_OPEN_FLAGS | OPENAT2_REGULAR)
|
|
|
|
/*
|
|
* Kernel-internal carrier for OPENAT2_REGULAR. The UAPI bit lives in the
|
|
* upper 32 bits of open_how::flags so open()/openat() cannot encode it.
|
|
* build_open_flags() translates it to this internal flag, which then
|
|
* propagates through op->open_flag and f->f_flags exactly like __FMODE_EXEC.
|
|
* do_dentry_open() strips it so userspace cannot observe it via
|
|
* fcntl(F_GETFL).
|
|
*
|
|
* Bit 30 is not claimed by any O_* flag on any architecture and stays clear
|
|
* of the sign bit of the int op->open_flag. fcntl_init() enforces that it
|
|
* never aliases an open-flag bit.
|
|
*/
|
|
#define __O_REGULAR (1 << 30)
|
|
|
|
/* List of all valid flags for the how->resolve argument: */
|
|
#define VALID_RESOLVE_FLAGS \
|
|
(RESOLVE_NO_XDEV | RESOLVE_NO_MAGICLINKS | RESOLVE_NO_SYMLINKS | \
|
|
RESOLVE_BENEATH | RESOLVE_IN_ROOT | RESOLVE_CACHED)
|
|
|
|
/* List of all open_how "versions". */
|
|
#define OPEN_HOW_SIZE_VER0 24 /* sizeof first published struct */
|
|
#define OPEN_HOW_SIZE_LATEST OPEN_HOW_SIZE_VER0
|
|
|
|
#ifndef force_o_largefile
|
|
#define force_o_largefile() (!IS_ENABLED(CONFIG_ARCH_32BIT_OFF_T))
|
|
#endif
|
|
|
|
#if BITS_PER_LONG == 32
|
|
#define IS_GETLK32(cmd) ((cmd) == F_GETLK)
|
|
#define IS_SETLK32(cmd) ((cmd) == F_SETLK)
|
|
#define IS_SETLKW32(cmd) ((cmd) == F_SETLKW)
|
|
#define IS_GETLK64(cmd) ((cmd) == F_GETLK64)
|
|
#define IS_SETLK64(cmd) ((cmd) == F_SETLK64)
|
|
#define IS_SETLKW64(cmd) ((cmd) == F_SETLKW64)
|
|
#else
|
|
#define IS_GETLK32(cmd) (0)
|
|
#define IS_SETLK32(cmd) (0)
|
|
#define IS_SETLKW32(cmd) (0)
|
|
#define IS_GETLK64(cmd) ((cmd) == F_GETLK)
|
|
#define IS_SETLK64(cmd) ((cmd) == F_SETLK)
|
|
#define IS_SETLKW64(cmd) ((cmd) == F_SETLKW)
|
|
#endif /* BITS_PER_LONG == 32 */
|
|
|
|
#define IS_GETLK(cmd) (IS_GETLK32(cmd) || IS_GETLK64(cmd))
|
|
#define IS_SETLK(cmd) (IS_SETLK32(cmd) || IS_SETLK64(cmd))
|
|
#define IS_SETLKW(cmd) (IS_SETLKW32(cmd) || IS_SETLKW64(cmd))
|
|
|
|
#endif
|