prpc.c:
- on_request: fix total_size computation. The original formula
sizeof(uint32_t)+sizeof(uint64_t)+response->length had two bugs:
(1) sizeof(uint32_t)+sizeof(uint64_t)=12 but offsetof(rpc_message_t,value)=16
due to struct alignment padding between the uint32_t type and uint64_t length
fields; and (2) respond() was storing full message size in response->length,
double-counting the header. Fix: use offsetof(rpc_message_t,value)+response->length,
consistent with readResponse() in rpcclient.cpp which reads a fixed header_size
of offsetof(rpc_message_t,value) bytes then reads msg->length payload bytes.
- respond: store payload length only in response->length (value_length+1),
not full message size, to match the client protocol expectation.
- prpc_init: add null check on malloc return value.
- prpc_register: restore old handler table and return -1 if prpc_init fails.
papi.c:
- papi_result_thread: add missing MAX_API_RESPONSE_SIZE guard (present in
papi_result but absent here), preventing server-controlled unbounded malloc.
- papi_result, papi_result_thread: add null checks on malloc before passing
pointer to psock_readall.
- papi_result_async: add MAX_API_RESPONSE_SIZE check and malloc null check
on reader->respsize path.
- calc_ret_len: add _NEED_DATA(1) guard before ARRAY and HASH while-loop
conditions; empty containers with datalen=0 caused out-of-bounds read.
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
status_change_thread reads psync_status.filestodownload, filestoupload,
localisfull, and remoteisfull under statusmutex, while pstatus_download_recalc,
pstatus_upload_recalc, and pstatus_set write them without statusmutex (or under
a different mutex). TSan reports the race at pqevent.c:274.
Replace all writes of these four fields with __atomic_store_n and capture
atomic snapshots in status_change_thread before the condition, using the
snapshots for both comparison and the status_old update after struct copy.
Also use atomic loads in calc_status() for filestodownload and filestoupload.
Closes#335
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
psync_status.status is written in pstatus_set() (pstatus.c:263) after
releasing status_internal_mutex, while status_change() (pclsync_lib.cpp:435)
reads it concurrently from the callback thread without any lock. TSan
reports the race between T9 (write in pstatus_set) and the status callback
thread (read in status_change).
Replace all reads and writes of psync_status.status with
__atomic_load_n/__atomic_store_n (__ATOMIC_RELAXED) across pstatus.c,
pqevent.c, and pclsync_lib.cpp. In status_change_thread, capture the
atomic value once into cur_status before the condition to avoid multiple
inconsistent loads. In status_change(), capture cur_status at entry and
use it throughout, also propagating it to the copied status_ struct.
Closes#334
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
psync_current_time is a global time_t written by timer_thread without
holding timer_mutex, while ptimer_register reads it under timer_mutex.
TSan reports the race at ptimer.c:152 (write) and ptimer.c:216 (read).
Replace all reads with __atomic_load_n and all writes with
__atomic_store_n using __ATOMIC_RELAXED. This covers the race sites in
ptimer.c and all external readers in plocalscan.c, pnetlibs.c, and
psynclib.c that access psync_current_time without any mutex.
Closes#333
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Acquire mutex before reading tm->refcnt to prevent race with
psync_task_dec_refcnt(). Fixes ThreadSanitizer warning.
Fixes#332
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
In psync_unlink(), putil_wipe(psync_my_pass, sizeof(psync_my_pass))
only wiped 8 bytes (pointer size) instead of the full password string.
Changed to strlen(psync_my_pass) and added NULL check.
Fixes pcl-zqv.9.1 (partial)
GH #276
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
strcmp() at line 315 short-circuits on first differing byte, leaking
timing info about trusted fingerprints.
Replace with constant-time comparison using memcmp and bitwise OR
accumulation across all trusted entries.
Fixes GH #239
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
reader->data = malloc(reader->respsize) at line 462 is not checked
for NULL before goto again continues the loop and dereferences it.
Add NULL check and return ASYNC_RES_READY with result=NULL on failure.
Fixes GH #238
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
cbidx = request->type - 20 computed before checking lower bound.
If request->type < calbacks_lower_band, cbidx wraps to large value
causing out-of-bounds array access.
Move bounds check before subtraction and use calbacks_lower_band
instead of hardcoded 20.
Fixes GH #235
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
msgErr was declared as char[1024] but cast to char** and passed to
ptools_backend_call(). The function writes a char* into *err,
corrupting the first sizeof(char*) bytes of the array.
Change to char *errPtr = NULL and free it after use.
Fixes GH #216
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
Loop variable j was declared inside the loop and reset to 0 on every
iteration. All branches had early continue, so j++ never executed.
Change j to i - reqParCnt to correctly index into optionalParams array.
Also fix paramtype check to use j instead of i.
Fixes GH #215
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
When eventid has PEVENT_TYPE_FOLDER set, the code allocated
psync_file_event_t size but cast to psync_folder_event_t.
Swap the sizeof() calls to match the actual struct types used.
Fixes GH #229
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
Three functions had a consistent typo (missing 'i' in builder):
- psync_list_bulder_push_num
- psync_list_bulder_pop_num
- psync_list_bulder_add_element
Renamed to correct spelling in plist.h, plist.c, and all callers.
Fixes GH #219
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
excepions is read without holding timer_ex_mutex, but
ptimer_exception_handler() modifies it under the mutex.
Move the read inside the critical section to prevent the race.
Fixes GH #227
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
pthread_create() return value is ignored. If it fails, the allocated
thread_data is leaked.
Check return value; on error, log and free the data.
Fixes GH #221
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
keyParams is only allocated when pCnt > 0 but freed unconditionally.
When pCnt == 0, free() is called on an uninitialized pointer (UB).
Initialize to NULL so free() is a no-op when allocation is skipped.
Fixes GH #214
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
mmap(2) returns MAP_FAILED ((void*)-1) on error, not NULL. The check
'if (likely(ret))' treats MAP_FAILED as truthy, so the emergency retry
path is never reached and callers receive MAP_FAILED as a valid pointer.
Normalize pmem_mmap() to return NULL on failure (converting MAP_FAILED
to NULL in the mmap path). This confines the platform-specific error
handling to one place and eliminates the need for preprocessor checks
in pmem_mmap_safe() and psync_mmap_anon_emergency().
Fixes GH #226
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
- Wrap std::stoull() in try/catch to prevent daemon crash on invalid folder ID
- Add proper read loop for RPC messages to handle partial reads
- Validate msg->length before memcpy to prevent heap over-read
- Handle daemon bugs gracefully (EOF before full message)
- Fix operator precedence in sync remove command
Fixes#205, #206
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
- Add return statement in catch(...) block to prevent fallthrough
- Fix operator precedence in GetState() to capture Call() return value
Fixes#201, #202
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
- Add return statement in catch(...) block to prevent fallthrough
- Fix operator precedence in GetState() to capture Call() return value
Fixes#201, #202
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
* Fix debug build segfault: add strong overrides for psql_lock/unlock functions
The weak/strong symbol approach requires that psql.c undefs the debug
macros to define function implementations. This means functions in psql.c
call psql_lock()/psql_unlock() as functions, not macros.
In debug builds, these must be strong overrides that call the _do_
variants to properly maintain lockctr/rdlockctr. Without these overrides,
the weak stubs are used which don't update counters, causing assertion
failures in psql_unlock().
Added strong overrides in psql_debug.c for:
- psql_lock() -> psql_do_lock(__FILE__, __LINE__)
- psql_rdlock() -> psql_do_rdlock(__FILE__, __LINE__)
- psql_statement() (calls psql_do_lock directly)
Also made psql_statement, psql_start, and all query/prepare functions
weak in psql.c to allow debug overrides.
Fixes#138
* Fix umask and password memory wipe
- Set umask(0077) in daemonize() to prevent world-readable files
- Wipe password from memory after psync_set_user_pass()
Fixes#203, #204
---------
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
The weak/strong symbol approach requires that psql.c undefs the debug
macros to define function implementations. This means functions in psql.c
call psql_lock()/psql_unlock() as functions, not macros.
In debug builds, these must be strong overrides that call the _do_
variants to properly maintain lockctr/rdlockctr. Without these overrides,
the weak stubs are used which don't update counters, causing assertion
failures in psql_unlock().
Added strong overrides in psql_debug.c for:
- psql_lock() -> psql_do_lock(__FILE__, __LINE__)
- psql_rdlock() -> psql_do_rdlock(__FILE__, __LINE__)
- psql_statement() (calls psql_do_lock directly)
Also made psql_statement, psql_start, and all query/prepare functions
weak in psql.c to allow debug overrides.
Fixes#138
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
* Fix#103: Refactor putil to use putil_ namespace and enforce encapsulation
All public functions in putil now use the putil_ prefix (e.g., putil_strdup,
putil_strcat, putil_time_format, putil_base64_encode, etc.). Internal
variables (normalize_table, base64_reverse_table) and the constructor remain
static. Updated all 34 call-site files across pclsync accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix#96: Refactor pfs to use pfs_ namespace and enforce encapsulation
All public functions in the pfs module renamed from psync_fs_ to pfs_
(e.g., pfs_update_openfile, pfs_refresh, pfs_start, pfs_stop, etc.).
Extern vars psync_fake_prefix/psync_fake_prefix_len renamed to
pfs_fake_prefix/pfs_fake_prefix_len. Functions only used within pfs.c
(pfs_update_openfile_fileid_locked, pfs_chown) made static. Updated all
call sites across 17 files including pclsync_lib.cpp.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Refactor pfsxattr to use pfs_xattr_ namespace
All psync_fs_ prefixed functions in the pfsxattr module renamed to
pfs_xattr_ (e.g., pfs_xattr_set, pfs_xattr_get, pfs_xattr_list,
pfs_xattr_remove, pfs_xattr_file_deleted, pfs_xattr_task_to_file, etc.).
Local macro psync_fs_set_thread_name renamed to pfs_xattr_set_thread_name.
Internal static helpers (delete_object_id, update_object_id,
xattr_get_object_id_locked) remain static and unchanged.
Updated all call sites in pfs.c, pfsupload.c, and pdiff.c.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Rename pfs_xattr_ namespace to pfs_xatr_
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Refactor pfs sub-modules to use namespaced function prefixes
- pfscrypto_* → pfs_crpt_*
- psync_fsstatic_* → pfs_stat_*
- psync_fsupload_* → pfs_upld_*
- psync_fstask_* → pfs_task_* (functions only; typedefs unchanged)
- psync_fsfolder_*/psync_fsfolderid_*/psync_fsfolderflags_*/
get_decname_for_folder/psync_get_folderid → pfs_fldr_*
Type names, macro constants, and typedef aliases are unchanged.
Updated all call sites across 17 files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix#90: Support 2FA when running as a daemon
In daemon mode, when PSTATUS_TFA_REQUIRED fires:
- Automatically send an SMS 2FA code via psync_tfa_send_sms()
- Log delivery details to syslog with instructions to use
`echo 'tfa CODE' | pcloudcc -k`
- Block on a condition variable until the code arrives via RPC
Add a new `tfa <code>` command to the pcloudcc -k control interface.
This sends the code to the daemon over the existing Unix socket RPC
channel (SENDTFA message type).
Bad codes (PSTATUS_BAD_TFA_CODE) are handled: the daemon logs a syslog
warning and waits for a corrected code without re-sending SMS.
Devices are trusted by default (trusted=1 in psync_tfa_set_code) so
repeated 2FA prompts are avoided for the configured trust period.
TFA codes are wiped from memory after use.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add auth command: supply password to running daemon
Mirrors the tfa command pattern. When PSTATUS_LOGIN_REQUIRED fires in
daemon mode, read_password() now logs a syslog notice and blocks on a
condition variable rather than calling exit(1).
The new `auth <password>` control command (SENDAUTH RPC type) signals
the condvar and unblocks the daemon. Usage:
echo 'auth PASSWORD' | pcloudcc -k
The password is wiped from the CLI-side buffer immediately after the
RPC call. putil_wipe() is used for both the tfa and auth transient
strings on the sending side.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add status command: show current sync state from CLI
Adds a `status` (alias `st`) command to the interactive CLI and to the
pcloudcc -k control interface. The daemon-side handler calls
psync_get_status(), formats the status name plus download/upload
strings, and writes the result to shared memory. The client reads and
prints it.
Example output:
Status: READY
Download: idle
Upload: idle
Also works non-interactively:
echo 'status' | pcloudcc -k
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix#67: Add sync pause and sync resume commands
Wire psync_pause() and psync_resume() from the C library into the RPC
command layer, registering SYNCPAUSE and SYNCRESUME handlers and exposing
them as 'sync pause' and 'sync resume' CLI subcommands.
Note: daemon quit is already handled by the existing 'finalize' command,
which calls psync_destroy() and exits the daemon process cleanly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Remove obsolete Pending Transfers section from README
The pending command now provides a proper way to check for pending
transfers, making the manual cache-inspection workaround obsolete.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Wire psync_pause() and psync_resume() from the C library into the RPC
command layer, registering SYNCPAUSE and SYNCRESUME handlers and exposing
them as 'sync pause' and 'sync resume' CLI subcommands.
Note: daemon quit is already handled by the existing 'finalize' command,
which calls psync_destroy() and exits the daemon process cleanly.
Co-authored-by: Levi Neely <lkn@darkstar.example.net>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>