Skip to content

[Bug] Mutex leak in fs_write_finished() on retriable errors #9

@hanqing2025

Description

@hanqing2025

Describe the Bug

A mutex leak in fs_write_finished() causes file-specific deadlock when the server returns retriable errors. The function acquires a file mutex but returns without releasing it when encountering error codes 1007 or 5003, bypassing the normal unlock path.

Location: pfs.c (lines 2689-2722)

static void fs_write_finished(psync_socket *api, void *_wt)
{
    // ... error handling ...

    pthread_mutex_lock(&of->mutex);  // File mutex acquired (line 2689)

    if (!res) {
        debug(D_WARNING, "failed! setting error to %d of file %s", NOT_CONNECTED_ERR, of->file->name);
        of->error = NOT_CONNECTED_ERR;
        if (of->waitcmd)
            pthread_cond_broadcast(&of->cond);
        goto decref;  // Goes to unlock
    }

    sub = find_res(res, "result");
    if (!sub || sub->type != PARAM_NUM) {
        debug(D_BUG, "EIO");
        of->error = -EIO;
        if (of->waitcmd)
            pthread_cond_broadcast(&of->cond);
    }
    else if (sub->num != 0) {
        if ((sub->num == 1007 || sub->num == 5003) && wt->tries < fs_settings.retrycnt) {
            reset_conn_id(of);
            return reschedule_write(wt);  // BUG: Returns without unlock! (line 2707)
        }
        debug(D_ERROR, "error %u", (int unsigned)sub->num);
        of->error = convert_error(sub->num);
        if (of->waitcmd)
            pthread_cond_broadcast(&of->cond);
    }

decref:  // This label is bypassed on line 2707
    dec_openfile_refcnt_locked(of);
    pthread_mutex_unlock(&of->mutex);  // Never reached on error 1007/5003 (line 2722)
    free(_wt);
}

Impact:

  • File's of->mutex remains permanently locked after encountering retriable errors
  • All subsequent operations on that specific file (read, write, close) deadlock permanently
  • User cannot access or close the affected file → Denial of Service for that file
  • Multiple files can be affected if error occurs repeatedly

Execution Flow:

Thread 1 (write operation on file A):
  → pthread_mutex_lock(&of->mutex)        // Lock file A's mutex
  → Server returns error 1007 or 5003     // Retriable error
  → if ((sub->num == 1007 || sub->num == 5003) && ...)
  → reset_conn_id(of)
  → return reschedule_write(wt)           // EXIT - bypasses decref label
  → of->mutex (file A) remains locked forever

Thread 2 (any operation on file A):
  → Attempt to read/write/close file A
  → pthread_mutex_lock(&of->mutex)        // BLOCKS permanently
  → File A becomes permanently inaccessible

Other files:
  → Operations on other files continue normally
  → But if errors 1007/5003 occur, those files also become locked

This occurs when the pCloud server returns error codes:

  • 1007: Typically connection/retry errors
  • 5003: Typically service unavailable errors

And the retry count has not been exhausted. The function attempts to reschedule the write but forgets to unlock the file mutex.


I would appreciate it if you could review and confirm this potential issue. Thank you for your time and for maintaining this project!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions