-
Notifications
You must be signed in to change notification settings - Fork 22
Fix #4: Segfault on nonexisting disk image #19
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
Fix #4: Segfault on nonexisting disk image #19
Conversation
… std::experimental;
- Check if the disk exists - Check if autocreate mb is set - Create the file * Stop the segfaults: - Assume default filename based on device id/type - Assume default filesize
|
Thank you for submitting this.
Also this doesn't solve #4, only implements a workaround preventing the situation where the bug triggers from happening Edit: Sorry, I used the wrong executable for the debugger - the segfault in your code after setting the disk file to an unreadable file is a different one: However this problem will still persist when using the exception instead of (the original segfault) Let me know if you have any questions related to this - I'm aware it is a little bit confusing. |
|
And another comment - please rebase your branch instead of merging the |
- do not assume a default filesize for nonexisting files. - do not std::exit, the segfault i'm trying to fix is not caused by the throw
…tting. has to be in project root...
|
First of all, thank you for taking the time to look at the pr and write such a detailed response. Helps me a lot and I appreciate it. Ooh, merge vs. rebase, almost as big a holy war as vim/emacs. I'll undo that commit, so master isn't merged in. After reading your response I realized indeed that the segfault wasn't caused by the FAILURE_ macro throwing an exception, my assumption was wrong. Thus most of the code I wrote was also incorrect in solving the issue since I didn't understood the cause of the issue. I've updated the pr, removed the c++17 and std::filesystem, since I only wanted to use that to check if the file exists. What are the platforms axpbox targets? compiler wise, I also moved the clang format file to the root of the project, my IDE (clion) otherwise doesn't want to use it. (I'm used to a linux/nucleus and GCC specific codebase which recently we've upgraded to support C++ 17, no cross platform required, mostly business logic, might explain some of my code) I'm now working on a correct solution for the segfault. |
11c40df to
e387224
Compare
…s[] only has 7 items. Delete-ing number 8 segfaults, which means that memory wasn't new-ed. During construction of components, they are registered and counted in a counter. I first tried to change the array to a std::vector, but that left a half constructed item, failed as well. Adding an unregister function, and calling that in the destructor, assures that the component that failed to construct (due to disk throwing an error inside its constructor) is not used later on in other destructors.
|
I think I've found the issue. During construction, the disk component throws an exception, which in main is handled as the last generic exception. There is a counter for the components, which is incremented in the base class. The component never constructs, so the destructor accesses invalid memory. I've added a counter decrement to the base class destructor. In the image you see iNumComponents = 8, but the eight component in that array is not yet complete (it's blue instead of yellow). Let me know what you think! |
I certainly prefer merge when adding changes from a PR, I only don't like merging the main branch into a working branch, since there would be multiple merges in the main branch after merging the working branch.
Yeah, this issue is a tricky one - if it was that easy I would have solved it already.
es40 aimed to support a wide range of operating system, including Windows and OpenVMS. I currently aim to target Unix-like systems in general (which are targeted by POCO POSIX support), but in the future I may change that to whatever Qt Core 5 runs on (I'm currently working on this in the qtcore branch, but I may decide not to do this - also see #7). As of the C++ version - es40 is written in very generic C++, esentially using it only as "C with classes"; for AXPbox C++11 is fine (and in fact I would like to modify it to include C++11 features as unique_ptr and so on). It comes with the cost of dropping support of some platforms that don't have a C++11 compiler, but I consider this less important. (Another fact is the version of CMake used doesn't build without C++11, so I can't build it for example on my Mac OS X Leopard machine, so one could say it's kind of required even now.)
Good idea - I didn't know that and set the CLion style manually.
OK :) |
I also noticed that yesterday. The cause is the |
|
Yesterday I added some more commits to handle the issue, (unwinding in the correct order) but not sure if that's the correct way. The segfault for me at least doesn't occur. I'll try to work the comments you made on platform support and development contributions into the wiki (or the GitHub document which is shown when doing a merge request). Should save you some work. |
lenticularis39
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise this looks nice. I also like the fact you used C++ functions instead of C ones (as in es40) - in the future I would like to rewrite the entire code to be like that :)
src/System.cpp
Outdated
| } | ||
|
|
||
| void CSystem::UnregisterComponent(CSystemComponent *component) { | ||
| if (acComponents[iNumComponents] == component) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if unregistering a component this way is a good idea - the destructors may be called in a different order that the constructors.
Maybe the component storing its number and checking for that would help with this?
(I don't generally like the component managing its registration in CSystem, but that's another issue.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think comparing by memory address is less likely to cause race condition when components are destructed i another order. A counter would have to be atomic, and a for loop to compare all items would have the same crash behavior.
Catching the exception at creation and then handling it would be another solution but that would also require some refactoring of the component registration (it happens so early in the base class, it be better if it was registering after constructing for this bug).
I could try to just not set it to a nullptr because it will be deleted right after the call to stop threads? Or try how a std::atomic counter would perform? What has your preference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right - I missed that the component number couldn't be read from an uninitialized component. I take my comment back, you can keep the code as it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could try to just not set it to a nullptr because it will be deleted right after the call to stop threads? Or try how a std::atomic counter would perform? What has your preference?
I might be missing something, but doesn't the if before the nullptr setting check the wrong index? iNumComponents is increased in CSystem::RegisterComponent, so it should point to the next (empty) element, not to the last one. If that's the case then the setting to nullptr isn't needed, because it clearly works without it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you are right, it checks the wrong component. The entire check can be removed, I'll do that right away.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's done, now it just decrements the counter.
Which specific c++ functions vs c functions do you refer to? |
|

I saw the bug (#4) which seems simple enough for me to look at. I choose to assume default values (for name and autocreate size) if none were given, I hope you agree with that (the bug itself has little context, just, subject says enough). I also used
std::filesystemand anstd::fstreambecause I'm used to those (I haven't worked withFILE*much), plus split up the code a bit in separate small functions.Default filename like:
pci0.15(ali_ide).disk0.0(file).default.imgOutput when nothing is specified for the disk:
If it's a CD drive, autocreation is not done and we just exit (no segfault).
I also considered a sort of configuration file validator before starting the emulator, but that would require quite a bit more work. And, then we'd have a validated config file but in all code would validate again, that seems double work ...
Let me know what you think and if you like the default value idea. My reasoning was that in the config file there already is a disk section, so the user does seem to want a disk, lets do a default that is good enough to install OpenVMS on.