Skip to content

Commit 104f956

Browse files
zackrgregkh
authored andcommitted
drm/vmwgfx: Keep a gem reference to user bos in surfaces
commit 91398b4 upstream. Surfaces can be backed (i.e. stored in) memory objects (mob's) which are created and managed by the userspace as GEM buffers. Surfaces grab only a ttm reference which means that the gem object can be deleted underneath us, especially in cases where prime buffer export is used. Make sure that all userspace surfaces which are backed by gem objects hold a gem reference to make sure they're not deleted before vmw surfaces are done with them, which fixes: ------------[ cut here ]------------ refcount_t: underflow; use-after-free. WARNING: CPU: 2 PID: 2632 at lib/refcount.c:28 refcount_warn_saturate+0xfb/0x150 Modules linked in: overlay vsock_loopback vmw_vsock_virtio_transport_common vmw_vsock_vmci_transport vsock snd_ens1371 snd_ac97_codec ac97_bus snd_pcm gameport> CPU: 2 PID: 2632 Comm: vmw_ref_count Not tainted 6.5.0-rc2-vmwgfx #1 Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020 RIP: 0010:refcount_warn_saturate+0xfb/0x150 Code: eb 9e 0f b6 1d 8b 5b a6 01 80 fb 01 0f 87 ba e4 80 00 83 e3 01 75 89 48 c7 c7 c0 3c f9 a3 c6 05 6f 5b a6 01 01 e8 15 81 98 ff <0f> 0b e9 6f ff ff ff 0f b> RSP: 0018:ffffbdc34344bba0 EFLAGS: 00010286 RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000027 RDX: ffff960475ea1548 RSI: 0000000000000001 RDI: ffff960475ea1540 RBP: ffffbdc34344bba8 R08: 0000000000000003 R09: 65646e75203a745f R10: ffffffffa5b32b20 R11: 72657466612d6573 R12: ffff96037d6a6400 R13: ffff9603484805b0 R14: 000000000000000b R15: ffff9603bed06060 FS: 00007f5fd8520c40(0000) GS:ffff960475e80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f5fda755000 CR3: 000000010d012005 CR4: 00000000003706e0 Call Trace: <TASK> ? show_regs+0x6e/0x80 ? refcount_warn_saturate+0xfb/0x150 ? __warn+0x91/0x150 ? refcount_warn_saturate+0xfb/0x150 ? report_bug+0x19d/0x1b0 ? handle_bug+0x46/0x80 ? exc_invalid_op+0x1d/0x80 ? asm_exc_invalid_op+0x1f/0x30 ? refcount_warn_saturate+0xfb/0x150 drm_gem_object_handle_put_unlocked+0xba/0x110 [drm] drm_gem_object_release_handle+0x6e/0x80 [drm] drm_gem_handle_delete+0x6a/0xc0 [drm] ? __pfx_vmw_bo_unref_ioctl+0x10/0x10 [vmwgfx] vmw_bo_unref_ioctl+0x33/0x40 [vmwgfx] drm_ioctl_kernel+0xbc/0x160 [drm] drm_ioctl+0x2d2/0x580 [drm] ? __pfx_vmw_bo_unref_ioctl+0x10/0x10 [vmwgfx] ? do_vmi_munmap+0xee/0x180 vmw_generic_ioctl+0xbd/0x180 [vmwgfx] vmw_unlocked_ioctl+0x19/0x20 [vmwgfx] __x64_sys_ioctl+0x99/0xd0 do_syscall_64+0x5d/0x90 ? syscall_exit_to_user_mode+0x2a/0x50 ? do_syscall_64+0x6d/0x90 ? handle_mm_fault+0x16e/0x2f0 ? exit_to_user_mode_prepare+0x34/0x170 ? irqentry_exit_to_user_mode+0xd/0x20 ? irqentry_exit+0x3f/0x50 ? exc_page_fault+0x8e/0x190 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 RIP: 0033:0x7f5fda51aaff Code: 00 48 89 44 24 18 31 c0 48 8d 44 24 60 c7 04 24 10 00 00 00 48 89 44 24 08 48 8d 44 24 20 48 89 44 24 10 b8 10 00 00 00 0f 05 <41> 89 c0 3d 00 f0 ff ff 7> RSP: 002b:00007ffd536a4d30 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 RAX: ffffffffffffffda RBX: 00007ffd536a4de0 RCX: 00007f5fda51aaff RDX: 00007ffd536a4de0 RSI: 0000000040086442 RDI: 0000000000000003 RBP: 0000000040086442 R08: 000055fa603ada50 R09: 0000000000000000 R10: 0000000000000001 R11: 0000000000000246 R12: 00007ffd536a51b8 R13: 0000000000000003 R14: 000055fa5ebb4c80 R15: 00007f5fda90f040 </TASK> ---[ end trace 0000000000000000 ]--- A lot of the analyis on the bug was done by Murray McAllister and Ian Forbes. Reported-by: Murray McAllister <[email protected]> Cc: Ian Forbes <[email protected]> Signed-off-by: Zack Rusin <[email protected]> Fixes: a950b98 ("drm/vmwgfx: Do not drop the reference to the handle too soon") Cc: <[email protected]> # v6.2+ Reviewed-by: Martin Krastev <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected] Signed-off-by: Jocelyn Falempe <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 5a4087a commit 104f956

10 files changed

+71
-55
lines changed

drivers/gpu/drm/vmwgfx/vmwgfx_bo.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ static int vmw_user_bo_synccpu_release(struct drm_file *filp,
595595
if (!(flags & drm_vmw_synccpu_allow_cs)) {
596596
atomic_dec(&vmw_bo->cpu_writers);
597597
}
598-
vmw_user_bo_unref(vmw_bo);
598+
vmw_user_bo_unref(&vmw_bo);
599599
}
600600

601601
return ret;
@@ -637,7 +637,7 @@ int vmw_user_bo_synccpu_ioctl(struct drm_device *dev, void *data,
637637
return ret;
638638

639639
ret = vmw_user_bo_synccpu_grab(vbo, arg->flags);
640-
vmw_user_bo_unref(vbo);
640+
vmw_user_bo_unref(&vbo);
641641
if (unlikely(ret != 0)) {
642642
if (ret == -ERESTARTSYS || ret == -EBUSY)
643643
return -EBUSY;
@@ -711,7 +711,6 @@ int vmw_user_bo_lookup(struct drm_file *filp,
711711
}
712712

713713
*out = gem_to_vmw_bo(gobj);
714-
ttm_bo_get(&(*out)->base);
715714

716715
return 0;
717716
}

drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,8 @@ static int vmw_cotable_resize(struct vmw_resource *res, size_t new_size)
407407
* for the new COTable. Initially pin the buffer object to make sure
408408
* we can use tryreserve without failure.
409409
*/
410-
ret = vmw_bo_create(dev_priv, new_size, &vmw_mob_placement,
411-
true, true, vmw_bo_bo_free, &buf);
410+
ret = vmw_gem_object_create(dev_priv, new_size, &vmw_mob_placement,
411+
true, true, vmw_bo_bo_free, &buf);
412412
if (ret) {
413413
DRM_ERROR("Failed initializing new cotable MOB.\n");
414414
return ret;
@@ -475,7 +475,7 @@ static int vmw_cotable_resize(struct vmw_resource *res, size_t new_size)
475475

476476
vmw_resource_mob_attach(res);
477477
/* Let go of the old mob. */
478-
vmw_bo_unreference(&old_buf);
478+
vmw_user_bo_unref(&old_buf);
479479
res->id = vcotbl->type;
480480

481481
ret = dma_resv_reserve_fences(bo->base.resv, 1);
@@ -492,7 +492,7 @@ static int vmw_cotable_resize(struct vmw_resource *res, size_t new_size)
492492
out_wait:
493493
ttm_bo_unpin(bo);
494494
ttm_bo_unreserve(bo);
495-
vmw_bo_unreference(&buf);
495+
vmw_user_bo_unref(&buf);
496496

497497
return ret;
498498
}

drivers/gpu/drm/vmwgfx/vmwgfx_drv.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,11 @@ static inline void vmw_bo_prio_del(struct vmw_buffer_object *vbo, int prio)
969969
/**
970970
* GEM related functionality - vmwgfx_gem.c
971971
*/
972+
extern int vmw_gem_object_create(struct vmw_private *dev_priv,
973+
size_t size, struct ttm_placement *placement,
974+
bool interruptible, bool pin,
975+
void (*bo_free)(struct ttm_buffer_object *bo),
976+
struct vmw_buffer_object **p_bo);
972977
extern int vmw_gem_object_create_with_handle(struct vmw_private *dev_priv,
973978
struct drm_file *filp,
974979
uint32_t size,
@@ -1600,12 +1605,19 @@ vmw_bo_reference(struct vmw_buffer_object *buf)
16001605
return buf;
16011606
}
16021607

1603-
static inline void vmw_user_bo_unref(struct vmw_buffer_object *vbo)
1608+
static inline struct vmw_buffer_object *vmw_user_bo_ref(struct vmw_buffer_object *vbo)
16041609
{
1605-
if (vbo) {
1606-
ttm_bo_put(&vbo->base);
1607-
drm_gem_object_put(&vbo->base.base);
1608-
}
1610+
drm_gem_object_get(&vbo->base.base);
1611+
return vbo;
1612+
}
1613+
1614+
static inline void vmw_user_bo_unref(struct vmw_buffer_object **buf)
1615+
{
1616+
struct vmw_buffer_object *tmp_buf = *buf;
1617+
1618+
*buf = NULL;
1619+
if (tmp_buf)
1620+
drm_gem_object_put(&tmp_buf->base.base);
16091621
}
16101622

16111623
static inline void vmw_fifo_resource_inc(struct vmw_private *dev_priv)

drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ static int vmw_translate_mob_ptr(struct vmw_private *dev_priv,
11471147
SVGAMobId *id,
11481148
struct vmw_buffer_object **vmw_bo_p)
11491149
{
1150-
struct vmw_buffer_object *vmw_bo;
1150+
struct vmw_buffer_object *vmw_bo, *tmp_bo;
11511151
uint32_t handle = *id;
11521152
struct vmw_relocation *reloc;
11531153
int ret;
@@ -1159,7 +1159,8 @@ static int vmw_translate_mob_ptr(struct vmw_private *dev_priv,
11591159
return PTR_ERR(vmw_bo);
11601160
}
11611161
ret = vmw_validation_add_bo(sw_context->ctx, vmw_bo, true, false);
1162-
vmw_user_bo_unref(vmw_bo);
1162+
tmp_bo = vmw_bo;
1163+
vmw_user_bo_unref(&tmp_bo);
11631164
if (unlikely(ret != 0))
11641165
return ret;
11651166

@@ -1201,7 +1202,7 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,
12011202
SVGAGuestPtr *ptr,
12021203
struct vmw_buffer_object **vmw_bo_p)
12031204
{
1204-
struct vmw_buffer_object *vmw_bo;
1205+
struct vmw_buffer_object *vmw_bo, *tmp_bo;
12051206
uint32_t handle = ptr->gmrId;
12061207
struct vmw_relocation *reloc;
12071208
int ret;
@@ -1213,7 +1214,8 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,
12131214
return PTR_ERR(vmw_bo);
12141215
}
12151216
ret = vmw_validation_add_bo(sw_context->ctx, vmw_bo, false, false);
1216-
vmw_user_bo_unref(vmw_bo);
1217+
tmp_bo = vmw_bo;
1218+
vmw_user_bo_unref(&tmp_bo);
12171219
if (unlikely(ret != 0))
12181220
return ret;
12191221

drivers/gpu/drm/vmwgfx/vmwgfx_gem.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,22 @@ void vmw_gem_destroy(struct ttm_buffer_object *bo)
133133
kfree(vbo);
134134
}
135135

136+
int vmw_gem_object_create(struct vmw_private *vmw,
137+
size_t size, struct ttm_placement *placement,
138+
bool interruptible, bool pin,
139+
void (*bo_free)(struct ttm_buffer_object *bo),
140+
struct vmw_buffer_object **p_bo)
141+
{
142+
int ret = vmw_bo_create(vmw, size, placement, interruptible, pin, bo_free, p_bo);
143+
144+
if (ret != 0)
145+
goto out_no_bo;
146+
147+
(*p_bo)->base.base.funcs = &vmw_gem_object_funcs;
148+
out_no_bo:
149+
return ret;
150+
}
151+
136152
int vmw_gem_object_create_with_handle(struct vmw_private *dev_priv,
137153
struct drm_file *filp,
138154
uint32_t size,
@@ -141,16 +157,14 @@ int vmw_gem_object_create_with_handle(struct vmw_private *dev_priv,
141157
{
142158
int ret;
143159

144-
ret = vmw_bo_create(dev_priv, size,
145-
(dev_priv->has_mob) ?
160+
ret = vmw_gem_object_create(dev_priv, size,
161+
(dev_priv->has_mob) ?
146162
&vmw_sys_placement :
147163
&vmw_vram_sys_placement,
148-
true, false, &vmw_gem_destroy, p_vbo);
164+
true, false, &vmw_gem_destroy, p_vbo);
149165
if (ret != 0)
150166
goto out_no_bo;
151167

152-
(*p_vbo)->base.base.funcs = &vmw_gem_object_funcs;
153-
154168
ret = drm_gem_handle_create(filp, &(*p_vbo)->base.base, handle);
155169
out_no_bo:
156170
return ret;

drivers/gpu/drm/vmwgfx/vmwgfx_kms.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,8 +1402,8 @@ static int vmw_create_bo_proxy(struct drm_device *dev,
14021402
/* Reserve and switch the backing mob. */
14031403
mutex_lock(&res->dev_priv->cmdbuf_mutex);
14041404
(void) vmw_resource_reserve(res, false, true);
1405-
vmw_bo_unreference(&res->backup);
1406-
res->backup = vmw_bo_reference(bo_mob);
1405+
vmw_user_bo_unref(&res->backup);
1406+
res->backup = vmw_user_bo_ref(bo_mob);
14071407
res->backup_offset = 0;
14081408
vmw_resource_unreserve(res, false, false, false, NULL, 0);
14091409
mutex_unlock(&res->dev_priv->cmdbuf_mutex);
@@ -1600,7 +1600,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
16001600
err_out:
16011601
/* vmw_user_lookup_handle takes one ref so does new_fb */
16021602
if (bo)
1603-
vmw_user_bo_unref(bo);
1603+
vmw_user_bo_unref(&bo);
16041604
if (surface)
16051605
vmw_surface_unreference(&surface);
16061606

drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ int vmw_overlay_ioctl(struct drm_device *dev, void *data,
457457

458458
ret = vmw_overlay_update_stream(dev_priv, buf, arg, true);
459459

460-
vmw_user_bo_unref(buf);
460+
vmw_user_bo_unref(&buf);
461461

462462
out_unlock:
463463
mutex_unlock(&overlay->mutex);

drivers/gpu/drm/vmwgfx/vmwgfx_resource.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ static void vmw_resource_release(struct kref *kref)
140140
if (res->coherent)
141141
vmw_bo_dirty_release(res->backup);
142142
ttm_bo_unreserve(bo);
143-
vmw_bo_unreference(&res->backup);
143+
vmw_user_bo_unref(&res->backup);
144144
}
145145

146146
if (likely(res->hw_destroy != NULL)) {
@@ -330,10 +330,10 @@ static int vmw_resource_buf_alloc(struct vmw_resource *res,
330330
return 0;
331331
}
332332

333-
ret = vmw_bo_create(res->dev_priv, res->backup_size,
334-
res->func->backup_placement,
335-
interruptible, false,
336-
&vmw_bo_bo_free, &backup);
333+
ret = vmw_gem_object_create(res->dev_priv, res->backup_size,
334+
res->func->backup_placement,
335+
interruptible, false,
336+
&vmw_bo_bo_free, &backup);
337337
if (unlikely(ret != 0))
338338
goto out_no_bo;
339339

@@ -452,11 +452,11 @@ void vmw_resource_unreserve(struct vmw_resource *res,
452452
vmw_resource_mob_detach(res);
453453
if (res->coherent)
454454
vmw_bo_dirty_release(res->backup);
455-
vmw_bo_unreference(&res->backup);
455+
vmw_user_bo_unref(&res->backup);
456456
}
457457

458458
if (new_backup) {
459-
res->backup = vmw_bo_reference(new_backup);
459+
res->backup = vmw_user_bo_ref(new_backup);
460460

461461
/*
462462
* The validation code should already have added a
@@ -544,7 +544,7 @@ vmw_resource_check_buffer(struct ww_acquire_ctx *ticket,
544544
ttm_bo_put(val_buf->bo);
545545
val_buf->bo = NULL;
546546
if (backup_dirty)
547-
vmw_bo_unreference(&res->backup);
547+
vmw_user_bo_unref(&res->backup);
548548

549549
return ret;
550550
}
@@ -719,7 +719,7 @@ int vmw_resource_validate(struct vmw_resource *res, bool intr,
719719
goto out_no_validate;
720720
else if (!res->func->needs_backup && res->backup) {
721721
WARN_ON_ONCE(vmw_resource_mob_attached(res));
722-
vmw_bo_unreference(&res->backup);
722+
vmw_user_bo_unref(&res->backup);
723723
}
724724

725725
return 0;

drivers/gpu/drm/vmwgfx/vmwgfx_shader.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ static int vmw_gb_shader_init(struct vmw_private *dev_priv,
177177

178178
res->backup_size = size;
179179
if (byte_code) {
180-
res->backup = vmw_bo_reference(byte_code);
180+
res->backup = vmw_user_bo_ref(byte_code);
181181
res->backup_offset = offset;
182182
}
183183
shader->size = size;
@@ -806,7 +806,7 @@ static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv,
806806
shader_type, num_input_sig,
807807
num_output_sig, tfile, shader_handle);
808808
out_bad_arg:
809-
vmw_user_bo_unref(buffer);
809+
vmw_user_bo_unref(&buffer);
810810
return ret;
811811
}
812812

drivers/gpu/drm/vmwgfx/vmwgfx_surface.c

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -683,9 +683,6 @@ static void vmw_user_surface_base_release(struct ttm_base_object **p_base)
683683
container_of(base, struct vmw_user_surface, prime.base);
684684
struct vmw_resource *res = &user_srf->srf.res;
685685

686-
if (res && res->backup)
687-
drm_gem_object_put(&res->backup->base.base);
688-
689686
*p_base = NULL;
690687
vmw_resource_unreference(&res);
691688
}
@@ -848,23 +845,17 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
848845
* expect a backup buffer to be present.
849846
*/
850847
if (dev_priv->has_mob && req->shareable) {
851-
uint32_t backup_handle;
852-
853-
ret = vmw_gem_object_create_with_handle(dev_priv,
854-
file_priv,
855-
res->backup_size,
856-
&backup_handle,
857-
&res->backup);
848+
ret = vmw_gem_object_create(dev_priv,
849+
res->backup_size,
850+
&vmw_sys_placement,
851+
true,
852+
false,
853+
&vmw_gem_destroy,
854+
&res->backup);
858855
if (unlikely(ret != 0)) {
859856
vmw_resource_unreference(&res);
860857
goto out_unlock;
861858
}
862-
vmw_bo_reference(res->backup);
863-
/*
864-
* We don't expose the handle to the userspace and surface
865-
* already holds a gem reference
866-
*/
867-
drm_gem_handle_delete(file_priv, backup_handle);
868859
}
869860

870861
tmp = vmw_resource_reference(&srf->res);
@@ -1505,7 +1496,7 @@ vmw_gb_surface_define_internal(struct drm_device *dev,
15051496
if (ret == 0) {
15061497
if (res->backup->base.base.size < res->backup_size) {
15071498
VMW_DEBUG_USER("Surface backup buffer too small.\n");
1508-
vmw_bo_unreference(&res->backup);
1499+
vmw_user_bo_unref(&res->backup);
15091500
ret = -EINVAL;
15101501
goto out_unlock;
15111502
} else {
@@ -1519,8 +1510,6 @@ vmw_gb_surface_define_internal(struct drm_device *dev,
15191510
res->backup_size,
15201511
&backup_handle,
15211512
&res->backup);
1522-
if (ret == 0)
1523-
vmw_bo_reference(res->backup);
15241513
}
15251514

15261515
if (unlikely(ret != 0)) {

0 commit comments

Comments
 (0)