Skip to content

fix(loader): randomized AppImage path pollutes luac cache #31165#35636

Merged
justinmk merged 1 commit intoneovim:masterfrom
shmerl:loader_cache_fix
Dec 10, 2025
Merged

fix(loader): randomized AppImage path pollutes luac cache #31165#35636
justinmk merged 1 commit intoneovim:masterfrom
shmerl:loader_cache_fix

Conversation

@shmerl
Copy link
Contributor

@shmerl shmerl commented Sep 5, 2025

Fixes infinite growth of number of *.luac files in the cache and actual cache file misses caused by randomness of filenames when using AppImage with loader enabled.

fix #31165

@shmerl shmerl changed the title fix(loader): prevent randomness in AppImage generated luac files in the cache #31165 fix(loader): prevent randomness in AppImage luac files in the cache #31165 Sep 5, 2025
@shmerl
Copy link
Contributor Author

shmerl commented Sep 5, 2025

Hm, it fails some checks like this:

runtime/lua/vim/loader.lua:122:11 [Warning] Can not infer type. (no-unknown)
        local idx = name:find('/usr/', 1, true)
              ^^^

I'm not exactly sure what it doesn't like. Any suggestion how to prevent that? The idx should be a number of nil.

@shmerl shmerl force-pushed the loader_cache_fix branch 3 times, most recently from becd3fe to c069214 Compare September 5, 2025 22:09
@justinmk justinmk marked this pull request as ready for review September 13, 2025 22:32
@justinmk
Copy link
Member

I pushed a change, can you confirm it works so we can wrap this up

@justinmk justinmk changed the title fix(loader): prevent randomness in AppImage luac files in the cache #31165 fix(loader): randomized AppImage path pollutes luac cache #31165 Sep 13, 2025
@shmerl
Copy link
Contributor Author

shmerl commented Sep 14, 2025

I pushed a change, can you confirm it works so we can wrap this up

Never did it before, how can I build an appimage from my branch?

@shmerl
Copy link
Contributor Author

shmerl commented Sep 14, 2025

OK, I managed to repack things using appimage-tool using the patched file. Checking.

@shmerl
Copy link
Contributor Author

shmerl commented Sep 14, 2025

Weird, even with fixed file I still see this accumulation:

Only in luac: %2ftmp%2f.mount_NeovimHiB7yR%2fusr%2fshare%2fnvim%2fruntime%2ffiletype.luac
Only in luac: %2ftmp%2f.mount_NeovimHiB7yR%2fusr%2fshare%2fnvim%2fruntime%2flua%2fvim%2ftermcap.luac
Only in luac: %2ftmp%2f.mount_NeovimHiB7yR%2fusr%2fshare%2fnvim%2fruntime%2fplugin%2feditorconfig.luac
Only in luac: %2ftmp%2f.mount_NeovimHiB7yR%2fusr%2fshare%2fnvim%2fruntime%2fplugin%2fman.luac
Only in luac: %2ftmp%2f.mount_NeovimHiB7yR%2fusr%2fshare%2fnvim%2fruntime%2fplugin%2fosc52.luac
Only in luac: %2ftmp%2f.mount_NeovimHiB7yR%2fusr%2fshare%2fnvim%2fruntime%2fplugin%2ftohtml.luac

FYI, the way I tested it. Extracted existing appimage release:

nvim --appimage-extract

Replaced loader.lua and then repacked things back with

wcurl https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage

./appimagetool-x86_64.AppImage --no-appstream squashfs-root 

Really not sure why it's not helping though. Could be something else is going on? I can try replacing things in the function you suggested for a test.

@shmerl
Copy link
Contributor Author

shmerl commented Sep 14, 2025

Nope, we are missing something here. Even moving that fix to the normalize function doesn't help.

@justinmk
Copy link
Member

Try removing the os.getenv('APPIMAGE') check

@shmerl
Copy link
Contributor Author

shmerl commented Sep 14, 2025

Nope, that's not it. It's still using that cursed path in filenames:

%2ftmp%2f.mount_NeovimygI0ZN%2fusr%2fshare%2...

I think something else is happening besides loader may be? Also, I can confirm that the variable is set if I read this from inside neovim itself

:= os.getenv('APPIMAGE')

Returns binary path.

@justinmk
Copy link
Member

To double check that the appimage "repack" is actually working, can you try throwing an error in the loader somewhere and confirm that the error is thrown after repack :)

@shmerl
Copy link
Contributor Author

shmerl commented Sep 14, 2025

Weird, adding error('foo') to that cache_filename function does nothing. Even adding it on top level of loader.lua has no effect. Something is off here.

Also, I see my changes in loader.lua that's mounted to /tmp with appimage when neovim is opened.

@justinmk
Copy link
Member

Just a guess, but confirm that enable=true here

if enable then

else you are hitting the "standard" path in that if-else, which does not use the custom loader.

@shmerl
Copy link
Contributor Author

shmerl commented Sep 15, 2025

I added something like this in runtime/lua/vim/loader.lua:

function M.enable(enable)
  error("enable = " .. enable)
...

But I don't see any exception thrown. I don't get it, is this module even used at all?

@nenahp
Copy link

nenahp commented Sep 15, 2025

i would guess u need to use make to compile as loader.lua may be preload as bytecode

@shmerl
Copy link
Contributor Author

shmerl commented Sep 15, 2025

loader.lua may be preload as bytecode

Where would it come from though? I don't see any luac files in the appimage tree.

@justinmk
Copy link
Member

how are you starting nvim? try --luamod-dev and make sure you point to the VIMRUNTIME that has your changes:

VIMRUNTIME=$HOME/dev/neovim/runtime/ nvim --luamod-dev

@shmerl
Copy link
Contributor Author

shmerl commented Sep 15, 2025

I was actually starting re-packed appimage since that should trigger the env variable check. I can try using the above method.

@shmerl
Copy link
Contributor Author

shmerl commented Sep 21, 2025

@justinmk: Just got to testing it using your method with providing the modified runtime with the fix. It works!

I.e. I unpacked the appimage to $HOME/tmp/neovim_debug, applied the change to loader.lua and tried this (using same appimage nvim binary I unpacked things from):

VIMRUNTIME=$HOME/tmp/neovim_debug/squashfs-root/usr/share/nvim/runtime nvim --luamod-dev

Now those previously problematic files are created without the bad prefix like this:

%2fusr%2fshare%2fnvim%2fruntime%2ffiletype.luac

So I can confirm that the fix helps.

@justinmk
Copy link
Member

justinmk commented Sep 22, 2025

Nice! Did you try moving the logic to normalize() instead of cache_filename ? That seems more complete.

To write a test for this, just add a new it() case to test/functional/lua/loader_spec.lua and (try AI for this):

  1. in the clear{env={...}} have it set the APPIMAGE env var, and set VIMRUNTIME to something like ./Xtest/tmpbogus/usr/...
  2. it should require() some module that lives in the test directory ./Xtest/tmpbogus/usr/nvim/runtime/lua/foo.lua
  3. have it call the loader._profile function:
    function M._profile(opts)
  4. have it call loader._inspect():
    function M._inspect(opts)
    • it looks like the stats don't mention filenames, but that can be easily changed. That makes it easy to check the cached filenames.
  5. assert that the cached name is /usr/nvim/runtime/lua/foo.lua

@shmerl
Copy link
Contributor Author

shmerl commented Sep 22, 2025

That's a bit over my head now, I'll have to take a closer look how to make a new test. Do you suggest to add it to some existing unit test file or make a new one?

@shmerl
Copy link
Contributor Author

shmerl commented Sep 22, 2025

On doing it in normalize, it does work. I tried this:

local function normalize(path)
  path = fs.normalize(path, { expand_env = false, _fast = true })

  if os.getenv('APPIMAGE') ~= nil then
    -- Avoid cache pollution caused by AppImage randomizing the program root. #31165
    -- "/tmp/.mount_nvimAmpHPH/usr/share/nvim/runtime" => "/usr/share/nvim/runtime"
    path = path:match("(/usr/.*)") or path
  end

  return path
end

@justinmk
Copy link
Member

That's a bit over my head now,

try an AI to get something started. You can run the test like this:

TEST_FILE=test/functional/lua/loader_spec.lua make functionaltest

I'll have to take a closer look how to make a new test. Do you suggest to add it to some existing unit test file or make a new one?

test/functional/lua/loader_spec.lua

@shmerl
Copy link
Contributor Author

shmerl commented Sep 22, 2025

I'll look into the test a bit later.

@justinmk
Copy link
Member

justinmk commented Dec 9, 2025

rebase?

@shmerl
Copy link
Contributor Author

shmerl commented Dec 9, 2025

I'll refersh it soon, but I didn't have a chance to look into unit tests. I'll have some time for that later in the month.

@justinmk
Copy link
Member

We can just merge this for now. Meanwhile, the value of vim.loader is in question: #36905

@justinmk justinmk merged commit 78bbe53 into neovim:master Dec 10, 2025
35 checks passed
@neovim-backports
Copy link

Successfully created backport PR for release-0.11:

github-actions bot pushed a commit that referenced this pull request Dec 10, 2025
Problem:
When using the Nvim appimage, `~/.cache/nvim/luac` directory can grow to
250,000+ files.

Example of 2 identical files in `./luac/`:

    %2ftmp%2f.mount_nvim.a65Rja0%2fusr%2fshare%2fnvim%2fruntime%2flua%2fvim%2ftreesitter.luac
    %2ftmp%2f.mount_nvim.aNpxXgo%2fusr%2fshare%2fnvim%2fruntime%2flua%2fvim%2ftreesitter.luac

Analysis:
The `nvim.appimage` mounts nvim at a different temporary path each time
it is invoked. The naming scheme of these cache files is random, which
defats the purpose of the cache creates N new files on every launch of
nvim.

Steps to reproduce:
1. install `nvim.appimage`
2. `mv ~/.cache/nvim/luac ~/.cache/nvim/luac.backup`
3. `nvim`
4. Observe contents of `~/.cache/nvim/luac/`
5. Close nvim and run `nvim` again
6. Observe contents of `~/.cache/nvim/luac/` and see that new identical
   files have been added with a different mount prefix

Solution:
When running from an appimage, trim the random part of the filepaths.

(cherry picked from commit 78bbe53)
github-actions bot pushed a commit that referenced this pull request Dec 10, 2025
Problem:
When using the Nvim appimage, `~/.cache/nvim/luac` directory can grow to
250,000+ files.

Example of 2 identical files in `./luac/`:

    %2ftmp%2f.mount_nvim.a65Rja0%2fusr%2fshare%2fnvim%2fruntime%2flua%2fvim%2ftreesitter.luac
    %2ftmp%2f.mount_nvim.aNpxXgo%2fusr%2fshare%2fnvim%2fruntime%2flua%2fvim%2ftreesitter.luac

Analysis:
The `nvim.appimage` mounts nvim at a different temporary path each time
it is invoked. The naming scheme of these cache files is random, which
defats the purpose of the cache creates N new files on every launch of
nvim.

Steps to reproduce:
1. install `nvim.appimage`
2. `mv ~/.cache/nvim/luac ~/.cache/nvim/luac.backup`
3. `nvim`
4. Observe contents of `~/.cache/nvim/luac/`
5. Close nvim and run `nvim` again
6. Observe contents of `~/.cache/nvim/luac/` and see that new identical
   files have been added with a different mount prefix

Solution:
When running from an appimage, trim the random part of the filepaths.

(cherry picked from commit 78bbe53)
shmerl added a commit to shmerl/neovim that referenced this pull request Dec 14, 2025
justinmk added a commit to justinmk/neovim that referenced this pull request Jan 26, 2026
Following is a list of fix/feature commits in this release.
See `:help news` in Nvim for release notes.

FEATURES
--------------------------------------------------------------------------------
- b92e92b lsp: support auto-force escalation in client stop neovim#36430
- 808d973 lsp: warn about unknown filetype neovim#36910

FIXES
--------------------------------------------------------------------------------
- 5e7af0b :ls: check for finished terminal properly (neovim#37303)
- 6ce7b9b api: autocmds mess up nvim_get_option_value's dummy buffer
- c124594 api: buffer overflow in nvim_buf_get_extmarks overlap neovim#37184
- 7f51431 api: crash when moving curwin to other tabpage neovim#35679
- 7896fe2 api: do not allow opening float to closing buffer
- 91ebbc6 api: ignore split_disallowed when opening a float
- 10a1df2 api: nvim_get_option_value dummy buffer crashes
- da825e5 api: on_bytes gets stale data on :substitute neovim#36487
- a9ffdca api: open_win leak from naughty autocommands
- 92849ea api: parse_expression crash with ident and curly
- 1db945b api: parse_expression crash with unopened ] and node
- 79b67ce appimage: wrong $ARCH used by linuxdeploy neovim#36712
- 46011a4 autocmd: fire TabClosed after freeing tab page
- a512d43 autocmd: heap UAF with :bwipe in Syntax autocmd
- bea500d autocmd: parsing of comma-separated buflocal patterns
- 648cf4e autocmd: skip empty comma-separated patterns properly
- fa24e04 buffer: defer w_buffer clearing to prevent dict watcher crash neovim#36748
- 9fb49aa buffer: don't allow changedtick watcher to delete buffer (neovim#36764)
- cae3c83 buffer: don't reuse 1-line terminal buffer (neovim#37261)
- 6f84ea7 buffer: switching buffer should respect jumpoptions+=view (neovim#36969)
- 16ca7ce build: disable problematic marktree assert in RelWithDebInfo builds
- c469cba channel: unreference list after callback finishes (neovim#37358)
- 43f5297 clipboard: tmux clipboard data may be stale neovim#36787
- 0358f37 clipboard: use tmux only in a tmux session (neovim#36603)
- 7e99466 eval: 0 should mean current tabpage in gettabvar() (neovim#36891)
- 656ff4c events: crash on WinScrolled neovim#35995
- 63c5a10 install: only install "tee" on Windows neovim#36629
- e8c21a8 langmap: assert failure on mapping to char >= 256 (neovim#37291)
- 890c257 lsp: check `nvim.lsp.enable` before `doautoall` neovim#36518
- 275c769 lua: don't remove first char of non-file stacktrace source (neovim#37008)
- 83c589d lua: relax `vim.wait()` timeout validation (neovim#36907)
- bd2317f lua: separate vim.{g,b,w,t} types neovim#37081
- f21c169 lua: vim._with() doesn't save boolean options properly (neovim#37354)
- 9acbf51 lua: vim.wait(math.huge) fails neovim#36885
- 5143419 man.lua: :Man slow/hangs if MANPAGER is set neovim#36689
- df9452e man.lua: show_toc condition may cause infinite loop neovim#36979
- 124c182 marks: wrong line('w$', win) with conceal_lines (neovim#37047)
- 6ef1b65 normal: assertion failure with "gk" in narrow window (neovim#37444)
- 5ca2eb5 remote: remote-ui connect timeout on slow networks neovim#36800
- 2a3cd8d rpc: don't overwrite already received results on error (neovim#37339)
- e0fdfd3 scripts: release.sh
- ba600c4 session: window sizes not stored with float windows (neovim#37344)
- 53090ab statusline: scope truncation bookkeeping
- 800118e terminal: :edit should respect 'bufhidden' with exited job (neovim#37301)
- 4b41c28 terminal: <Ignore> should be no-op (neovim#37494)
- 074d342 terminal: avoid multiple terminals writing to same buffer (neovim#37219)
- 2cc7873 terminal: crash when TermClose deletes other buffers
- ea87192 terminal: crash when TermClose switches back to terminal buffer
- ceed171 terminal: crash with race between buffer close and OSC 2 (neovim#37225)
- acc46e1 terminal: handle closing terminal with pending TermRequest (neovim#37227)
- bb31e7b terminal: inconsistent mode change when switching buffer (neovim#37215)
- 40c974e terminal: restore options properly when switching buffer (neovim#37485)
- 46f569a treesitter: use metadata in :EditQuery captures neovim#37116
- 076f799 trust: :trust command on Windows neovim#36509
- d997c8e tutor: escape tutor filename neovim#36539
- fcd0517 ui.open: use "start" instead of deprecated "rundll32" neovim#36731
- 6a507ba vim.fs: abspath(".") returns "/…/." (neovim#36584)
- d974c68 vim.fs: root() should always return absolute path neovim#36466
- 91fd4d1 vim.loader: randomized AppImage path pollutes luac cache neovim#35636
- 45cda1b vim.loader: randomized AppImage path pollutes luac cache neovim#36944
- d9631c7 window: crash closing only non-float if autocmds :tabonly (neovim#37218)
- f7e2554 window: crash closing split if autocmds close other splits (neovim#37233)
- 7a9bced window: disallow closing autocmd window in other tabpage
- 88619e1 window: handle closing the only non-float in other tabpage
- d38ba7e window: restore b_nwindows if win_close_othertab keeps window
- 6338d2d window: win_move_after UAF from naughty autocmds (neovim#37065)
- fac7c10 windows: set manifest resource ID to 1 in nvim.rc for MinGW (neovim#36611)

BUILD
--------------------------------------------------------------------------------
- d0ed06d haiku os support neovim#36639
- a94647b build(windows): restore "tee" on Windows neovim#36627
- 1f93acc build(windows): vendor xxd.c (neovim#36755)

REVERTED CHANGES
--------------------------------------------------------------------------------
- ae25f69 fix: vim.lsp.omnifunc should not throw away other items

VIM PATCHES
--------------------------------------------------------------------------------
- b3eab00 229f79c: runtime(yaml): fix wrong order of undo_ftplugin suboptions
- 89f8e97 3a324c8: runtime(doc): Fix typo in syntax.txt (neovim#37522)
- d1cd79a 64799a5: runtime(doc): clarify the behavior of CTRL-Z
- 0978d83 7bc9880: runtime(make): do not automatically indent after a special target
- 781da75 8.1.0753: printf format not checked for semsg() (neovim#37248)
- 44eae48 9.1.0893: No test that undofile format does not regress (neovim#37193)
- 9a50420 9.1.1872: Cmdline history not updated when mapping <Up> and <CR> (neovim#36334)
- d1604e0 9.1.1969: Wrong cursor position after formatting with long 'formatprg' (neovim#36918)
- fda8d2c 9.1.2028: [security]: Buffer-overflow with incomplete multi-byte chars (neovim#37133)
- f96e401 9.1.2055: Division by zero in :file after failing to wipe buffer (neovim#37268)
- f8961c3 9.1.2058: b_locked_split is not checked for :sbuffer
- 9f2b991 9.1.2066: :wqall! doesn't close a terminal like :qall! does (neovim#37314)
- b1fa8f1 9.1.2068: :bd/bw may try to switch to a closing buffer
- 600d9f3 9.1.2086: Memory leak when skipping invalid literal dict
- 0cc15be 9.1.2087: Crash when using :tabonly in BufUnload
- d052d22 9.1.2090: Last buffer not freed with EXITFREE
- 537e8d6 9.1.2095: :wqall! doesn't quit when using :quit in BufWritePost
- 0b1f5a1 9.1.2105: tests: not enough tests for using plain_vgetc() (neovim#37521)
- 0da1e4b 9.1.2107: :normal may change cmdline history (neovim#37523)
- a66fce6 98a0cbf: patch 9.1.1971: crash with invalid positional argument 0 in printf() (neovim#36919)
- 85404d1 eb732ed: runtime(doc): Wrap overlength lines in uganda.txt (neovim#36550)
- a93b5a7 fe8c8b1: runtime(doc): fix outdated :function help
- 0706c55 partial:9.1.1955: sort() does not handle large numbers correctly (neovim#36840)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

vim.loader: .cache/nvim/luac/ contains 250k files when using AppImage

3 participants