Skip to content

Commit f7a741c

Browse files
ljskernelakpm00
authored andcommitted
mm: do not assume file == vma->vm_file in compat_vma_mmap_prepare()
In commit bb666b7 ("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>
1 parent af67038 commit f7a741c

3 files changed

Lines changed: 50 additions & 26 deletions

File tree

include/linux/fs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2279,6 +2279,8 @@ static inline bool can_mmap_file(struct file *file)
22792279
return true;
22802280
}
22812281

2282+
int __compat_vma_mmap_prepare(const struct file_operations *f_op,
2283+
struct file *file, struct vm_area_struct *vma);
22822284
int compat_vma_mmap_prepare(struct file *file, struct vm_area_struct *vma);
22832285

22842286
static inline int vfs_mmap(struct file *file, struct vm_area_struct *vma)

mm/util.c

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,17 +1133,51 @@ void flush_dcache_folio(struct folio *folio)
11331133
EXPORT_SYMBOL(flush_dcache_folio);
11341134
#endif
11351135

1136+
/**
1137+
* __compat_vma_mmap_prepare() - See description for compat_vma_mmap_prepare()
1138+
* for details. This is the same operation, only with a specific file operations
1139+
* struct which may or may not be the same as vma->vm_file->f_op.
1140+
* @f_op: The file operations whose .mmap_prepare() hook is specified.
1141+
* @file: The file which backs or will back the mapping.
1142+
* @vma: The VMA to apply the .mmap_prepare() hook to.
1143+
* Returns: 0 on success or error.
1144+
*/
1145+
int __compat_vma_mmap_prepare(const struct file_operations *f_op,
1146+
struct file *file, struct vm_area_struct *vma)
1147+
{
1148+
struct vm_area_desc desc = {
1149+
.mm = vma->vm_mm,
1150+
.file = file,
1151+
.start = vma->vm_start,
1152+
.end = vma->vm_end,
1153+
1154+
.pgoff = vma->vm_pgoff,
1155+
.vm_file = vma->vm_file,
1156+
.vm_flags = vma->vm_flags,
1157+
.page_prot = vma->vm_page_prot,
1158+
};
1159+
int err;
1160+
1161+
err = f_op->mmap_prepare(&desc);
1162+
if (err)
1163+
return err;
1164+
set_vma_from_desc(vma, &desc);
1165+
1166+
return 0;
1167+
}
1168+
EXPORT_SYMBOL(__compat_vma_mmap_prepare);
1169+
11361170
/**
11371171
* compat_vma_mmap_prepare() - Apply the file's .mmap_prepare() hook to an
1138-
* existing VMA
1139-
* @file: The file which possesss an f_op->mmap_prepare() hook
1172+
* existing VMA.
1173+
* @file: The file which possesss an f_op->mmap_prepare() hook.
11401174
* @vma: The VMA to apply the .mmap_prepare() hook to.
11411175
*
11421176
* Ordinarily, .mmap_prepare() is invoked directly upon mmap(). However, certain
1143-
* 'wrapper' file systems invoke a nested mmap hook of an underlying file.
1177+
* stacked filesystems invoke a nested mmap hook of an underlying file.
11441178
*
11451179
* Until all filesystems are converted to use .mmap_prepare(), we must be
1146-
* conservative and continue to invoke these 'wrapper' filesystems using the
1180+
* conservative and continue to invoke these stacked filesystems using the
11471181
* deprecated .mmap() hook.
11481182
*
11491183
* However we have a problem if the underlying file system possesses an
@@ -1161,25 +1195,7 @@ EXPORT_SYMBOL(flush_dcache_folio);
11611195
*/
11621196
int compat_vma_mmap_prepare(struct file *file, struct vm_area_struct *vma)
11631197
{
1164-
struct vm_area_desc desc = {
1165-
.mm = vma->vm_mm,
1166-
.file = vma->vm_file,
1167-
.start = vma->vm_start,
1168-
.end = vma->vm_end,
1169-
1170-
.pgoff = vma->vm_pgoff,
1171-
.vm_file = vma->vm_file,
1172-
.vm_flags = vma->vm_flags,
1173-
.page_prot = vma->vm_page_prot,
1174-
};
1175-
int err;
1176-
1177-
err = file->f_op->mmap_prepare(&desc);
1178-
if (err)
1179-
return err;
1180-
set_vma_from_desc(vma, &desc);
1181-
1182-
return 0;
1198+
return __compat_vma_mmap_prepare(file->f_op, file, vma);
11831199
}
11841200
EXPORT_SYMBOL(compat_vma_mmap_prepare);
11851201

tools/testing/vma/vma_internal.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,8 +1466,8 @@ static inline void free_anon_vma_name(struct vm_area_struct *vma)
14661466
static inline void set_vma_from_desc(struct vm_area_struct *vma,
14671467
struct vm_area_desc *desc);
14681468

1469-
static inline int compat_vma_mmap_prepare(struct file *file,
1470-
struct vm_area_struct *vma)
1469+
static inline int __compat_vma_mmap_prepare(const struct file_operations *f_op,
1470+
struct file *file, struct vm_area_struct *vma)
14711471
{
14721472
struct vm_area_desc desc = {
14731473
.mm = vma->vm_mm,
@@ -1482,14 +1482,20 @@ static inline int compat_vma_mmap_prepare(struct file *file,
14821482
};
14831483
int err;
14841484

1485-
err = file->f_op->mmap_prepare(&desc);
1485+
err = f_op->mmap_prepare(&desc);
14861486
if (err)
14871487
return err;
14881488
set_vma_from_desc(vma, &desc);
14891489

14901490
return 0;
14911491
}
14921492

1493+
static inline int compat_vma_mmap_prepare(struct file *file,
1494+
struct vm_area_struct *vma)
1495+
{
1496+
return __compat_vma_mmap_prepare(file->f_op, file, vma);
1497+
}
1498+
14931499
/* Did the driver provide valid mmap hook configuration? */
14941500
static inline bool can_mmap_file(struct file *file)
14951501
{

0 commit comments

Comments
 (0)