gpu-kms: Don't add outputs without modes

There seems to be a kernel race when one disconnects an external
monitor connected to a DisplayPort via a USB-C adapter. The race
results in a connector being reported as connected, but without any
modes supported.

This had the side effect that we tried to set a preferred mode to
the first listed mode, but as no modes were available, we instead tried
to dereference the first element of a NULL array, causing a
segmentation fault.

Mitigate this by skipping adding output if no supported modes are
advertised and the output doesn't support scaling, while moving the
fallback path for calculating a preferred output mode to after possibly
adding the common modes, to avoid the unvolentary NULL dereference.

https://bugzilla.gnome.org/show_bug.cgi?id=789501
This commit is contained in:
Jonas Ådahl 2017-11-03 15:55:07 +08:00
parent 6147be3dff
commit d092e913d6
3 changed files with 50 additions and 24 deletions

View File

@ -675,11 +675,21 @@ init_outputs (MetaGpuKms *gpu_kms,
{
MetaOutput *output;
MetaOutput *old_output;
GError *error = NULL;
old_output = find_output_by_id (old_outputs, connector->connector_id);
output = meta_create_kms_output (gpu_kms, connector, resources,
old_output);
outputs = g_list_prepend (outputs, output);
old_output,
&error);
if (!output)
{
g_warning ("Failed to create KMS output: %s", error->message);
g_error_free (error);
}
else
{
outputs = g_list_prepend (outputs, output);
}
}
}

View File

@ -438,9 +438,10 @@ compare_modes (const void *one,
return g_strcmp0 (b->name, a->name);
}
static void
init_output_modes (MetaOutput *output,
MetaGpuKms *gpu_kms)
static gboolean
init_output_modes (MetaOutput *output,
MetaGpuKms *gpu_kms,
GError **error)
{
MetaOutputKms *output_kms = output->driver_private;
unsigned int i;
@ -460,15 +461,35 @@ init_output_modes (MetaOutput *output,
output->preferred_mode = output->modes[i];
}
/* FIXME: MSC feature bit? */
/* Presume that if the output supports scaling, then we have
* a panel fitter capable of adjusting any mode to suit.
*/
if (output_kms->has_scaling)
add_common_modes (output, gpu_kms);
if (!output->modes)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"No modes available");
return FALSE;
}
qsort (output->modes, output->n_modes,
sizeof (MetaCrtcMode *), compare_modes);
if (!output->preferred_mode)
output->preferred_mode = output->modes[0];
return TRUE;
}
MetaOutput *
meta_create_kms_output (MetaGpuKms *gpu_kms,
drmModeConnector *connector,
MetaKmsResources *resources,
MetaOutput *old_output)
meta_create_kms_output (MetaGpuKms *gpu_kms,
drmModeConnector *connector,
MetaKmsResources *resources,
MetaOutput *old_output,
GError **error)
{
MetaGpu *gpu = META_GPU (gpu_kms);
MetaOutput *output;
@ -527,17 +548,11 @@ meta_create_kms_output (MetaGpuKms *gpu_kms,
output->height_mm = connector->mmHeight;
}
init_output_modes (output, gpu_kms);
/* FIXME: MSC feature bit? */
/* Presume that if the output supports scaling, then we have
* a panel fitter capable of adjusting any mode to suit.
*/
if (output_kms->has_scaling)
add_common_modes (output, gpu_kms);
qsort (output->modes, output->n_modes,
sizeof (MetaCrtcMode *), compare_modes);
if (!init_output_modes (output, gpu_kms, error))
{
g_object_unref (output);
return NULL;
}
output_kms->n_encoders = connector->count_encoders;
output_kms->encoders = g_new0 (drmModeEncoderPtr, output_kms->n_encoders);

View File

@ -35,9 +35,10 @@ gboolean meta_output_kms_can_clone (MetaOutput *output,
GBytes * meta_output_kms_read_edid (MetaOutput *output);
MetaOutput * meta_create_kms_output (MetaGpuKms *gpu_kms,
drmModeConnector *connector,
MetaKmsResources *resources,
MetaOutput *old_output);
MetaOutput * meta_create_kms_output (MetaGpuKms *gpu_kms,
drmModeConnector *connector,
MetaKmsResources *resources,
MetaOutput *old_output,
GError **error);
#endif /* META_OUTPUT_KMS_H */