From c3dfa7121a1aa80eec3f9f662bd91ecc93310c9b Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 30 Aug 2004 20:34:10 -0700 Subject: [PATCH] [PATCH] hugetlbfs private mappings Hugetlbfs silently coerce private mappings of hugetlb files into shared ones. So private writable mapping has MAP_SHARED semantics. I think, such mappings should be disallowed. First, such behavior allows open hugetlbfs file O_RDONLY, and overwrite it via mmap(PROT_READ|PROT_WRITE, MAP_PRIVATE), so it is security bug. Second, private writable mmap() should fail just because kernel does not support this. I belisve, it is ok to allow private readonly hugetlb mappings, sys_mprotect() does not work with hugetlb vmas. There is another problem. Hugetlb mapping is always prefaulted, pages allocated at mmap() time. So even readonly mapping allows to enlarge the size of the hugetlbfs file, and steal huge pages without appropriative permissions. Signed-off-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/hugetlbfs/inode.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index d5aa417d89560..857d7bed77d24 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -52,6 +52,9 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) loff_t len, vma_len; int ret; + if ((vma->vm_flags & (VM_MAYSHARE | VM_WRITE)) == VM_WRITE) + return -EINVAL; + if (vma->vm_pgoff & (HPAGE_SIZE / PAGE_SIZE - 1)) return -EINVAL; @@ -70,10 +73,19 @@ static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) file_accessed(file); vma->vm_flags |= VM_HUGETLB | VM_RESERVED; vma->vm_ops = &hugetlb_vm_ops; + + ret = -ENOMEM; + len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); + if (!(vma->vm_flags & VM_WRITE) && len > inode->i_size) + goto out; + ret = hugetlb_prefault(mapping, vma); - len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); - if (ret == 0 && inode->i_size < len) + if (ret) + goto out; + + if (inode->i_size < len) inode->i_size = len; +out: up(&inode->i_sem); return ret; -- 2.53.0