[Mesa-dev] [PATCH] egl/dri2: dri2_make_current: Make sure display is initialized before using it
Nicolas Boichat
drinkcat at chromium.org
Mon Jul 18 03:19:03 UTC 2016
On Fri, Jul 15, 2016 at 9:03 PM, Emil Velikov <emil.l.velikov at gmail.com> wrote:
> On 15 July 2016 at 09:28, Nicolas Boichat <drinkcat at chromium.org> wrote:
>> android.opengl.cts.WrapperTest#testGetIntegerv1 CTS test calls
>> eglTerminate, followed by eglReleaseThread. In that case, the
>> display will not be initialized when eglReleaseThread calls
>> MakeCurrent with NULL parameters, to unbind the context, which
>> causes a a segfault in drv->API.MakeCurrent (dri2_make_current),
>> either in glFlush or in a latter call.
>>
>> A similar case is observed in this bug:
>> https://bugs.freedesktop.org/show_bug.cgi?id=69622, where the test
>> call eglTerminate, then eglMakeCurrent(dpy, NULL, NULL, NULL).
>>
>> This patch:
>> 1. Validates that the display is initialized, if ctx/dsurf/rsurf
>> are not all NULL.
>> 2. Does not call glFlush/unBindContext is there is no display.
>> 3. However, it still goes through the normal path as
>> drv->API.DestroyContext decrements the reference count on the
>> context, and frees the structure.
>>
>> Cc: "11.2 12.0" <mesa-stable at lists.freedesktop.org>
>> Signed-off-by: Nicolas Boichat <drinkcat at chromium.org>
>> ---
>>
>> I did not actually test the case reported on the bug, only the CTS
>> test, would be nice if someone can try it out (Rhys? Chad?).
>>
>> Applies after "egl/dri2: dri2_make_current: Set EGL error if bindContext
>> fails", but wanted to keep the 2 patches separate as they are different
>> problems and discussions.
>>
>> src/egl/drivers/dri2/egl_dri2.c | 13 ++++++++++---
>> 1 file changed, 10 insertions(+), 3 deletions(-)
>>
>> diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
>> index 2e97d85..3269e52 100644
>> --- a/src/egl/drivers/dri2/egl_dri2.c
>> +++ b/src/egl/drivers/dri2/egl_dri2.c
>> @@ -1166,7 +1166,8 @@ dri2_destroy_context(_EGLDriver *drv, _EGLDisplay *disp, _EGLContext *ctx)
>> struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
>>
>> if (_eglPutContext(ctx)) {
>> - dri2_dpy->core->destroyContext(dri2_ctx->dri_context);
>> + if (dri2_dpy)
> Not sure if this is the right place/we need this.
>
> When calling eglDestroyContext (after eglTerminate or not) this cannot
> happen since the _EGL_CHECK_CONTEXT macro will dive into the _eglCheck
> functions and will bail out since the since the dpy is no initialized.
>
> The only case we can reach this is from dri2_make_current. See below for more.
Yes, that's correct.
>> @@ -1189,6 +1190,10 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf,
>> __DRIdrawable *ddraw, *rdraw;
>> __DRIcontext *cctx;
>>
>> + /* Check that the display is initialized */
>> + if ((ctx != NULL || dsurf != NULL || rsurf != NULL) && !dri2_dpy)
>> + return _eglError(EGL_NOT_INITIALIZED, "eglMakeCurrent error");
>> +
> Was doing to say "the first few lines in eglMakeCurrent) should
> already handle these" only to realise that dri2_dpy doesn't wrap
> around/subclass the respective _EGLfoo type (_EGLDisplay), like the
> dri2_egl_{surface,context...} :-\
>
> At the same time, dri2_make_current cannot (should not really) do
> anything in the !dri2_dpy case, since we cannot:
> - call unbind the old ctx bind the new one, or even restore/rebind
> the original ctx
> - free any of the ctx/surf resources. The latter should already be
> gone, as upon eglTerminate walk over the ctx/surf lists and tear them
> all down (see _eglReleaseDisplayResources).
>
> Thus something like the following should be fine imho.
>
> if (!dri2_dpy)
> /* copy/reword some my earlier comment in here */
> return 0;
Yes, that was my first approach, but we'd still leak the current dri2_ctx.
If I trace the reference counting right:
1. eglCreateContext => API.CreateContext =>
- _eglInitResource => RefCount = 1
- _eglLinkContext => RefCount = 2
2. eglMakeCurrent => API.MakeCurrent => _eglBindContext =>
eglGetContext => RefCount = 3
3. eglTerminate => API.Terminate => _eglReleaseDisplayResources =>
- _eglUnlinkContext => _eglUnlinkResource => Refcount = 2
- API.DestroyContext => RefCount = 1 (so context structure is not freed)
4. eglReleaseThread (or eglMakeCurrent(dpy, NULL, NULL, NULL)) =>
API.MakeCurrent(drv, disp, NULL, NULL, NULL) => API.DestroyContext =>
Refcount = 0 and structure is freed (with my current patch).
Well, the structure is freed, but driDestroyContext is never called
(so pcp->driScreenPriv->driver->DestroyContext is not called), which,
looks a bit concerning...
So one way around this is to get eglTerminate to destroy the current
context(s). But then the spec says
(https://www.khronos.org/registry/egl/sdk/docs/man/html/eglTerminate.xhtml):
If contexts or surfaces associated with display is current to any
thread, they are not released until they are no longer current as a
result of eglMakeCurrent.
(tbh I'm not sure if we are handling surfaces correctly either, but,
well, that's another problem...)
>> /* make new bindings */
>> if (!_eglBindContext(ctx, dsurf, rsurf, &old_ctx, &old_dsurf, &old_rsurf)) {
>> /* _eglBindContext already sets the EGL error (in _eglCheckMakeCurrent) */
>> @@ -1196,14 +1201,14 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf,
>> }
>>
>> /* flush before context switch */
>> - if (old_ctx && dri2_drv->glFlush)
>> + if (old_ctx && dri2_dpy && dri2_drv->glFlush)
> Mildly related: We might want to bail out in dri2_load on
> !dri2_drv->glFlush and drop the check here. If we cannot get glapi
> and/or glFlush symbol things are seriously stuffed and pretending to
> continue just won't work.
>
>> dri2_destroy_surface(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf)
>> {
>> struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
>> + if (!dri2_dpy)
>> + return EGL_TRUE;
> Analogous to the ctx hunk, this shouldn't be needed.
>
> Cheers,
> Emil
More information about the mesa-dev
mailing list