A better way to do F_SEAL_FUTURE_WRITE seal was discussed [1] last week
where we don't need to modify core VFS structures to get the same
behavior of the seal. This solves several side-effects pointed out by
Andy [2].

[1] https://lore.kernel.org/lkml/20181111173650.ga256...@google.com/
[2] 
https://lore.kernel.org/lkml/69ce06cc-e47c-4992-848a-66eb23ee6...@amacapital.net/

Suggested-by: Andy Lutomirski <l...@kernel.org>
Fixes: 5e653c2923fd ("mm: Add an F_SEAL_FUTURE_WRITE seal to memfd")
Signed-off-by: Joel Fernandes (Google) <j...@joelfernandes.org>
---
 fs/hugetlbfs/inode.c |  2 +-
 mm/memfd.c           | 19 -------------------
 mm/shmem.c           | 24 +++++++++++++++++++++---
 3 files changed, 22 insertions(+), 23 deletions(-)

diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 762028994f47..5b54bf893a67 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -558,7 +558,7 @@ static long hugetlbfs_punch_hole(struct inode *inode, 
loff_t offset, loff_t len)
                inode_lock(inode);
 
                /* protected by i_mutex */
-               if (info->seals & F_SEAL_WRITE) {
+               if (info->seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE)) {
                        inode_unlock(inode);
                        return -EPERM;
                }
diff --git a/mm/memfd.c b/mm/memfd.c
index 63fff5e77114..650e65a46b9c 100644
--- a/mm/memfd.c
+++ b/mm/memfd.c
@@ -201,25 +201,6 @@ 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;
 
diff --git a/mm/shmem.c b/mm/shmem.c
index 32eb29bd72c6..cee9878c87f1 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2121,6 +2121,23 @@ int shmem_lock(struct file *file, int lock, struct 
user_struct *user)
 
 static int shmem_mmap(struct file *file, struct vm_area_struct *vma)
 {
+       struct shmem_inode_info *info = SHMEM_I(file_inode(file));
+
+       /*
+        * New PROT_READ and MAP_SHARED mmaps are not allowed when "future
+        * write" seal active.
+        */
+       if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_WRITE) &&
+           (info->seals & F_SEAL_FUTURE_WRITE))
+               return -EPERM;
+
+       /*
+        * Since the F_SEAL_FUTURE_WRITE seals allow for a MAP_SHARED read-only
+        * mapping, take care to not allow mprotect to revert protections.
+        */
+       if (info->seals & F_SEAL_FUTURE_WRITE)
+               vma->vm_flags &= ~(VM_MAYWRITE);
+
        file_accessed(file);
        vma->vm_ops = &shmem_vm_ops;
        if (IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE) &&
@@ -2346,8 +2363,9 @@ shmem_write_begin(struct file *file, struct address_space 
*mapping,
        pgoff_t index = pos >> PAGE_SHIFT;
 
        /* i_mutex is held by caller */
-       if (unlikely(info->seals & (F_SEAL_WRITE | F_SEAL_GROW))) {
-               if (info->seals & F_SEAL_WRITE)
+       if (unlikely(info->seals & (F_SEAL_GROW |
+                                  F_SEAL_WRITE | F_SEAL_FUTURE_WRITE))) {
+               if (info->seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE))
                        return -EPERM;
                if ((info->seals & F_SEAL_GROW) && pos + len > inode->i_size)
                        return -EPERM;
@@ -2610,7 +2628,7 @@ static long shmem_fallocate(struct file *file, int mode, 
loff_t offset,
                DECLARE_WAIT_QUEUE_HEAD_ONSTACK(shmem_falloc_waitq);
 
                /* protected by i_mutex */
-               if (info->seals & F_SEAL_WRITE) {
+               if (info->seals & (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE)) {
                        error = -EPERM;
                        goto out;
                }
-- 
2.19.1.1215.g8438c0b245-goog

Reply via email to