Skip to content

Commit 0299a13

Browse files
committed
Merge tag 'for-linus-iommufd' of git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd
Pull iommufd fixes from Jason Gunthorpe: "Two user triggerable problems: - Syzkaller found a way to trigger a WARN_ON and leak memory by racing destroy with other actions - There is still a bug in the "batch carry" stuff that gets invoked for complex cases with accesses and unmapping of huge pages. The test suite found this (triggers rarely)" * tag 'for-linus-iommufd' of git://git.kernel.org/pub/scm/linux/kernel/git/jgg/iommufd: iommufd: Set end correctly when doing batch carry iommufd: IOMMUFD_DESTROY should not increase the refcount
2 parents c75981a + b7c822f commit 0299a13

File tree

4 files changed

+76
-31
lines changed

4 files changed

+76
-31
lines changed

drivers/iommu/iommufd/device.c

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,7 @@ EXPORT_SYMBOL_NS_GPL(iommufd_device_bind, IOMMUFD);
109109
*/
110110
void iommufd_device_unbind(struct iommufd_device *idev)
111111
{
112-
bool was_destroyed;
113-
114-
was_destroyed = iommufd_object_destroy_user(idev->ictx, &idev->obj);
115-
WARN_ON(!was_destroyed);
112+
iommufd_object_destroy_user(idev->ictx, &idev->obj);
116113
}
117114
EXPORT_SYMBOL_NS_GPL(iommufd_device_unbind, IOMMUFD);
118115

@@ -382,7 +379,7 @@ void iommufd_device_detach(struct iommufd_device *idev)
382379
mutex_unlock(&hwpt->devices_lock);
383380

384381
if (hwpt->auto_domain)
385-
iommufd_object_destroy_user(idev->ictx, &hwpt->obj);
382+
iommufd_object_deref_user(idev->ictx, &hwpt->obj);
386383
else
387384
refcount_dec(&hwpt->obj.users);
388385

@@ -456,10 +453,7 @@ EXPORT_SYMBOL_NS_GPL(iommufd_access_create, IOMMUFD);
456453
*/
457454
void iommufd_access_destroy(struct iommufd_access *access)
458455
{
459-
bool was_destroyed;
460-
461-
was_destroyed = iommufd_object_destroy_user(access->ictx, &access->obj);
462-
WARN_ON(!was_destroyed);
456+
iommufd_object_destroy_user(access->ictx, &access->obj);
463457
}
464458
EXPORT_SYMBOL_NS_GPL(iommufd_access_destroy, IOMMUFD);
465459

drivers/iommu/iommufd/iommufd_private.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,19 @@ void iommufd_object_abort_and_destroy(struct iommufd_ctx *ictx,
176176
struct iommufd_object *obj);
177177
void iommufd_object_finalize(struct iommufd_ctx *ictx,
178178
struct iommufd_object *obj);
179-
bool iommufd_object_destroy_user(struct iommufd_ctx *ictx,
180-
struct iommufd_object *obj);
179+
void __iommufd_object_destroy_user(struct iommufd_ctx *ictx,
180+
struct iommufd_object *obj, bool allow_fail);
181+
static inline void iommufd_object_destroy_user(struct iommufd_ctx *ictx,
182+
struct iommufd_object *obj)
183+
{
184+
__iommufd_object_destroy_user(ictx, obj, false);
185+
}
186+
static inline void iommufd_object_deref_user(struct iommufd_ctx *ictx,
187+
struct iommufd_object *obj)
188+
{
189+
__iommufd_object_destroy_user(ictx, obj, true);
190+
}
191+
181192
struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx,
182193
size_t size,
183194
enum iommufd_object_type type);

drivers/iommu/iommufd/main.c

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -116,51 +116,91 @@ struct iommufd_object *iommufd_get_object(struct iommufd_ctx *ictx, u32 id,
116116
return obj;
117117
}
118118

119+
/*
120+
* Remove the given object id from the xarray if the only reference to the
121+
* object is held by the xarray. The caller must call ops destroy().
122+
*/
123+
static struct iommufd_object *iommufd_object_remove(struct iommufd_ctx *ictx,
124+
u32 id, bool extra_put)
125+
{
126+
struct iommufd_object *obj;
127+
XA_STATE(xas, &ictx->objects, id);
128+
129+
xa_lock(&ictx->objects);
130+
obj = xas_load(&xas);
131+
if (xa_is_zero(obj) || !obj) {
132+
obj = ERR_PTR(-ENOENT);
133+
goto out_xa;
134+
}
135+
136+
/*
137+
* If the caller is holding a ref on obj we put it here under the
138+
* spinlock.
139+
*/
140+
if (extra_put)
141+
refcount_dec(&obj->users);
142+
143+
if (!refcount_dec_if_one(&obj->users)) {
144+
obj = ERR_PTR(-EBUSY);
145+
goto out_xa;
146+
}
147+
148+
xas_store(&xas, NULL);
149+
if (ictx->vfio_ioas == container_of(obj, struct iommufd_ioas, obj))
150+
ictx->vfio_ioas = NULL;
151+
152+
out_xa:
153+
xa_unlock(&ictx->objects);
154+
155+
/* The returned object reference count is zero */
156+
return obj;
157+
}
158+
119159
/*
120160
* The caller holds a users refcount and wants to destroy the object. Returns
121161
* true if the object was destroyed. In all cases the caller no longer has a
122162
* reference on obj.
123163
*/
124-
bool iommufd_object_destroy_user(struct iommufd_ctx *ictx,
125-
struct iommufd_object *obj)
164+
void __iommufd_object_destroy_user(struct iommufd_ctx *ictx,
165+
struct iommufd_object *obj, bool allow_fail)
126166
{
167+
struct iommufd_object *ret;
168+
127169
/*
128170
* The purpose of the destroy_rwsem is to ensure deterministic
129171
* destruction of objects used by external drivers and destroyed by this
130172
* function. Any temporary increment of the refcount must hold the read
131173
* side of this, such as during ioctl execution.
132174
*/
133175
down_write(&obj->destroy_rwsem);
134-
xa_lock(&ictx->objects);
135-
refcount_dec(&obj->users);
136-
if (!refcount_dec_if_one(&obj->users)) {
137-
xa_unlock(&ictx->objects);
138-
up_write(&obj->destroy_rwsem);
139-
return false;
140-
}
141-
__xa_erase(&ictx->objects, obj->id);
142-
if (ictx->vfio_ioas && &ictx->vfio_ioas->obj == obj)
143-
ictx->vfio_ioas = NULL;
144-
xa_unlock(&ictx->objects);
176+
ret = iommufd_object_remove(ictx, obj->id, true);
145177
up_write(&obj->destroy_rwsem);
146178

179+
if (allow_fail && IS_ERR(ret))
180+
return;
181+
182+
/*
183+
* If there is a bug and we couldn't destroy the object then we did put
184+
* back the caller's refcount and will eventually try to free it again
185+
* during close.
186+
*/
187+
if (WARN_ON(IS_ERR(ret)))
188+
return;
189+
147190
iommufd_object_ops[obj->type].destroy(obj);
148191
kfree(obj);
149-
return true;
150192
}
151193

152194
static int iommufd_destroy(struct iommufd_ucmd *ucmd)
153195
{
154196
struct iommu_destroy *cmd = ucmd->cmd;
155197
struct iommufd_object *obj;
156198

157-
obj = iommufd_get_object(ucmd->ictx, cmd->id, IOMMUFD_OBJ_ANY);
199+
obj = iommufd_object_remove(ucmd->ictx, cmd->id, false);
158200
if (IS_ERR(obj))
159201
return PTR_ERR(obj);
160-
iommufd_ref_to_users(obj);
161-
/* See iommufd_ref_to_users() */
162-
if (!iommufd_object_destroy_user(ucmd->ictx, obj))
163-
return -EBUSY;
202+
iommufd_object_ops[obj->type].destroy(obj);
203+
kfree(obj);
164204
return 0;
165205
}
166206

drivers/iommu/iommufd/pages.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ static void batch_clear_carry(struct pfn_batch *batch, unsigned int keep_pfns)
297297
batch->pfns[0] = batch->pfns[batch->end - 1] +
298298
(batch->npfns[batch->end - 1] - keep_pfns);
299299
batch->npfns[0] = keep_pfns;
300-
batch->end = 0;
300+
batch->end = 1;
301301
}
302302

303303
static void batch_skip_carry(struct pfn_batch *batch, unsigned int skip_pfns)

0 commit comments

Comments
 (0)