mm: do not assume file == vma->vm_file in compat_vma_mmap_prepare()
In commit bb666b7c27 ("mm: add mmap_prepare() compatibility layer for
nested file systems") we introduced the ability for stacked drivers and
file systems to correctly invoke the f_op->mmap_prepare() handler from an
f_op->mmap() handler via a compatibility layer implemented in
compat_vma_mmap_prepare().
This populates vm_area_desc fields according to those found in the (not
yet fully initialised) VMA passed to f_op->mmap().
However this function implicitly assumes that the struct file which we are
operating upon is equal to vma->vm_file. This is not a safe assumption in
all cases.
The only really sane situation in which this matters would be something
like e.g. i915_gem_dmabuf_mmap() which invokes vfs_mmap() against
obj->base.filp:
ret = vfs_mmap(obj->base.filp, vma);
if (ret)
return ret;
And then sets the VMA's file to this, should the mmap operation succeed:
vma_set_file(vma, obj->base.filp);
That is - it is the file that is intended to back the VMA mapping.
This is not an issue currently, as so far we have only implemented
f_op->mmap_prepare() handlers for some file systems and internal mm uses,
and the only stacked f_op->mmap() operations that can be performed upon
these are those in backing_file_mmap() and coda_file_mmap(), both of which
use vma->vm_file.
However, moving forward, as we convert drivers to using
f_op->mmap_prepare(), this will become a problem.
Resolve this issue by explicitly setting desc->file to the provided file
parameter and update callers accordingly.
Callers are expected to read desc->file and update desc->vm_file - the
former will be the file provided by the caller (if stacked, this may
differ from vma->vm_file).
If the caller needs to differentiate between the two they therefore now
can.
While we are here, also provide a variant of compat_vma_mmap_prepare()
that operates against a pointer to any file_operations struct and does not
assume that the file_operations struct we are interested in is file->f_op.
This function is __compat_vma_mmap_prepare() and we invoke it from
compat_vma_mmap_prepare() so that we share code between the two functions.
This is important, because some drivers provide hooks in a separate
struct, for instance struct drm_device provides an fops field for this
purpose.
Also update the VMA selftests accordingly.
Link: https://lkml.kernel.org/r/dd0c72df8a33e8ffaa243eeb9b01010b670610e9.1756920635.git.lorenzo.stoakes@oracle.com
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Reviewed-by: Christian Brauner <brauner@kernel.org>
Reviewed-by: Pedro Falcato <pfalcato@suse.de>
Reviewed-by: Liam R. Howlett <Liam.Howlett@oracle.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: David Hildenbrand <david@redhat.com>
Cc: Jan Kara <jack@suse.cz>
Cc: Jann Horn <jannh@google.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
committed by
Andrew Morton
parent
af6703838e
commit
f7a741c53b
@@ -2279,6 +2279,8 @@ static inline bool can_mmap_file(struct file *file)
|
||||
return true;
|
||||
}
|
||||
|
||||
int __compat_vma_mmap_prepare(const struct file_operations *f_op,
|
||||
struct file *file, struct vm_area_struct *vma);
|
||||
int compat_vma_mmap_prepare(struct file *file, struct vm_area_struct *vma);
|
||||
|
||||
static inline int vfs_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
|
||||
62
mm/util.c
62
mm/util.c
@@ -1133,17 +1133,51 @@ void flush_dcache_folio(struct folio *folio)
|
||||
EXPORT_SYMBOL(flush_dcache_folio);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* __compat_vma_mmap_prepare() - See description for compat_vma_mmap_prepare()
|
||||
* for details. This is the same operation, only with a specific file operations
|
||||
* struct which may or may not be the same as vma->vm_file->f_op.
|
||||
* @f_op: The file operations whose .mmap_prepare() hook is specified.
|
||||
* @file: The file which backs or will back the mapping.
|
||||
* @vma: The VMA to apply the .mmap_prepare() hook to.
|
||||
* Returns: 0 on success or error.
|
||||
*/
|
||||
int __compat_vma_mmap_prepare(const struct file_operations *f_op,
|
||||
struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct vm_area_desc desc = {
|
||||
.mm = vma->vm_mm,
|
||||
.file = file,
|
||||
.start = vma->vm_start,
|
||||
.end = vma->vm_end,
|
||||
|
||||
.pgoff = vma->vm_pgoff,
|
||||
.vm_file = vma->vm_file,
|
||||
.vm_flags = vma->vm_flags,
|
||||
.page_prot = vma->vm_page_prot,
|
||||
};
|
||||
int err;
|
||||
|
||||
err = f_op->mmap_prepare(&desc);
|
||||
if (err)
|
||||
return err;
|
||||
set_vma_from_desc(vma, &desc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(__compat_vma_mmap_prepare);
|
||||
|
||||
/**
|
||||
* compat_vma_mmap_prepare() - Apply the file's .mmap_prepare() hook to an
|
||||
* existing VMA
|
||||
* @file: The file which possesss an f_op->mmap_prepare() hook
|
||||
* existing VMA.
|
||||
* @file: The file which possesss an f_op->mmap_prepare() hook.
|
||||
* @vma: The VMA to apply the .mmap_prepare() hook to.
|
||||
*
|
||||
* Ordinarily, .mmap_prepare() is invoked directly upon mmap(). However, certain
|
||||
* 'wrapper' file systems invoke a nested mmap hook of an underlying file.
|
||||
* stacked filesystems invoke a nested mmap hook of an underlying file.
|
||||
*
|
||||
* Until all filesystems are converted to use .mmap_prepare(), we must be
|
||||
* conservative and continue to invoke these 'wrapper' filesystems using the
|
||||
* conservative and continue to invoke these stacked filesystems using the
|
||||
* deprecated .mmap() hook.
|
||||
*
|
||||
* However we have a problem if the underlying file system possesses an
|
||||
@@ -1161,25 +1195,7 @@ EXPORT_SYMBOL(flush_dcache_folio);
|
||||
*/
|
||||
int compat_vma_mmap_prepare(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct vm_area_desc desc = {
|
||||
.mm = vma->vm_mm,
|
||||
.file = vma->vm_file,
|
||||
.start = vma->vm_start,
|
||||
.end = vma->vm_end,
|
||||
|
||||
.pgoff = vma->vm_pgoff,
|
||||
.vm_file = vma->vm_file,
|
||||
.vm_flags = vma->vm_flags,
|
||||
.page_prot = vma->vm_page_prot,
|
||||
};
|
||||
int err;
|
||||
|
||||
err = file->f_op->mmap_prepare(&desc);
|
||||
if (err)
|
||||
return err;
|
||||
set_vma_from_desc(vma, &desc);
|
||||
|
||||
return 0;
|
||||
return __compat_vma_mmap_prepare(file->f_op, file, vma);
|
||||
}
|
||||
EXPORT_SYMBOL(compat_vma_mmap_prepare);
|
||||
|
||||
|
||||
@@ -1466,8 +1466,8 @@ static inline void free_anon_vma_name(struct vm_area_struct *vma)
|
||||
static inline void set_vma_from_desc(struct vm_area_struct *vma,
|
||||
struct vm_area_desc *desc);
|
||||
|
||||
static inline int compat_vma_mmap_prepare(struct file *file,
|
||||
struct vm_area_struct *vma)
|
||||
static inline int __compat_vma_mmap_prepare(const struct file_operations *f_op,
|
||||
struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct vm_area_desc desc = {
|
||||
.mm = vma->vm_mm,
|
||||
@@ -1482,7 +1482,7 @@ static inline int compat_vma_mmap_prepare(struct file *file,
|
||||
};
|
||||
int err;
|
||||
|
||||
err = file->f_op->mmap_prepare(&desc);
|
||||
err = f_op->mmap_prepare(&desc);
|
||||
if (err)
|
||||
return err;
|
||||
set_vma_from_desc(vma, &desc);
|
||||
@@ -1490,6 +1490,12 @@ static inline int compat_vma_mmap_prepare(struct file *file,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int compat_vma_mmap_prepare(struct file *file,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
return __compat_vma_mmap_prepare(file->f_op, file, vma);
|
||||
}
|
||||
|
||||
/* Did the driver provide valid mmap hook configuration? */
|
||||
static inline bool can_mmap_file(struct file *file)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user