From d1308f4fe7f94aae51ca9f70947aea8e09597f37 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 6 Jan 2010 15:19:25 +0000 Subject: modes: Retry GETRESOURCES if a hotplug event occurs between the two ioctls Peter Clifton hit an issue whereby he had a spurious TV hotplug event that occurred between the two GETRESOURCES ioctls that caused the kernel to skip filling one array and led to a crash (as the size of the allocated and initialised block of memory differed from the reported size). Fixes: http://bugs.freedesktop.org/show_bug.cgi?id=25912 Crash whilst probing modes Signed-off-by: Chris Wilson Reported-by: Peter Clifton --- xf86drmMode.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/xf86drmMode.c b/xf86drmMode.c index ca36b71b..7edc625a 100644 --- a/xf86drmMode.c +++ b/xf86drmMode.c @@ -136,14 +136,16 @@ void drmModeFreeEncoder(drmModeEncoderPtr ptr) drmModeResPtr drmModeGetResources(int fd) { - struct drm_mode_card_res res; + struct drm_mode_card_res res, counts; drmModeResPtr r = 0; +retry: memset(&res, 0, sizeof(struct drm_mode_card_res)); - if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)) return 0; + counts = res; + if (res.count_fbs) res.fb_id_ptr = VOID2U64(drmMalloc(res.count_fbs*sizeof(uint32_t))); if (res.count_crtcs) @@ -153,18 +155,31 @@ drmModeResPtr drmModeGetResources(int fd) if (res.count_encoders) res.encoder_id_ptr = VOID2U64(drmMalloc(res.count_encoders*sizeof(uint32_t))); - if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)) { - r = NULL; + if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)) goto err_allocs; + + /* The number of available connectors and etc may have changed with a + * hotplug event in between the ioctls, in which case the field is + * silently ignored by the kernel. + */ + if (counts.count_fbs < res.count_fbs || + counts.count_crtcs < res.count_crtcs || + counts.count_connectors < res.count_connectors || + counts.count_encoders < res.count_encoders) + { + drmFree(U642VOID(res.fb_id_ptr)); + drmFree(U642VOID(res.crtc_id_ptr)); + drmFree(U642VOID(res.connector_id_ptr)); + drmFree(U642VOID(res.encoder_id_ptr)); + + goto retry; } /* * return */ - - if (!(r = drmMalloc(sizeof(*r)))) - return 0; + goto err_allocs; r->min_width = res.min_width; r->max_width = res.max_width; -- cgit v1.2.3