Skip to content

fix(addon): unref TSFN, env cleanup hook + sys.path strip#60

Open
klodr wants to merge 2 commits into
session-foundation:mainfrom
klodr:fix/addon-tsfn-and-release-syspath
Open

fix(addon): unref TSFN, env cleanup hook + sys.path strip#60
klodr wants to merge 2 commits into
session-foundation:mainfrom
klodr:fix/addon-tsfn-and-release-syspath

Conversation

@klodr
Copy link
Copy Markdown

@klodr klodr commented May 18, 2026

Two unrelated fixes bundled (small surface):

  1. src/addon.cpp: tsfn.Unref(env) + napi_add_env_cleanup_hook so a CLI-style require() doesn't hang on the TSFN's strong ref, and a later libsession log from a background thread doesn't BlockingCall into a destroyed env (abort / UAF). The logger callback already drops messages when the TSFN has been released.

  2. build_release_archive.py: strip sys.path[0] (the script's own directory) before import git_archive_all, so a checked-in git_archive_all.py (or directory) at the repo root can't shadow the hash-pinned wheel and execute arbitrary code at release-tarball time.

Test plan

  • pnpm install from a freshly-cloned repo works
  • A short-lived CLI that require()s this binding exits cleanly (no hang)
  • prepare_release.sh builds the tarball as before

klodr added 2 commits May 17, 2026 17:48
The logger ThreadSafeFunction was created with strong-ref + count=1
and never Unref()'d or Release()'d. Two observable consequences:

1. `node -e "require('./')"` hangs forever after the require resolves
   because the TSFN holds a ref on the loop with no pending work.
   `tsfn.Unref(env)` after creation lets short-lived CLIs / scripts
   exit cleanly while still allowing the BlockingCall to deliver
   while the loop is alive for other reasons.

2. On N-API env teardown (Worker shutdown, vm context reload), a
   later libsession log emission from a background thread would
   BlockingCall into a destroyed env, which is UB and can abort or
   call into a stale handle. `napi_add_env_cleanup_hook` Releases
   the TSFN and nulls the global; the add_logger lambda guards with
   `if (!tsfn) return` so a race between Release and the log thread
   drops silently instead of aborting.

The single-env case (95% of consumers, including the default
require flow) is now safe. Multi-env scenarios (multiple Workers
loading the addon concurrently) still race on the process-global
handle — that needs a per-env refactor via napi_set_instance_data
and is left for a follow-up.

Signed-off-by: Claude Perrin <klodr@users.noreply.github.com>
`python3 build_release_archive.py` imports `git_archive_all` while
`sys.path[0]` is the repo root. A checked-in `git_archive_all.py`
(or directory) shadowing the hash-pinned wheel would execute at
release-tarball-creation time — nullifying the pin and giving
arbitrary code execution to anyone who can land a PR with such a
file. Drop the leading sys.path entry so the venv / site-packages
is the only source of truth for the import.

Signed-off-by: Claude Perrin <klodr@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant