Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std::fs::write is not atomic - document it explicitly or make it atomic #82590

Open
Kixunil opened this issue Feb 27, 2021 · 8 comments
Open
Labels
A-io Area: `std::io`, `std::fs`, `std::net` and `std::path` T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@Kixunil
Copy link
Contributor

Kixunil commented Feb 27, 2021

By not being atomic, std::fs::write() may be a footgun for people trying to create correct/secure code.
As an example, nodejs has a function with same behavior that led to a security risk. In that case creation of the secret token file succeeded but writing to it failed leaving an empty token file which caused application to accept empty authentication token later.

The documentation currently kind of describes the behavior but the implications may not be obvious to people. Adding a note about the risk of empty/corrupted file being left in case of failure could help people avoid issues.

If atomic behavior is undesired due to compatibility reasons, then maybe add another atomic_write() function and suggest it in write() doc. This could be done in a crate obviously but maybe the security/robustness benefits are a good reason to put it into std.

I'm willing to make a PR regardless of the conclusion.

@the8472
Copy link
Member

the8472 commented Feb 27, 2021

By not being atomic, std::fs::write() may be a footgun for people trying to create correct/secure code.

That isn't limited to fs::write though. Several of the convenience methods shouldn't be used in security-sensitive contexts since they would suffer from lack of atomicity or TOCTOU races. In some contexts even path traversal isn't safe by default.

then maybe add another atomic_write() function

Several crates for atomic file operations exist on crates.io.

Note that the usual atomic file creation dances have a cost, they keep more data on the disk until the operation is complete, which could be an issue on constrained systems. I'm not saying this concern would be a blocker for making fs::write atomic, but it's not without downsides. Additionally the atomic file dances can occasionally leave you with stranded temporary files on the filesystem.

@Kixunil
Copy link
Contributor Author

Kixunil commented Feb 27, 2021

@the8472 very good points and I agree 100%. I didn't intend to dismiss those. My idea is to make std more helpful somehow. Whether it's by making operations safer or documentation more explicit is up for discussion.

If I was confident that there's one true solution, I'd just open a PR, not an issue. :)

Several crates for atomic file operations exist on crates.io.

Could you recommend some you're experienced with?

@the8472
Copy link
Member

the8472 commented Feb 27, 2021

My idea is to make std more helpful somehow. Whether it's by making operations safer or documentation more explicit is up for discussion.

I think improving the documentation would be fine. There just is that niggling doubt that we can never do enough to document all possible pitfalls for all security-critical use-cases. This could basically fill a book.

Could you recommend some you're experienced with?

I don't want to endorse anything. tempfile and atomicwrites both look half-decent in different ways (at least on unix systems) but each lacks some things I would expect from a maximally paranoid implementation. Each already has issues/PRs open about improving the state the things.

@Kixunil
Copy link
Contributor Author

Kixunil commented Feb 27, 2021

This could basically fill a book.

I hope it's not that bad. :D

Thanks for pointers!

@the8472
Copy link
Member

the8472 commented Feb 27, 2021

It's that bad if we're talking about using filesystem APIs in security contexts in general. Atomically and durably overwriting a file in a potentially attacker-controlled directory is a bit smaller in scope, it would merely take up several blog articles or a chapter in the book.

And that's just about linux stuff. Other OSes bring their own complications.

@ChrisDenton ChrisDenton added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. A-io Area: `std::io`, `std::fs`, `std::net` and `std::path` labels Oct 27, 2022
@thomcc
Copy link
Member

thomcc commented Oct 27, 2022

I would be very opposed to making std::fs::write attempt to be atomic by default. There's no general way of doing that that doesn't come with its own drawbacks, especially when used in publicly accessible directories (Apple's "Secure Coding Guide" contains documentation warning against this).

And this isn't getting into the fact that we can't really make it atomic in the face of crashes in many situations. (To be clear: in general it is that bad)

@the8472
Copy link
Member

the8472 commented Oct 27, 2022

On platforms that have O_TMPFILE and *at syscalls it is possible, but google tells me that macos does not have the former.

@Kixunil
Copy link
Contributor Author

Kixunil commented Oct 27, 2022

especially when used in publicly accessible directories

If you use non-atomic write there you're already screwed.

But intuitively, I agree that documenting is better than making it atomic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-io Area: `std::io`, `std::fs`, `std::net` and `std::path` T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants