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,

Reply via email to