mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
proc: only bump parent nlink when registering directories
proc_register() increments the parent directory's link count for every entry it registers, while remove_proc_entry() and remove_proc_subtree() decrement it only when the removed entry is a directory. Regular files thus inflate the parent's count while they exist, and leak one link permanently on every create and remove cycle. For example, /proc/bus/pci/00 with twenty-two device files and no subdirectories reports nlink 24 instead of 2, and SR-IOV VF enable and disable cycles, each creating and removing the VF config space entries under /proc/bus/pci/<bus>, inflate the link count of that directory without bound. Before commite06689bf57("proc: change ->nlink under proc_subdir_lock"), the increment lived in proc_mkdir_data() and proc_create_mount_point(), and was therefore applied only to directories. Moving it into proc_register() to bring it under proc_subdir_lock dropped the S_ISDIR check. Thus, move the nlink accounting into pde_subdir_insert() and pde_erase(), only updating it for directories in both, so the link count is always changed together with the directory entry itself. Fixes:e06689bf57("proc: change ->nlink under proc_subdir_lock") Cc: stable@vger.kernel.org # v5.5+ Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Link: https://patch.msgid.link/20260613211005.921692-1-kwilczynski@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
committed by
Christian Brauner
parent
681e452683
commit
16b02eb4b9
@@ -112,6 +112,8 @@ static bool pde_subdir_insert(struct proc_dir_entry *dir,
|
||||
/* Add new node and rebalance tree. */
|
||||
rb_link_node(&de->subdir_node, parent, new);
|
||||
rb_insert_color(&de->subdir_node, root);
|
||||
if (S_ISDIR(de->mode))
|
||||
dir->nlink++;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -404,7 +406,6 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
|
||||
write_unlock(&proc_subdir_lock);
|
||||
goto out_free_inum;
|
||||
}
|
||||
dir->nlink++;
|
||||
write_unlock(&proc_subdir_lock);
|
||||
|
||||
return dp;
|
||||
@@ -706,6 +707,8 @@ static void pde_erase(struct proc_dir_entry *pde, struct proc_dir_entry *parent)
|
||||
{
|
||||
rb_erase(&pde->subdir_node, &parent->subdir);
|
||||
RB_CLEAR_NODE(&pde->subdir_node);
|
||||
if (S_ISDIR(pde->mode))
|
||||
parent->nlink--;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -731,8 +734,6 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
|
||||
de = NULL;
|
||||
} else {
|
||||
pde_erase(de, parent);
|
||||
if (S_ISDIR(de->mode))
|
||||
parent->nlink--;
|
||||
}
|
||||
}
|
||||
write_unlock(&proc_subdir_lock);
|
||||
@@ -791,8 +792,6 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
|
||||
continue;
|
||||
}
|
||||
next = de->parent;
|
||||
if (S_ISDIR(de->mode))
|
||||
next->nlink--;
|
||||
write_unlock(&proc_subdir_lock);
|
||||
|
||||
proc_entry_rundown(de);
|
||||
|
||||
Reference in New Issue
Block a user