RTEMS | testsuites/fstests: Check JFFS2 unmount releases its resources (!1440)
Sam Price (@TheSamPrice)
gitlab at rtems.org
Fri Aug 21 03:52:33 UTC 2026
Sam Price commented: https://gitlab.rtems.org/rtems/rtos/rtems/-/merge_requests/1440#note_157676
The two diagnostics this change answers, from `gcc -fanalyzer` at 15.2.0,
built for `riscv/mbv`:
```
../../../cpukit/libfs/src/jffs2/src/fs-rtems.c:1093:22: warning: use after 'free' of 'root_i' [CWE-416] [-Wanalyzer-use-after-free]
../../../cpukit/libfs/src/jffs2/src/fs-rtems.c:1098:9: warning: double-'free' of 'root_i' [CWE-415] [-Wanalyzer-double-free]
```
Both are false positives. The analyser assumes `jffs2_iput()` frees its
argument, which it only does when `i_count` reaches zero *and* `i_nlink` is
zero. The assert above the call fixes `i_count` at 1, so it turns on the root
inode's link count, which is not visible at that point. The test settles it by
measurement: nine mount and unmount cycles, `malloc_walk()` after each, and a
resource snapshot compared across them.
The full analyser output, with the event paths it reports, is below.
<details>
<summary>gcc -fanalyzer, both diagnostics with event paths (247 lines)</summary>
```
../../../cpukit/libfs/src/jffs2/src/fs-rtems.c:1093:22: warning: use after 'free' of 'root_i' [CWE-416] [-Wanalyzer-use-after-free]
1093 | assert(root_i->i_cache_next == NULL);
| ~~~~~~^~~~~~~~~~~~~~
'rtems_jffs2_fsunmount': events 1-2
│
│ 1073 | static void rtems_jffs2_fsunmount(rtems_filesystem_mount_table_entry_t *mt_entry)
│ | ^~~~~~~~~~~~~~~~~~~~~
│ | |
│ | (1) entry to 'rtems_jffs2_fsunmount'
│......
│ 1092 | icache_evict(root_i, NULL);
│ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
│ | |
│ | (2) calling 'icache_evict' from 'rtems_jffs2_fsunmount'
│
└──> 'icache_evict': events 3-8
│
│ 71 | static void icache_evict(struct _inode *root_i, struct _inode *i)
│ | ^~~~~~~~~~~~
│ | |
│ | (3) entry to 'icache_evict'
│......
│ 80 | while (this) {
│ | ~~~~
│ | |
│ | (4) following 'true' branch (when 'this' is non-NULL)... ─>─┐
│ | │
│ | │
│ |┌───────────────────────────────────────────────────────────────────────────┘
│ 81 |│ next = this->i_cache_next;
│ |│ ~~~~~~~~~~~~~~~~~~~~~~~~~
│ |│ |
│ |└────────────────────>(5) ...to here
│ 82 | if (this != i && this->i_count == 0) {
│ | ~
│ | |
│ | (6) following 'true' branch... ─>─┐
│ | │
│ | │
│ |┌─────────────────────────────────────────────────────┘
│ 83 |│ struct _inode *parent = this->i_parent;
│ |│ ~~~~~~
│ |│ |
│ |└──────────────────────────────────────>(7) ...to here
│......
│ 90 | free(this);
│ | ~~~~~~~~~~
│ | |
│ | (8) freed here
│
<──────┘
│
'rtems_jffs2_fsunmount': event 9
│
│ 1092 | icache_evict(root_i, NULL);
│ | ^~~~~~~~~~~~~~~~~~~~~~~~~~
│ | |
│ | (9) returning to 'rtems_jffs2_fsunmount' from 'icache_evict'
│
'rtems_jffs2_fsunmount': event 10
│
│ 1093 | assert(root_i->i_cache_next == NULL);
│ | ~~~~~~^~~~~~~~~~~~~~
│ | |
│ | (10) ⚠️ use after 'free' of 'root_i'; freed at (8)
│
../../../cpukit/libfs/src/jffs2/src/fs-rtems.c:1098:9: warning: double-'free' of 'root_i' [CWE-415] [-Wanalyzer-double-free]
1098 | free(root_i);
| ^~~~~~~~~~~~
'rtems_jffs2_fsunmount': events 1-2
│
│ 1073 | static void rtems_jffs2_fsunmount(rtems_filesystem_mount_table_entry_t *mt_entry)
│ | ^~~~~~~~~~~~~~~~~~~~~
│ | |
│ | (1) entry to 'rtems_jffs2_fsunmount'
│......
│ 1092 | icache_evict(root_i, NULL);
│ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
│ | |
│ | (2) calling 'icache_evict' from 'rtems_jffs2_fsunmount'
│
└──> 'icache_evict': events 3-4
│
│ 71 | static void icache_evict(struct _inode *root_i, struct _inode *i)
│ | ^~~~~~~~~~~~
│ | |
│ | (3) entry to 'icache_evict'
│......
│ 82 | if (this != i && this->i_count == 0) {
│ | ~
│ | |
│ | (4) following 'true' branch (when 'this != i')... ─>─┐
│ | │
│
'icache_evict': event 5
│
│ | │
│ |┌────────────────────────────────────────────────────────────────────────┘
│ 82 |│ if (this != i && this->i_count == 0) {
│ |│ ~~~~^~~~~~~~~
│ |│ |
│ |└────────────────────────────────────>(5) ...to here
│
'icache_evict': event 6
│
│ 82 | if (this != i && this->i_count == 0) {
│ | ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
│ | |
│ | (6) following 'false' branch... ─>─┐
│ | │
│
'icache_evict': event 7
│
│cc1:
│ (7): ...to here
│
<──────┘
│
'rtems_jffs2_fsunmount': event 8
│
│ 1092 | icache_evict(root_i, NULL);
│ | ^~~~~~~~~~~~~~~~~~~~~~~~~~
│ | |
│ | (8) returning to 'rtems_jffs2_fsunmount' from 'icache_evict'
│
'rtems_jffs2_fsunmount': event 9
│
│ 1093 | assert(root_i->i_cache_next == NULL);
│ | ~~~~~~^~~~~~~~~~~~~~
│ | |
│ | (9) state of 'INIT_VAL((*INIT_VAL((*INIT_VAL(mt_entry_9(D))).mt_fs_root)).location.node_access)': 'start' -> 'assumed-non-null' (NULL origin)
│
'rtems_jffs2_fsunmount': event 10
│
│ 1093 | assert(root_i->i_cache_next == NULL);
│ | ^~~~~~
│ | |
│ | (10) following 'false' branch... ─>─┐
│ | │
│
'rtems_jffs2_fsunmount': event 11
│
│ | │
│ |┌────────────────────────────────────────────┘
│ 1094 |│ assert(root_i->i_count == 1);
│ |│ ~~~~~~^~~~~~~~~
│ |│ |
│ |└────────────────────>(11) ...to here
│
'rtems_jffs2_fsunmount': event 12
│
│ 1094 | assert(root_i->i_count == 1);
│ | ^~~~~~
│ | |
│ | (12) following 'false' branch... ─>─┐
│ | │
│
'rtems_jffs2_fsunmount': events 13-14
│
│ | │
│ |┌────────────────────────────────────────────┘
│ 1095 |│ jffs2_iput(root_i);
│ |│ ^~~~~~~~~~~~~~~~~~
│ |│ |
│ |└───────>(13) ...to here
│ | (14) calling 'jffs2_iput' from 'rtems_jffs2_fsunmount'
│
└──> 'jffs2_iput': event 15
│
│ 1617 | void jffs2_iput(struct _inode *i)
│ | ^~~~~~~~~~
│ | |
│ | (15) entry to 'jffs2_iput'
│
'jffs2_iput': event 16
│
│ 1624 | assert(i != NULL);
│ | ^~~~~~
│ | |
│ | (16) following 'false' branch (when 'i' is non-NULL)... ─>─┐
│ | │
│
'jffs2_iput': events 17-21
│
│ | │
│ |┌───────────────────────────────────────────────────────────────────┘
│ 1626 |│ i->i_count--;
│ |│ ~^~~~~~~~~
│ |│ |
│ |└────────>(17) ...to here
│ 1627 |
│ 1628 | if (i->i_count < 0)
│ | ~
│ | |
│ | (18) following 'false' branch... ─>─┐
│ | │
│......
│ | │
│ |┌───────────────────────────────────────────────┘
│ 1631 |│ if (i->i_count)
│ |│ ~
│ |│ |
│ |└──────────>(19) ...to here
│ | (20) following 'false' branch... ─>─┐
│ | │
│......
│ | │
│ |┌───────────────────────────────────────────────┘
│ 1634 |│ if (!i->i_nlink) {
│ |│ ~~~~~~~~~~
│ |│ |
│ |└─────────────>(21) ...to here
│
'jffs2_iput': events 22-24
│
│ 1634 | if (!i->i_nlink) {
│ | ^
│ | |
│ | (22) following 'true' branch... ─>─┐
│ | │
│......
│ | │
│ |┌──────────────────────────────────────────────┘
│ 1638 |│ if (i->i_cache_prev)
│ |│ ~~~~~~~~~~~~~~~
│ |│ |
│ |└────────────────────>(23) ...to here
│......
│ 1646 | free(i);
│ | ~~~~~~~
│ | |
│ | (24) first 'free' here
│
<──────┘
│
'rtems_jffs2_fsunmount': events 25-26
│
│ 1095 | jffs2_iput(root_i);
│ | ^~~~~~~~~~~~~~~~~~
│ | |
│ | (25) returning to 'rtems_jffs2_fsunmount' from 'jffs2_iput'
│......
│ 1098 | free(root_i);
│ | ~~~~~~~~~~~~
│ | |
│ | (26) ⚠️ second 'free' here; first 'free' was at (24)
│
```
</details>
--
View it on GitLab: https://gitlab.rtems.org/rtems/rtos/rtems/-/merge_requests/1440#note_157676
You're receiving this email because of your account on gitlab.rtems.org. Unsubscribe from this thread: https://gitlab.rtems.org/-/sent_notifications/5-2635qsv5cljo0p9tugmdtjd22-1d/unsubscribe | Manage all notifications: https://gitlab.rtems.org/-/profile/notifications | Help: https://gitlab.rtems.org/help
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.rtems.org/pipermail/bugs/attachments/20260821/1a6cde1d/attachment-0001.htm>
More information about the bugs
mailing list