Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions build_release_archive.py

This file was deleted.

4 changes: 3 additions & 1 deletion include/utilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,9 @@ std::vector<unsigned char> from_base64_to_vector(std::string_view x);
// Concept to match containers with a size() method
template <typename T>
concept HasSize = requires(T t) {
{ t.size() } -> std::convertible_to<size_t>;
{
t.size()
} -> std::convertible_to<size_t>;
};

template <HasSize T>
Expand Down
4 changes: 2 additions & 2 deletions prepare_release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ read_char() {
rm -f ./libsession_util_nodejs*.tar.gz
python -m venv .venv
. .venv/bin/activate
pip install git-archive-all
pip install -r requirements.txt --require-hashes

PACKAGE_VERSION=$(node -p "require('./package.json').version")
GIT_COMMIT=$(git rev-parse HEAD)
Expand Down Expand Up @@ -42,7 +42,7 @@ esac
echo "Continuing..."

echo "Building tar archive of source..."
python3 build_release_archive.py libsession_util_nodejs-v$PACKAGE_VERSION.tar.gz --include src/version.h
git-archive-all libsession_util_nodejs-v$PACKAGE_VERSION.tar.gz --include src/version.h

echo "tar archive size:"
du -sh libsession_util_nodejs*.tar.gz
Expand Down
10 changes: 10 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Release-tooling Python dependencies, version- and hash-pinned so a
# compromised PyPI account / dependency-confusion upload can't inject a
# different artifact at release-tarball time. Installed via:
# pip install -r requirements.txt --require-hashes
# prepare_release.sh then invokes the `git-archive-all` console script
# from the venv directly, so a repo-root git_archive_all.py can't shadow
# this pinned wheel (the CWD is never on sys.path for a console script).
git-archive-all==1.23.1 \
--hash=sha256:d9f9611f2df629de3df1d6f134955ced4c704cbc0e423d769a7c0e55a8484242 \
--hash=sha256:a42fe0cedd2e0361250a4f3130f3d3543f640d4f1fe28530771df5972108729f
87 changes: 66 additions & 21 deletions src/addon.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <napi.h>

#include <mutex>
#include <oxen/log.hpp>

#include "blinding/blinding.hpp"
Expand All @@ -13,30 +14,74 @@
#include "user_groups_config.hpp"

Napi::ThreadSafeFunction tsfn;
// Guards `tsfn` against the race between libsession's background log
// threads (which read it and BlockingCall through it) and N-API env
// teardown (which Releases and nulls it). The check + call in the
// logger and the check + release in the cleanup hook must each be
// atomic w.r.t. the other, or we risk a torn read / use-after-free on
// the wrapper.
std::mutex tsfn_mutex;

Napi::Object InitAll(Napi::Env env, Napi::Object exports) {

tsfn = Napi::ThreadSafeFunction::New(
env,
Napi::Function::New(env, [](const Napi::CallbackInfo& info) {}),
"LoggerCallback",
0,
1);

session::add_logger([](std::string_view msg) {
tsfn.BlockingCall(
new std::string(msg),
[](Napi::Env env, Napi::Function jsCallback, std::string* msg) {
Napi::HandleScope scope(env);
Napi::Function consoleLog = env.Global()
.Get("console")
.As<Napi::Object>()
.Get("log")
.As<Napi::Function>();
Napi::String jsStr = Napi::String::New(env, "libsession: " + *msg);
consoleLog.Call({jsStr});
delete msg;
});
{
std::lock_guard<std::mutex> lock(tsfn_mutex);
tsfn = Napi::ThreadSafeFunction::New(
env,
Napi::Function::New(env, [](const Napi::CallbackInfo& info) {}),
"LoggerCallback",
0,
1);
// The logger callback is fire-and-forget. Without Unref(), the
// TSFN keeps a strong ref on the loop and a `require()` from a
// short-lived CLI hangs forever waiting on the TSFN.
tsfn.Unref(env);
}

// Release the TSFN when the N-API env tears down. Without this, a
// later libsession log from a background thread could BlockingCall
// into a destroyed env (abort / UAF). Taking the lock makes the
// check + release atomic w.r.t. the logger callback below.
env.AddCleanupHook([]() {
std::lock_guard<std::mutex> lock(tsfn_mutex);
if (tsfn) {
tsfn.Release();
tsfn = nullptr;
}
});

// Register the libsession -> console.log bridge exactly once per
// process. Re-running InitAll (multiple Workers / vm contexts) must
// not stack duplicate callbacks onto libsession's global logger
// registry — the single persistent callback always reads whatever
// the current `tsfn` is, under the lock.
static std::once_flag logger_once;
std::call_once(logger_once, [] {
session::add_logger([](std::string_view msg) {
// Hold the lock across the check + BlockingCall so env
// teardown can't Release/null `tsfn` underneath us.
std::lock_guard<std::mutex> lock(tsfn_mutex);
if (!tsfn)
return;
auto* payload = new std::string(msg);
napi_status status = tsfn.BlockingCall(
payload, [](Napi::Env env, Napi::Function jsCallback, std::string* msg) {
Napi::HandleScope scope(env);
Napi::Function consoleLog = env.Global()
.Get("console")
.As<Napi::Object>()
.Get("log")
.As<Napi::Function>();
Napi::String jsStr = Napi::String::New(env, "libsession: " + *msg);
consoleLog.Call({jsStr});
delete msg;
});
// On a rejected call (e.g. napi_closing if a Release slipped
// in) the finalize callback never runs, so the payload would
// leak — free it here instead.
if (status != napi_ok)
delete payload;
});
});
oxen::log::set_level_default(oxen::log::Level::info);

Expand Down
Loading