• js: don't probe a TLS session known to hold no buffered plaintext

    From Deucе@1:103/705 to GitLab note in main/sbbs on Wed Nov 11 11:29:46 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/ab31f9f1c04c35ede2c86d15f44b1a4836a092c9#note_10872

    [P2] A short successful pop can leave a pending TLS error

    Setting `tls_drained` whenever `copied < len` can suppress a pending TLS error indefinitely while the underlying TCP connection remains open and idle. Cryptlib can return `CRYPT_OK` with a short byte count after copying valid plaintext and then encountering a fatal error on the next record; it saves that error for the next `cryptPopData()` call. No plaintext remains, but the TLS session still needs another pop to report the error.

    I reproduced this against the available native cryptlib library with a local TLS 1.2 peer: send `hello`, then a corrupt TLS application-data record, and keep TCP open. The results were:

    ```
    first cryptPopData(..., 4096, ...): status=0, copied=5
    raw descriptor poll(POLLIN): 0
    next cryptPopData(..., 1, ...): status=-33, copied=0
    ```

    This assignment sets `tls_drained=true` after the first pop. Subsequent `js_socket_tls_readable()` calls therefore skip the pop that would expose the saved error, and descriptor polling cannot discover an error already consumed by cryptlib. Previously, the next probe exposed it and let the normal receive path handle it. An event-driven reader can now wait until additional network activity or TCP closure instead of reporting the TLS failure.

    The drained-session optimization needs to preserve notification of pending TLS errors as well as buffered plaintext. The ordinary incoming-data path still works because the descriptor continues to be polled; this finding concerns the saved-error case. This reproduction exercises cryptlib directly, rather than the full JavaScript event loop.
    --- SBBSecho 3.38-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Rob Swindell@1:103/705 to GitLab note in main/sbbs on Wed Nov 11 13:57:14 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/ab31f9f1c04c35ede2c86d15f44b1a4836a092c9#note_10879

    Confirmed and fixed in 3cf61919e1.

    The saved error is in `sess_rd.c`: `getData()` stores a fatal error met after plaintext was copied in `pendingReadErrorState` and returns the data; only the next `cryptPopData()` reports it. I reproduced it through the JS event loop as well: a TLS 1.2 peer sends `hello` and a corrupt application-data record in one write and keeps TCP open, and a script reading from a `read` callback got `hello` and then nothing for 8 seconds. A close alert in place of the corrupt record behaves the same way.

    The fix: after a short successful pop, `js_socket_recv()` asks the session for `CRYPT_ATTRIBUTE_ERRORMESSAGE`. `MESSAGE_ENV_POPDATA` clears the error info before each read (`cryptses.c`), so a message present after a pop that returned `CRYPT_OK` is the saved error. In that case `tls_drained` stays false and the next `js_socket_tls_readable()` probe pops it. With the fix the script is told about 2 ms after the data; a clean short read sets nothing and costs no extra wait (the attribute read does no I/O).

    Two things for you to judge:

    - It depends on that clear-before-pop behaviour, and on every deferred fatal error carrying a message. The paths I traced (`tryRead()` failures, which go through `retExt()` or `sNetGetErrorInfo()`) all do, and internal errors are returned at once rather than deferred. A saved error with no message would still be missed. The alternative I tried first, one more zero-timeout pop after every short read, has no such dependency but held the loop about 10 ms longer per TLS read on Windows.
    - Once the probe has consumed the saved error, the `recv()` that follows logs `'Bad argument, parameter 1' (-1) popping data` rather than the original status. I have not looked into why.

    Built with MSVC and GCC; the reproduction ran on the GCC build only.

    — *Authored by Claude (Claude Code), on behalf of @rswindell*
    --- SBBSecho 3.38-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)