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

[Core] Base/FileInfo.cpp: To avoid race conditions we should rather return a file pointer than a file name #6803

Open
luzpaz opened this issue Apr 27, 2022 · 12 comments
Labels
Core Issue or PR touches core sections (App, Gui, Base) of FreeCAD Good first issue Suitable issues for potential first-time contributors Help wanted Refactor PRs that only refactor code
Milestone

Comments

@luzpaz
Copy link
Contributor

luzpaz commented Apr 27, 2022

std::string FileInfo::getTempFileName(const char* FileName, const char* Path)
{
//FIXME: To avoid race conditions we should rather return a file pointer
//than a file name.

@luzpaz luzpaz added Core Issue or PR touches core sections (App, Gui, Base) of FreeCAD Good first issue Suitable issues for potential first-time contributors Refactor PRs that only refactor code labels Apr 27, 2022
@nebularnoise
Copy link

Hi! I'm interested in taking this one (trying hacktoberfest for the first time)

In almost all case, it seems this function is called to instantiate an fstream, so it would seem to make sense to return an opened fstream directly. In a few other cases however, it seems only the name is needed, and the file should remain closed.

There could either be two functions, one returning the name, to maintain 'legacy' behaviour, and the other returning a fstream.
Alternatively, std::fstream could be wrapped to also store the filename.

@luzpaz
Copy link
Contributor Author

luzpaz commented Oct 18, 2022

@wwmayer could you weigh-in on this?

@luzpaz
Copy link
Contributor Author

luzpaz commented Oct 26, 2022

Hey @berniev could you take a stab at this?

@berniev
Copy link
Contributor

berniev commented Oct 26, 2022

@luzpaz Thank you for your thinking of me, but to be honest this question is still outside the realm of my knowledge and experience.

@luzpaz
Copy link
Contributor Author

luzpaz commented Nov 16, 2022

@nebularnoise this maybe too ambitious. We need @wwmayer to weigh-in. But in the meantime, feel free to choose another issue?

@wwmayer
Copy link
Contributor

wwmayer commented Nov 18, 2022

The function getTempFileName() is supposed to return the name of a tmp. file. Therefore it uses some OS-specific functions that already create a handle of a tmp. file and release it immediately (Windows: GetTempFileName / DeleteFile and other OS mkstemp / unlink) just to get this name.

The problem is that when trying to open the file for writing there is no guarantee that in the meantime any other process already has access on this file. The consequence may be that the access will be denied or even worse the file will be (partially) overridden.

The better way would be to return a class object (e.g. TemporaryFile) where its constructor creates the file and the destructor releases it.

@luzpaz luzpaz added this to the 1.0 milestone Nov 6, 2023
@CalebHeydon
Copy link

I was looking through the codebase to see where this function is used, and it looks like there are a few places where something is appended to the temporary filename. I was wondering what the best way to handle that would be? Here's an example:

fileInfo.setFile(Base::FileInfo::getTempFileName() + ".stl");

@nebularnoise
Copy link

nebularnoise commented Apr 11, 2024

@CalebHeydon
In terms of the exposed API, maybe adding a "suffix" argument would work?
On top of that, as @wwmayer suggested, if the function returns a TemporaryFile object, it would prevent future "abuses" of this function call which modify the filename.

In terms of implementation, mkstemps supports suffixes out of the box. For Windows, I'm not aware of a system call which does, but I'm no expert. In any case, if we're to return a file handle, it will be necessary to check whether the file creation is successful, so we might as well move this "append" within the implementation, and try to create the file until successful.

@CalebHeydon
Copy link

Adding a suffix argument is a good idea. What would be the benefit of creating a TemporaryFile wrapper over returning an fstream? File deletion?

@nebularnoise
Copy link

Yes, I assume in many cases, the file is ultimately deleted. For that, an fstream is inadequate.

The original comment suggests one workaround, using a std::FILE* directly.

//FIXME: To avoid race conditions we should rather return a file pointer
//than a file name.

While that does solve both problems of being able to delete the file, and to maintain ownership of it to prevent race conditions, it leaves the function very vulnerable to misuses, because returning a std::FILE* gives too much power to the caller.
There's no way to prevent them from copying the filename and closing the file immediately. From their perspective, it would be a very convenient thing to do, it's the same behaviour as the old function, they got a string containing the name of a temporary file. But we'd be back to square one.

Wrapping a std::FILE* into a TemporaryFile would provide a much friendlier interface, with unique_ptr-like "lifetime management", and prevent reintroduction of similar race condition issues.

@CalebHeydon
Copy link

I not a C++ expert. How would you implement the TemporaryFile lifetime management? Why not just have the function return a unique pointer?

@nebularnoise
Copy link

unique_ptr applies the RAII philosophy to memory allocation, this class would do the same but for files on the filesystem.

I've been playing with the idea on Compiler Explorer, here's what I've got so far
https://godbolt.org/z/9cGxWoW98

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Core Issue or PR touches core sections (App, Gui, Base) of FreeCAD Good first issue Suitable issues for potential first-time contributors Help wanted Refactor PRs that only refactor code
Projects
None yet
Development

No branches or pull requests

5 participants