diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2010-01-06 15:19:25 +0000 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2010-01-06 15:33:21 +0000 |
commit | d1308f4fe7f94aae51ca9f70947aea8e09597f37 (patch) | |
tree | 4db0684c9479c8cebe3003dbd4327bf8415ab197 /xf86drmMode.c | |
parent | 5dbc1b333b85695735dc5b484372758b9979b693 (diff) |
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 <chris@chris-wilson.co.uk>
Reported-by: Peter Clifton <pcjc2@cam.ac.uk>
Diffstat (limited to 'xf86drmMode.c')
-rw-r--r-- | xf86drmMode.c | 29 |
1 files 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; |