[PATCH 2/2] drm/msm: protect against faults from copy_from_user() in submit ioctl
Daniel Vetter
daniel at ffwll.ch
Sun Aug 28 16:53:40 UTC 2016
On Sun, Aug 28, 2016 at 12:43:46PM -0400, Rob Clark wrote:
> On Tue, Aug 23, 2016 at 2:03 AM, Daniel Vetter <daniel at ffwll.ch> wrote:
> > On Mon, Aug 22, 2016 at 03:38:05PM -0400, Rob Clark wrote:
> >> An evil userspace could try to cause deadlock by passing an unfaulted-in
> >> GEM bo as submit->bos (or submit->cmds) table. Which will trigger
> >> msm_gem_fault() while we already hold struct_mutex. See:
> >>
> >> https://github.com/freedreno/msmtest/blob/master/evilsubmittest.c
> >>
> >> Cc: stable at vger.kernel.org
> >> Signed-off-by: Rob Clark <robdclark at gmail.com>
> >> ---
> >> drivers/gpu/drm/msm/msm_drv.h | 6 ++++++
> >> drivers/gpu/drm/msm/msm_gem.c | 9 +++++++++
> >> drivers/gpu/drm/msm/msm_gem_submit.c | 3 +++
> >> 3 files changed, 18 insertions(+)
> >>
> >> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> >> index a35c1b6..957801e 100644
> >> --- a/drivers/gpu/drm/msm/msm_drv.h
> >> +++ b/drivers/gpu/drm/msm/msm_drv.h
> >> @@ -157,6 +157,12 @@ struct msm_drm_private {
> >> struct shrinker shrinker;
> >>
> >> struct msm_vblank_ctrl vblank_ctrl;
> >> +
> >> + /* task holding struct_mutex.. currently only used in submit path
> >> + * to detect and reject faults from copy_from_user() for submit
> >> + * ioctl.
> >> + */
> >> + struct task_struct *struct_mutex_task;
> >> };
> >>
> >> struct msm_format {
> >> diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
> >> index 8dfdeec..f6b8945 100644
> >> --- a/drivers/gpu/drm/msm/msm_gem.c
> >> +++ b/drivers/gpu/drm/msm/msm_gem.c
> >> @@ -196,11 +196,20 @@ int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
> >> {
> >> struct drm_gem_object *obj = vma->vm_private_data;
> >> struct drm_device *dev = obj->dev;
> >> + struct msm_drm_private *priv = dev->dev_private;
> >> struct page **pages;
> >> unsigned long pfn;
> >> pgoff_t pgoff;
> >> int ret;
> >>
> >> + /* This should only happen if userspace tries to pass a mmap'd
> >> + * but unfaulted gem bo vaddr into submit ioctl, triggering
> >> + * a page fault while struct_mutex is already held. This is
> >> + * not a valid use-case so just bail.
> >> + */
> >> + if (priv->struct_mutex_task == current)
> >
> > READ_ONCE here I think. Otherwise should at least work, though I still
> > think it's sloppy ;-)
>
> hmm, is READ_ONCE() actually needed? In the "== current" case I don't
> see how there could be a race, and the "!= current" case I don't
> really care..
>
> and yeah, maybe not sloppy but I wouldn't call it pretty. I couldn't
> come up with any low overhead way to check if submit->cmds /
> submit->objs wasn't a GEM object (otherwise I would have bailed out
> earlier than copy_from_user() with an -EINVAL, and wouldn't have
> needed this)
Like I said imo the correct fix isn't trying to detect your own objects,
but by adding a slowpath into the CS ioctl where you drop locks and copy
relocations (or whatever it is you need) into a vmalloced (or similar)
staging area. Not fast, but it'll work. And the only signal you need for
that is the short read/write from copy_*_user_atomic.
Without this you'll still hit the lockdep snag as soon as arm's copy*user
functions gain all the necessary annotations (like x86 already has). And
that seems to happen rsn if Al Viro's plans hold up.
-Daniel
>
> BR,
> -R
>
> > -Daniel
> >
> >> + return VM_FAULT_SIGBUS;
> >> +
> >> /* Make sure we don't parallel update on a fault, nor move or remove
> >> * something from beneath our feet
> >> */
> >> diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
> >> index 03d4ce2..0be57a9 100644
> >> --- a/drivers/gpu/drm/msm/msm_gem_submit.c
> >> +++ b/drivers/gpu/drm/msm/msm_gem_submit.c
> >> @@ -426,6 +426,8 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
> >> if (ret)
> >> return ret;
> >>
> >> + priv->struct_mutex_task = current;
> >> +
> >> if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
> >> out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
> >> if (out_fence_fd < 0) {
> >> @@ -549,6 +551,7 @@ out:
> >> out_unlock:
> >> if (ret && (out_fence_fd >= 0))
> >> put_unused_fd(out_fence_fd);
> >> + priv->struct_mutex_task = NULL;
> >> mutex_unlock(&dev->struct_mutex);
> >> return ret;
> >> }
> >> --
> >> 2.7.4
> >>
> >
> > --
> > Daniel Vetter
> > Software Engineer, Intel Corporation
> > http://blog.ffwll.ch
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
More information about the dri-devel
mailing list