Merge branch 'fixes'

Fixes recommended by maintainer.

1. Buffer Overflow in FUSE Options Parsing (pfs.c)
  - Fixed

2. Base64 Padding Bug (putil.c)
  - I believe the padding logic at lines 273-284 is not a bug - it's correct per RFC 4648 base64 specification. Padding is only added when length != 0
   (i.e., when there's a partial final block), and the correct number of "=" characters are added based on whether 1 or 2 bytes remain.

3. Memory Leaks in pdiff.c
  - Fixed

4. Race Condition in Log Rotation (pdbg.c)
  - I believe the code is actually already correct.

  - The code at line 20 already uses volatile sig_atomic_t:

  static volatile sig_atomic_t log_reopen_requested = 0;

  This is the correct and safe pattern for signal handler communication:

  1. sig_atomic_t - Guarantees atomic read/write operations (no partial updates)
  2. volatile - Prevents the compiler from optimizing away accesses

  The pattern is correct:
  - Signal handler (pdbg_reopen_log at line 178-180) only sets the flag - async-signal-safe
  - Main code checks/resets the flag inside pthread_mutex_lock() (lines 133-136, 231-234)
  - Actual log reopening (do_reopen_log) happens in the main thread, not signal context

  This is a well-established and safe pattern. The mutex ensures the check-and-reset is serialized with other logging, and sig_atomic_t
  ensures the signal-to-main-thread communication is atomic.

5. Hardcoded RSA Padding (pssl.c)
  - Fixed

6. Unchecked Exception (pclsync_lib.cpp)
  - Fixed

7. Missing Input Validation
  - Fixed

Issue #1: Double Logging and Memory Leak in `process_deletefile()`
  - Fixed duplicate log.
  - Note: There was no actual memory leak - the first path allocation at line 1731 was properly freed at line 1735 before the second
  allocation at line 1739.

Issue #2: FUSE Options Parsing Problems
  - Fixed
This commit is contained in:
Jonathan Senkerik 2026-02-18 22:08:15 -05:00
commit f445e80930
5 changed files with 41 additions and 14 deletions

View File

@ -163,6 +163,11 @@ int main(int argc, char **argv) {
if (vm.count("cache-size")) {
uint64_t cache_size_gb = vm["cache-size"].as<uint64_t>();
/* Validate cache size: minimum 1GB, maximum 1TB */
if (cache_size_gb < 1 || cache_size_gb > 1024) {
std::cerr << "error: cache-size must be between 1 and 1024 GB" << std::endl;
return 1;
}
uint64_t cache_size_bytes = cache_size_gb * 1024ULL * 1024ULL * 1024ULL;
char cache_size_str[32];
snprintf(cache_size_str, sizeof(cache_size_str), "%llu",
@ -171,7 +176,13 @@ int main(int argc, char **argv) {
}
if (vm.count("log-path")) {
setenv("PCLOUD_LOG_PATH", vm["log-path"].as<std::string>().c_str(), 1);
std::string log_path = vm["log-path"].as<std::string>();
/* Validate log path: must not be empty or start with /etc or /sys */
if (log_path.empty() || log_path.compare(0, 5, "/etc/") == 0 || log_path.compare(0, 5, "/sys/") == 0) {
std::cerr << "error: invalid log-path" << std::endl;
return 1;
}
setenv("PCLOUD_LOG_PATH", log_path.c_str(), 1);
}
if (vm.count("log-level")) {
@ -182,7 +193,13 @@ int main(int argc, char **argv) {
}
if (vm.count("fs-event-log")) {
setenv("PCLOUD_FS_EVENT_LOG", vm["fs-event-log"].as<std::string>().c_str(), 1);
std::string fs_event_log = vm["fs-event-log"].as<std::string>();
/* Validate fs-event-log path: must not be empty or start with /etc or /sys */
if (fs_event_log.empty() || fs_event_log.compare(0, 5, "/etc/") == 0 || fs_event_log.compare(0, 5, "/sys/") == 0) {
std::cerr << "error: invalid fs-event-log" << std::endl;
return 1;
}
setenv("PCLOUD_FS_EVENT_LOG", fs_event_log.c_str(), 1);
}
if (vm.count("fuse-opts")) {

View File

@ -1629,6 +1629,8 @@ static void process_modifyfile(const binresult *entry) {
path = pfolder_file_path(fileid, NULL);
ptask_lfile_rm(fileid, path);
free(path);
free(oldfilepath);
free(oldfilepath_rel);
needdownload = 1;
return;
}
@ -1728,7 +1730,6 @@ static void process_deletefile(const binresult *entry) {
pdownload_tasks_delete(fileid, 0, 1);
path = pfolder_file_path(fileid, NULL);
if (likely(path)) {
pdbg_logf(D_NOTICE, "remote file deleted %s", path);
ptask_lfile_rm(fileid, path);
free(path);
needdownload = 1;

View File

@ -3695,7 +3695,8 @@ static int psync_fs_do_start() {
if (fuse_opts_env && fuse_opts_env[0] != '\0') {
char *fuse_opts = strdup(fuse_opts_env);
if (fuse_opts) {
char *token = strtok(fuse_opts, ",");
char *saveptr = NULL;
char *token = strtok_r(fuse_opts, ",", &saveptr);
while (token) {
// Trim leading/trailing whitespace
while (*token == ' ' || *token == '\t') token++;
@ -3703,12 +3704,16 @@ static int psync_fs_do_start() {
while (end > token && (*end == ' ' || *end == '\t')) *end-- = '\0';
if (strlen(token) > 0) {
char opt_arg[256];
snprintf(opt_arg, sizeof(opt_arg), "-o%s", token);
fuse_opt_add_arg(&args, opt_arg);
pdbg_logf(D_NOTICE, "Adding FUSE option: %s", token);
if (strlen(token) > 250) {
pdbg_logf(D_WARNING, "FUSE option too long, skipping: %s", token);
} else {
char opt_arg[256];
snprintf(opt_arg, sizeof(opt_arg), "-o%s", token);
fuse_opt_add_arg(&args, opt_arg);
pdbg_logf(D_NOTICE, "Adding FUSE option: %s", token);
}
}
token = strtok(NULL, ",");
token = strtok_r(NULL, ",", &saveptr);
}
free(fuse_opts);
}

View File

@ -801,9 +801,9 @@ prsa_sign_sha256_hash(psync_rsa_privatekey_t rsa,
if (!ret)
return (psync_rsa_signature_t)(void *)PERROR_NO_MEMORY;
ret->datalen = rsalen;
/* Save current padding settings (assume PKCS_V21 with SHA1 as default) */
padding = MBEDTLS_RSA_PKCS_V21;
hash_id = MBEDTLS_MD_SHA1;
/* Save current padding settings using getter functions */
padding = mbedtls_rsa_get_padding_mode(rsa);
hash_id = mbedtls_rsa_get_md_alg(rsa);
mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256);
if (mbedtls_rsa_rsassa_pss_sign(rsa, rng_get, &rng,
MBEDTLS_MD_SHA256,

View File

@ -507,8 +507,12 @@ int clib::pclsync_lib::init() {
const char *cache_size_env = getenv("PCLOUD_CACHE_SIZE");
if (cache_size_env && cache_size_env[0] != '\0') {
uint64_t cache_size = std::stoull(cache_size_env);
psync_set_uint_setting("fscachesize", cache_size);
try {
uint64_t cache_size = std::stoull(cache_size_env);
psync_set_uint_setting("fscachesize", cache_size);
} catch (const std::exception &e) {
pdbg_logf(D_ERROR, "Invalid PCLOUD_CACHE_SIZE: %s", cache_size_env);
}
}
psync_start_sync(status_change, event_handler);