modesetting: Fix ms_covering_crtc() segfault with non-modesetting slave primary

ms_covering_crtc() uses RRFirstOutput() to determine a primary output to fall
back to if a drawable is overlapping a slave output.

If the primary output is a slave output, RRFirstOutput() will return a slave
output even if passed a master ScreenPtr. ms_covering_crtc() dereferences the
output's devPrivate, which is invalid for non-modesetting outputs, and can
crash.

Changing RRFirstOutput() could have unintended side effects for other callers,
so this change replaces the call to RRFirstOutput() with ms_first_output().
ms_first_output() ignores the primary output if it doesn't match the given
ScreenPtr, choosing the first connected output instead.

Signed-off-by: Alex Goins <agoins@nvidia.com>
This commit is contained in:
Alex Goins 2019-08-28 18:24:16 -05:00 committed by Aaron Plattner
parent c82f814313
commit 3ef9029ace

View File

@ -90,6 +90,39 @@ ms_crtc_on(xf86CrtcPtr crtc)
return crtc->enabled && drmmode_crtc->dpms_mode == DPMSModeOn;
}
/*
* Return the first output which is connected to an active CRTC on this screen.
*
* RRFirstOutput() will return an output from a slave screen if it is primary,
* which is not the behavior that ms_covering_crtc() wants.
*/
static RROutputPtr ms_first_output(ScreenPtr pScreen)
{
rrScrPriv(pScreen);
RROutputPtr output;
int i, j;
if (!pScrPriv)
return NULL;
if (pScrPriv->primaryOutput && pScrPriv->primaryOutput->crtc &&
(pScrPriv->primaryOutput->pScreen == pScreen)) {
return pScrPriv->primaryOutput;
}
for (i = 0; i < pScrPriv->numCrtcs; i++) {
RRCrtcPtr crtc = pScrPriv->crtcs[i];
for (j = 0; j < pScrPriv->numOutputs; j++) {
output = pScrPriv->outputs[j];
if (output->crtc == crtc)
return output;
}
}
return NULL;
}
/*
* Return the crtc covering 'box'. If two crtcs cover a portion of
* 'box', then prefer the crtc with greater coverage.
@ -135,7 +168,7 @@ ms_covering_crtc(ScreenPtr pScreen, BoxPtr box, Bool screen_is_ms)
ScreenPtr slave;
if (dixPrivateKeyRegistered(rrPrivKey))
primary_output = RRFirstOutput(scrn->pScreen);
primary_output = ms_first_output(scrn->pScreen);
if (!primary_output || !primary_output->crtc)
return NULL;