diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index f1d4da1685457..c01cf164a4f9e 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -220,6 +220,13 @@ struct syncobj_wait_entry { u64 point; }; +/* + * Upper limit for the number of handles passed to a single ioctl. The + * largest per-handle allocation is the wait entry array, keep it below the + * INT_MAX size limit of kvmalloc(). + */ +#define DRM_SYNCOBJ_MAX_HANDLES (INT_MAX / sizeof(struct syncobj_wait_entry)) + static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj, struct syncobj_wait_entry *wait); @@ -1051,7 +1058,7 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs, lockdep_assert_none_held_once(); } - points = kmalloc_array(count, sizeof(*points), GFP_KERNEL); + points = kvmalloc_array(count, sizeof(*points), GFP_KERNEL); if (points == NULL) return -ENOMEM; @@ -1064,7 +1071,7 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs, goto err_free_points; } - entries = kzalloc_objs(*entries, count); + entries = kvzalloc_objs(*entries, count); if (!entries) { timeout = -ENOMEM; goto err_free_points; @@ -1185,10 +1192,10 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs, &entries[i].fence_cb); dma_fence_put(entries[i].fence); } - kfree(entries); + kvfree(entries); err_free_points: - kfree(points); + kvfree(points); return timeout; } @@ -1238,7 +1245,10 @@ static int drm_syncobj_array_find(struct drm_file *file_private, struct drm_syncobj **syncobjs; int ret; - handles = kmalloc_array(count_handles, sizeof(*handles), GFP_KERNEL); + if (count_handles > DRM_SYNCOBJ_MAX_HANDLES) + return -EINVAL; + + handles = kvmalloc_array(count_handles, sizeof(*handles), GFP_KERNEL); if (handles == NULL) return -ENOMEM; @@ -1248,7 +1258,7 @@ static int drm_syncobj_array_find(struct drm_file *file_private, goto err_free_handles; } - syncobjs = kmalloc_objs(*syncobjs, count_handles); + syncobjs = kvmalloc_objs(*syncobjs, count_handles); if (syncobjs == NULL) { ret = -ENOMEM; goto err_free_handles; @@ -1262,16 +1272,16 @@ static int drm_syncobj_array_find(struct drm_file *file_private, } } - kfree(handles); + kvfree(handles); *syncobjs_out = syncobjs; return 0; err_put_syncobjs: while (i-- > 0) drm_syncobj_put(syncobjs[i]); - kfree(syncobjs); + kvfree(syncobjs); err_free_handles: - kfree(handles); + kvfree(handles); return ret; } @@ -1283,7 +1293,7 @@ static void drm_syncobj_array_free(struct drm_syncobj **syncobjs, for (i = 0; i < count; i++) drm_syncobj_put(syncobjs[i]); - kfree(syncobjs); + kvfree(syncobjs); } int @@ -1572,8 +1582,8 @@ drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data, if (ret < 0) return ret; - points = kmalloc_array(args->count_handles, sizeof(*points), - GFP_KERNEL); + points = kvmalloc_array(args->count_handles, sizeof(*points), + GFP_KERNEL); if (!points) { ret = -ENOMEM; goto out; @@ -1586,7 +1596,7 @@ drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data, goto err_points; } - chains = kmalloc_objs(*chains, args->count_handles); + chains = kvmalloc_objs(*chains, args->count_handles); if (!chains) { ret = -ENOMEM; goto err_points; @@ -1609,9 +1619,9 @@ drm_syncobj_timeline_signal_ioctl(struct drm_device *dev, void *data, dma_fence_put(fence); } err_chains: - kfree(chains); + kvfree(chains); err_points: - kfree(points); + kvfree(points); out: drm_syncobj_array_free(syncobjs, args->count_handles);