Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mm: do not assume file == vma->vm_file in compat_vma_mmap_prepare()

In commit bb666b7c2707 ("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>

authored by

Lorenzo Stoakes and committed by
Andrew Morton
f7a741c5 af670383

+50 -26
+2
include/linux/fs.h
··· 2279 2279 return true; 2280 2280 } 2281 2281 2282 + int __compat_vma_mmap_prepare(const struct file_operations *f_op, 2283 + struct file *file, struct vm_area_struct *vma); 2282 2284 int compat_vma_mmap_prepare(struct file *file, struct vm_area_struct *vma); 2283 2285 2284 2286 static inline int vfs_mmap(struct file *file, struct vm_area_struct *vma)
+39 -23
mm/util.c
··· 1134 1134 #endif 1135 1135 1136 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 + 1170 + /** 1137 1171 * 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. 1140 1174 * @vma: The VMA to apply the .mmap_prepare() hook to. 1141 1175 * 1142 1176 * 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. 1144 1178 * 1145 1179 * 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 1147 1181 * deprecated .mmap() hook. 1148 1182 * 1149 1183 * However we have a problem if the underlying file system possesses an ··· 1195 1161 */ 1196 1162 int compat_vma_mmap_prepare(struct file *file, struct vm_area_struct *vma) 1197 1163 { 1198 - struct vm_area_desc desc = { 1199 - .mm = vma->vm_mm, 1200 - .file = vma->vm_file, 1201 - .start = vma->vm_start, 1202 - .end = vma->vm_end, 1203 - 1204 - .pgoff = vma->vm_pgoff, 1205 - .vm_file = vma->vm_file, 1206 - .vm_flags = vma->vm_flags, 1207 - .page_prot = vma->vm_page_prot, 1208 - }; 1209 - int err; 1210 - 1211 - err = file->f_op->mmap_prepare(&desc); 1212 - if (err) 1213 - return err; 1214 - set_vma_from_desc(vma, &desc); 1215 - 1216 - return 0; 1164 + return __compat_vma_mmap_prepare(file->f_op, file, vma); 1217 1165 } 1218 1166 EXPORT_SYMBOL(compat_vma_mmap_prepare); 1219 1167
+9 -3
tools/testing/vma/vma_internal.h
··· 1466 1466 static inline void set_vma_from_desc(struct vm_area_struct *vma, 1467 1467 struct vm_area_desc *desc); 1468 1468 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) 1471 1471 { 1472 1472 struct vm_area_desc desc = { 1473 1473 .mm = vma->vm_mm, ··· 1482 1482 }; 1483 1483 int err; 1484 1484 1485 - err = file->f_op->mmap_prepare(&desc); 1485 + err = f_op->mmap_prepare(&desc); 1486 1486 if (err) 1487 1487 return err; 1488 1488 set_vma_from_desc(vma, &desc); 1489 1489 1490 1490 return 0; 1491 + } 1492 + 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); 1491 1497 } 1492 1498 1493 1499 /* Did the driver provide valid mmap hook configuration? */