Skip to content

Commit b2e3d22

Browse files
kext: sysfs_node: harden sysfsnode_find() against races and double‑free
sysfsnode_find() assumes vnode reclaim implies sfsnode free; can double‑free or race. Don’t free new_sfsnode blindly after vnode_getwithvid() fails and track whether new_sfsnode was inserted into the hash. Only free nodes we know we own.
1 parent 1d2717a commit b2e3d22

1 file changed

Lines changed: 13 additions & 4 deletions

File tree

kext/sysfs_node.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ sysfsnode_find(sfsmount_t *smp, sfsid_t node_id, sfssnode_t *snode,
9494
boolean_t locked = TRUE;
9595
sfsnode_t *target_sfsnode = NULL; /* This is the node that we will return. */
9696
sfsnode_t *new_sfsnode = NULL; /* Newly allocated node. Will be freed if not used. */
97+
boolean_t new_sfsnode_inserted = FALSE;
9798
vnode_t target_vnode = NULL; /* Start by assuming we will not get a vnode. */
9899
int32_t mount_id = smp->pmnt_id; /* File system id. */
99100

@@ -188,6 +189,7 @@ sysfsnode_find(sfsmount_t *smp, sfsid_t node_id, sfssnode_t *snode,
188189
* it belongs to.
189190
*/
190191
LIST_INSERT_HEAD(hash_bucket, target_sfsnode, node_hash);
192+
new_sfsnode_inserted = TRUE;
191193
}
192194
}
193195

@@ -241,10 +243,17 @@ sysfsnode_find(sfsmount_t *smp, sfsid_t node_id, sfssnode_t *snode,
241243
* was removed from the hash and freed, so we will be restarting from scratch.
242244
*/
243245
lck_mtx_lock(sfsnode_hash_mutex);
244-
target_sfsnode = NULL;
245-
OSFree(new_sfsnode, sizeof(sfsnode_t), sysfs_osmalloc_tag);
246-
new_sfsnode = NULL;
247246
locked = TRUE;
247+
248+
/* We *assume* reclaim removed target_sfsnode from hash and freed it.
249+
* Do NOT free new_sfsnode unless we know it was never inserted.
250+
*/
251+
if (!new_sfsnode_inserted && new_sfsnode != NULL) {
252+
OSFree(new_sfsnode, sizeof(sfsnode_t), sysfs_osmalloc_tag);
253+
new_sfsnode = NULL;
254+
}
255+
256+
target_sfsnode = NULL;
248257
continue;
249258
}
250259

@@ -329,7 +338,7 @@ sysfsnode_find(sfsmount_t *smp, sfsid_t node_id, sfssnode_t *snode,
329338
* Free the node we allocated, if we didn't use it. We do this
330339
* after releasing the hash lock just in case it might block.
331340
*/
332-
if (new_sfsnode != NULL && new_sfsnode != target_sfsnode) {
341+
if (new_sfsnode != NULL && new_sfsnode != target_sfsnode && !new_sfsnode_inserted) {
333342
OSFree(new_sfsnode, sizeof(sfsnode_t), sysfs_osmalloc_tag);
334343
new_sfsnode = NULL;
335344
}

0 commit comments

Comments
 (0)