diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h index 6448cdd9a350..a2f8658f1c55 100644 --- a/include/uapi/linux/fcntl.h +++ b/include/uapi/linux/fcntl.h @@ -41,6 +41,7 @@ #define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ #define F_SEAL_GROW 0x0004 /* prevent file from growing */ #define F_SEAL_WRITE 0x0008 /* prevent writes */ +#define F_SEAL_FUTURE_WRITE 0x0010 /* prevent future writes while mapped */ /* (1U << 31) is reserved for signed error codes */ /* diff --git a/mm/memfd.c b/mm/memfd.c index 2bb5e257080e..5ba9804e9515 100644 --- a/mm/memfd.c +++ b/mm/memfd.c @@ -150,7 +150,8 @@ static unsigned int *memfd_file_seals_ptr(struct file *file) #define F_ALL_SEALS (F_SEAL_SEAL | \ F_SEAL_SHRINK | \ F_SEAL_GROW | \ - F_SEAL_WRITE) + F_SEAL_WRITE | \ + F_SEAL_FUTURE_WRITE) static int memfd_add_seals(struct file *file, unsigned int seals) { @@ -219,6 +220,25 @@ static int memfd_add_seals(struct file *file, unsigned int seals) } } + if ((seals & F_SEAL_FUTURE_WRITE) && + !(*file_seals & F_SEAL_FUTURE_WRITE)) { + /* + * The FUTURE_WRITE seal also prevents growing and shrinking + * so we need them to be already set, or requested now. + */ + int test_seals = (seals | *file_seals) & + (F_SEAL_GROW | F_SEAL_SHRINK); + + if (test_seals != (F_SEAL_GROW | F_SEAL_SHRINK)) { + error = -EINVAL; + goto unlock; + } + + spin_lock(&file->f_lock); + file->f_mode &= ~(FMODE_WRITE | FMODE_PWRITE); + spin_unlock(&file->f_lock); + } + *file_seals |= seals; error = 0;