Skip to content

Commit 6647e76

Browse files
committed
v4l2: don't fall back to follow_pfn() if pin_user_pages_fast() fails
The V4L2_MEMORY_USERPTR interface is long deprecated and shouldn't be used (and is discouraged for any modern v4l drivers). And Seth Jenkins points out that the fallback to VM_PFNMAP/VM_IO is fundamentally racy and dangerous. Note that it's not even a case that should trigger, since any normal user pointer logic ends up just using the pin_user_pages_fast() call that does the proper page reference counting. That's not the problem case, only if you try to use special device mappings do you have any issues. Normally I'd just remove this during the merge window, but since Seth pointed out the problem cases, we really want to know as soon as possible if there are actually any users of this odd special case of a legacy interface. Neither Hans nor Mauro seem to think that such mis-uses of the old legacy interface should exist. As Mauro says: "See, V4L2 has actually 4 streaming APIs: - Kernel-allocated mmap (usually referred simply as just mmap); - USERPTR mmap; - read(); - dmabuf; The USERPTR is one of the oldest way to use it, coming from V4L version 1 times, and by far the least used one" And Hans chimed in on the USERPTR interface: "To be honest, I wouldn't mind if it goes away completely, but that's a bit of a pipe dream right now" but while removing this legacy interface entirely may be a pipe dream we can at least try to remove the unlikely (and actively broken) case of using special device mappings for USERPTR accesses. This replaces it with a WARN_ONCE() that we can remove once we've hopefully confirmed that no actual users exist. NOTE! Longer term, this means that a 'struct frame_vector' only ever contains proper page pointers, and all the games we have with converting them to pages can go away (grep for 'frame_vector_to_pages()' and the uses of 'vec->is_pfns'). But this is just the first step, to verify that this code really is all dead, and do so as quickly as possible. Reported-by: Seth Jenkins <[email protected]> Acked-by: Hans Verkuil <[email protected]> Acked-by: Mauro Carvalho Chehab <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: Jan Kara <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent a4412fd commit 6647e76

File tree

1 file changed

+12
-56
lines changed

1 file changed

+12
-56
lines changed

drivers/media/common/videobuf2/frame_vector.c

Lines changed: 12 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,7 @@
3535
int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
3636
struct frame_vector *vec)
3737
{
38-
struct mm_struct *mm = current->mm;
39-
struct vm_area_struct *vma;
40-
int ret_pin_user_pages_fast = 0;
41-
int ret = 0;
42-
int err;
38+
int ret;
4339

4440
if (nr_frames == 0)
4541
return 0;
@@ -52,57 +48,17 @@ int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
5248
ret = pin_user_pages_fast(start, nr_frames,
5349
FOLL_FORCE | FOLL_WRITE | FOLL_LONGTERM,
5450
(struct page **)(vec->ptrs));
55-
if (ret > 0) {
56-
vec->got_ref = true;
57-
vec->is_pfns = false;
58-
goto out_unlocked;
59-
}
60-
ret_pin_user_pages_fast = ret;
61-
62-
mmap_read_lock(mm);
63-
vec->got_ref = false;
64-
vec->is_pfns = true;
65-
ret = 0;
66-
do {
67-
unsigned long *nums = frame_vector_pfns(vec);
68-
69-
vma = vma_lookup(mm, start);
70-
if (!vma)
71-
break;
72-
73-
while (ret < nr_frames && start + PAGE_SIZE <= vma->vm_end) {
74-
err = follow_pfn(vma, start, &nums[ret]);
75-
if (err) {
76-
if (ret)
77-
goto out;
78-
// If follow_pfn() returns -EINVAL, then this
79-
// is not an IO mapping or a raw PFN mapping.
80-
// In that case, return the original error from
81-
// pin_user_pages_fast(). Otherwise this
82-
// function would return -EINVAL when
83-
// pin_user_pages_fast() returned -ENOMEM,
84-
// which makes debugging hard.
85-
if (err == -EINVAL && ret_pin_user_pages_fast)
86-
ret = ret_pin_user_pages_fast;
87-
else
88-
ret = err;
89-
goto out;
90-
}
91-
start += PAGE_SIZE;
92-
ret++;
93-
}
94-
/* Bail out if VMA doesn't completely cover the tail page. */
95-
if (start < vma->vm_end)
96-
break;
97-
} while (ret < nr_frames);
98-
out:
99-
mmap_read_unlock(mm);
100-
out_unlocked:
101-
if (!ret)
102-
ret = -EFAULT;
103-
if (ret > 0)
104-
vec->nr_frames = ret;
105-
return ret;
51+
vec->got_ref = true;
52+
vec->is_pfns = false;
53+
vec->nr_frames = ret;
54+
55+
if (likely(ret > 0))
56+
return ret;
57+
58+
/* This used to (racily) return non-refcounted pfns. Let people know */
59+
WARN_ONCE(1, "get_vaddr_frames() cannot follow VM_IO mapping");
60+
vec->nr_frames = 0;
61+
return ret ? ret : -EFAULT;
10662
}
10763
EXPORT_SYMBOL(get_vaddr_frames);
10864

0 commit comments

Comments
 (0)