This is an automated email from the ASF dual-hosted git repository. sjanc pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mynewt-core.git
commit 5a106e0ed99f29f9364c5ebf92b428eeae693487 Author: Szymon Janc <[email protected]> AuthorDate: Wed Oct 21 00:53:42 2026 +0200 fs/nffs: Fix use after free of unlinked directory Unlinking a directory freed its inode entry immediately, even if it was still referenced, e.g. by an open directory or by a directory entry returned from readdir. Accessing those afterwards (reading dirent name, continuing readdir or closing directory) used freed memory. Drop only the directory's own reference on unlink and free it when the last reference is dropped, same as is done for files. Dummy directory inodes created during restore are not referenced and are still freed immediately. Found by fs/nffs/selftest failing with OS_DEBUG_MODE enabled (mempool poison check). Assisted-by: Claude:claude-opus-5-5 --- fs/nffs/src/nffs_inode.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/fs/nffs/src/nffs_inode.c b/fs/nffs/src/nffs_inode.c index beea42d2b..c6797d062 100644 --- a/fs/nffs/src/nffs_inode.c +++ b/fs/nffs/src/nffs_inode.c @@ -36,6 +36,9 @@ static uint8_t *nffs_inode_filename_buf1 = /** A list of directory inodes with pending unlink operations. */ static struct nffs_hash_list nffs_inode_unlink_list; +static int nffs_inode_process_unlink_list(struct nffs_hash_entry **inout_next, + int ignore_corruption); + struct nffs_inode_entry * nffs_inode_entry_alloc(void) { @@ -384,6 +387,12 @@ nffs_inode_dec_refcnt(struct nffs_inode_entry *inode_entry) int rc; rc = nffs_inode_dec_refcnt_priv(inode_entry, 0); + if (rc != 0) { + return rc; + } + + /* Free directory that was unlinked while still referenced. */ + rc = nffs_inode_process_unlink_list(NULL, 0); return rc; } @@ -1136,7 +1145,16 @@ nffs_inode_unlink_from_ram_priv(struct nffs_inode *inode, nffs_inode_setflags(inode->ni_inode_entry, NFFS_INODE_FLAG_DELETED); if (nffs_hash_id_is_dir(inode->ni_inode_entry->nie_hash_entry.nhe_id)) { - nffs_inode_insert_unlink_list(inode->ni_inode_entry); + /* + * Directory still referenced (e.g. by open directory) is unlinked + * when its reference count drops to zero. Dummy directory inodes + * are not referenced at all. + */ + if (inode->ni_inode_entry->nie_refcnt > 1) { + inode->ni_inode_entry->nie_refcnt--; + } else { + nffs_inode_insert_unlink_list(inode->ni_inode_entry); + } rc = nffs_inode_process_unlink_list(out_next, ignore_corruption); } else { rc = nffs_inode_dec_refcnt_priv(inode->ni_inode_entry,
