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,13 +675,23 @@ init_outputs (MetaGpuKms *gpu_kms,
{ {
MetaOutput *output; MetaOutput *output;
MetaOutput *old_output; MetaOutput *old_output;
GError *error = NULL;
old_output = find_output_by_id (old_outputs, connector->connector_id); old_output = find_output_by_id (old_outputs, connector->connector_id);
output = meta_create_kms_output (gpu_kms, connector, resources, output = meta_create_kms_output (gpu_kms, connector, resources,
old_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); outputs = g_list_prepend (outputs, output);
} }
} }
}
/* Sort the outputs for easier handling in MetaMonitorConfig */ /* Sort the outputs for easier handling in MetaMonitorConfig */

View File

@ -438,9 +438,10 @@ compare_modes (const void *one,
return g_strcmp0 (b->name, a->name); return g_strcmp0 (b->name, a->name);
} }
static void static gboolean
init_output_modes (MetaOutput *output, init_output_modes (MetaOutput *output,
MetaGpuKms *gpu_kms) MetaGpuKms *gpu_kms,
GError **error)
{ {
MetaOutputKms *output_kms = output->driver_private; MetaOutputKms *output_kms = output->driver_private;
unsigned int i; unsigned int i;
@ -460,15 +461,35 @@ init_output_modes (MetaOutput *output,
output->preferred_mode = output->modes[i]; 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) if (!output->preferred_mode)
output->preferred_mode = output->modes[0]; output->preferred_mode = output->modes[0];
return TRUE;
} }
MetaOutput * MetaOutput *
meta_create_kms_output (MetaGpuKms *gpu_kms, meta_create_kms_output (MetaGpuKms *gpu_kms,
drmModeConnector *connector, drmModeConnector *connector,
MetaKmsResources *resources, MetaKmsResources *resources,
MetaOutput *old_output) MetaOutput *old_output,
GError **error)
{ {
MetaGpu *gpu = META_GPU (gpu_kms); MetaGpu *gpu = META_GPU (gpu_kms);
MetaOutput *output; MetaOutput *output;
@ -527,17 +548,11 @@ meta_create_kms_output (MetaGpuKms *gpu_kms,
output->height_mm = connector->mmHeight; output->height_mm = connector->mmHeight;
} }
init_output_modes (output, gpu_kms); if (!init_output_modes (output, gpu_kms, error))
{
/* FIXME: MSC feature bit? */ g_object_unref (output);
/* Presume that if the output supports scaling, then we have return NULL;
* 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);
output_kms->n_encoders = connector->count_encoders; output_kms->n_encoders = connector->count_encoders;
output_kms->encoders = g_new0 (drmModeEncoderPtr, output_kms->n_encoders); output_kms->encoders = g_new0 (drmModeEncoderPtr, output_kms->n_encoders);

View File

@ -38,6 +38,7 @@ GBytes * meta_output_kms_read_edid (MetaOutput *output);
MetaOutput * meta_create_kms_output (MetaGpuKms *gpu_kms, MetaOutput * meta_create_kms_output (MetaGpuKms *gpu_kms,
drmModeConnector *connector, drmModeConnector *connector,
MetaKmsResources *resources, MetaKmsResources *resources,
MetaOutput *old_output); MetaOutput *old_output,
GError **error);
#endif /* META_OUTPUT_KMS_H */ #endif /* META_OUTPUT_KMS_H */