Skip to content

fix(macros): Mutable program borrows can survive async dispatch#1365

Merged
vobradovich merged 2 commits into
masterfrom
vo/fix-mut-over-async
May 21, 2026
Merged

fix(macros): Mutable program borrows can survive async dispatch#1365
vobradovich merged 2 commits into
masterfrom
vo/fix-mut-over-async

Conversation

@vobradovich
Copy link
Copy Markdown
Member

Resolves:

Generated programs can now services borrowing program state mutably, and the generated async dispatch may hold those mutable borrows across .await while future messages can obtain new mutable borrows from the same static mut PROGRAM.

Sails stores the program instance in a generated static mut PROGRAM. The generated dispatcher then constructs a service from that mutable program reference and awaits service.try_handle(...). Service dispatch also awaits async handlers. A service factory such as fn accounts(&mut self) -> AccountsService<'_> can return a service holding &mut references into the program. If an async handler on that service awaits a reply or other future, the future can retain the mutable reference while the Gear/Sails async message loop later processes another message and obtains another &mut PROGRAM. This violates Rust's exclusive aliasing assumptions for &mut references and can also create application-level reentrancy/state races, for example two withdrawals using stale state around an await. The issue is introduced by the &mut self service exposure support and the corresponding generated mutable borrow of the global program instance.

@vobradovich vobradovich self-assigned this May 20, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request modifies the #[program] macro and associated examples to ensure service constructors use shared references (&self) instead of mutable ones, mitigating risks of illegal borrows across asynchronous boundaries. The changes include macro logic updates to emit compile-time errors for &mut self constructors, refactoring demo applications to utilize interior mutability via Cell and RefCell, and adding documentation warnings. Feedback was provided to correct a technical error in the documentation regarding the appropriate RefCell method for obtaining a mutable guard.

Comment thread README.md Outdated
@vobradovich vobradovich merged commit b50cce6 into master May 21, 2026
4 checks passed
@vobradovich vobradovich deleted the vo/fix-mut-over-async branch May 21, 2026 10:32
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.

2 participants