Bug fs::openFile assigns duplicate IDs causing memory leak and silent data corruption
The fs::openFile function determines the next file stream ID using the current size of the openedFiles map:
int virtualFileId = openedFiles.size();This breaks as soon as files are opened and closed out of sequence. Here's what happens:
- Open File A → gets ID 0 (map size is now 1)
- Open File B → gets ID 1 (map size is now 2)
- Close File A → map size drops back to 1
- Open File C →
openedFiles.size()returns 1 again → File C gets ID 1
Now File C has the same ID as File B. The map entry for ID 1 gets overwritten with File C's pointer — File B's ifstream* is never freed (memory leak), and any JS read operation using File B's ID will silently read from File C instead.
There is also a secondary race condition — openedFiles.size() is read before the mutex is acquired, so two concurrent openFile calls can independently compute the same ID even without any close operations involved.
Interestingly, os.cpp already handles this correctly for spawned process IDs using an atomic counter (nextVirtualPid). The same pattern is absent here.
To Reproduce
const idA = await Neutralino.filesystem.openFile("fileA.txt");
const idB = await Neutralino.filesystem.openFile("fileB.txt");
await Neutralino.filesystem.updateOpenedFile({ id: idA, event: "close" });
const idC = await Neutralino.filesystem.openFile("fileC.txt");
// idB and idC are now the same — reading idB silently reads fileC.txt
await Neutralino.filesystem.updateOpenedFile({ id: idB, event: "read" });Specifications
- OS: All platforms
- Neutralinojs version: Current
Source: neutralinojs/neutralinojs