Skip to content

Fix extremely slow ttyname_r performance on macOS#315

Closed
gdw2vs wants to merge 2 commits intodoy:mainfrom
gdw2vs:fix-macos-ttyname-performance
Closed

Fix extremely slow ttyname_r performance on macOS#315
gdw2vs wants to merge 2 commits intodoy:mainfrom
gdw2vs:fix-macos-ttyname-performance

Conversation

@gdw2vs
Copy link
Copy Markdown

@gdw2vs gdw2vs commented Feb 12, 2026

Note: This solution was implemented with assistance from Claude Sonnet 4.5. I don't know rust (yet). Feel free to close out.

Problem

On macOS, the ttyname_r system call can take 2-5+ seconds instead of milliseconds, causing severe performance degradation when retrieving passwords. This affects users with large password databases where the delay becomes particularly noticeable.

Fixes #11

Solution

This PR uses fcntl with F_GETPATH on macOS to retrieve the TTY device path, which is much faster than ttyname_r. The implementation:

  1. Checks if stdin is a terminal before attempting to get its path
  2. On macOS: Uses fcntl(F_GETPATH) to get the TTY path (fast)
  3. Other platforms: Continues using rustix::termios::ttyname (standard approach)
  4. Falls back gracefully: If fcntl fails on macOS, falls back to ttyname

Why This Addresses the Maintainer's Concern

The maintainer previously raised a concern about using /dev/tty (which would refer to the agent's terminal rather than the client's terminal). This solution does not use /dev/tty. Instead:

  • fcntl(F_GETPATH) is called on stdin of the rbw client process (the terminal where you run rbw get)
  • This returns the actual TTY device path like /dev/ttys001 or /dev/ttys002
  • That specific path is passed to the agent via the socket
  • The agent then passes it to pinentry, which displays on the correct terminal

This is exactly what ttyname_r was doing - we're just using a faster macOS-specific API to achieve the same result.

Multi-Terminal Scenario

This correctly handles the case where:

  • Agent started in Terminal 1
  • User unlocks from Terminal 2
  • Terminal 2's path (/dev/ttys002) is detected and passed to the agent
  • Pinentry displays on Terminal 2 (where the unlock was requested)

Testing

  • Password retrieval time reduced from 2-5+ seconds to ~0.2 seconds on macOS
  • All existing tests pass
  • Tested with actual Bitwarden account - password retrieval works correctly
  • Non-TTY contexts (like piped input) are properly handled

Technical Details

The fcntl call with F_GETPATH (value 50 on macOS) is a macOS-specific API that retrieves the filesystem path for a file descriptor. It's documented in the macOS fcntl man page and is the recommended approach for this use case on macOS.


Note: This solution was implemented with assistance from Claude Sonnet 4.5.

@gdw2vs gdw2vs force-pushed the fix-macos-ttyname-performance branch from ab55df5 to 3e08a6b Compare March 24, 2026 17:45
On macOS, the ttyname_r system call can take 2-5+ seconds instead of
milliseconds, causing severe performance issues when retrieving
passwords (issue doy#11).

This commit uses fcntl with F_GETPATH on macOS to get the TTY device
path, which is much faster. The fcntl approach returns the same TTY
path (e.g., /dev/ttys001) that ttyname_r would return, maintaining
correct behavior for multi-terminal scenarios where the agent is
started in one terminal and unlock is requested from another.

Changes:
- Check if stdin is a terminal before attempting to get its path
- On macOS: Use fcntl(F_GETPATH) to get TTY path (fast)
- Other platforms: Continue using rustix::termios::ttyname (standard)
- Falls back to ttyname if fcntl fails on macOS

Testing shows password retrieval time reduced from 2-5+ seconds to
~0.2 seconds on macOS.

Fixes doy#11

Note: This solution was implemented with assistance from Claude Sonnet 4.5.
@gdw2vs gdw2vs force-pushed the fix-macos-ttyname-performance branch from 9b31ff1 to 25c9d8a Compare March 24, 2026 21:29
…vulnerabilities (RUSTSEC-2026-0007, RUSTSEC-2026-0049)
@sewnie
Copy link
Copy Markdown

sewnie commented Mar 29, 2026

Couldn't you implement this upstream in termios rather than just rbw?

@gdw2vs
Copy link
Copy Markdown
Author

gdw2vs commented Apr 9, 2026

@sewnie was right — this fix belongs upstream in rustix rather than here.

rustix::termios::ttyname on macOS calls libc::ttyname_r directly, which is the source of the slowness. The Linux backend already avoids ttyname_r by reading from /proc/self/fd/<fd>; the same approach can be applied on macOS using fcntl(F_GETPATH), which is a single kernel call and is already used elsewhere in rustix itself (fs::getpath).

I've opened a PR upstream: bytecodealliance/rustix#1605

I tested a local build of rbw against the patched rustix and confirmed it's fast. Closing this in favor of the upstream fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Getting passwords is slow?

2 participants