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

Fix #6322: Attempt to crash the script instead of the whole game #9047

Merged
merged 2 commits into from
Apr 17, 2021

Conversation

rubidium42
Copy link
Contributor

Motivation / Problem

See #6322. A script allocating (way) too much memory can crash the game which is arguably worse than crashing just the script.

Description

The solution is two fold:

  1. Enable the limit check in the allocation logic, instead of relying on it to be checked after the "tick"
  2. Not using MallocT/ReallocT but the raw malloc/realloc, so instead of (M|Re)allocError killing the game the script is killed.

As side effect some extra information about the allocation was added to the error message that could potentially help the script's developer, but that required changing the error message in Script_FatalError from char* to std::string&.

Limitations

Scripts can now be killed earlier, and probably even during saving, when the memory limit is reached. Previously if the memory was returned before the save or tick finished, the script would just continue.
It is a somewhat open question whether the old behavior of limit testing should be maintained, so the AI can for short bursts and saving use more memory. That would mean this change does not impact the scripts as much, however the benefit of doing it when the allocation happens is that the log will show exactly where the limit got exceeded and by how much. This could then help the script developer see where a huge amount of memory got allocated (in error) and that would make fixing the underlying issues in the scripts easier.

In case the OS is out of memory, and there is not even enough memory to go through the script engine cleanup the game will still crash out with MallocError. Though, not going through the script engine cleanup will leave some "backups" to be not restored so the game would be in an undefined state and it would likely crash at another place.

Checklist for review

Some things are not automated, and forgotten often. This list is a reminder for the reviewers.

  • The bug fix is important enough to be backported? (label: 'backport requested')
  • This PR affects the save game format? (label 'savegame upgrade')
  • This PR affects the GS/AI API? (label 'needs review: Script API')
    • ai_changelog.hpp, gs_changelog.hpp need updating.
    • The compatibility wrappers (compat_*.nut) need updating.
  • This PR affects the NewGRF API? (label 'needs review: NewGRF')

@nielsmh
Copy link
Contributor

nielsmh commented Apr 17, 2021

It might still be possible to kill a 32 bit build of the game by forcing lots of little allocations in lots of AI scripts, but it'd be difficult to guard against absolutely every case I think, the way Squirrel's memory allocations are structured.

@rubidium42
Copy link
Contributor Author

It might still be possible to kill a 32 bit build of the game by forcing lots of little allocations in lots of AI scripts, but it'd be difficult to guard against absolutely every case I think, the way Squirrel's memory allocations are structured.

Even with the 64 bit build it is still possible to crash the game with lots and lots of small allocations, but then you run into the territory of not having the required free memory to clean up that mess. That can of course be mostly worked around by letting the script allocator allocate something like 1 MB that it can free upon malloc/realloc returning null, so the allocations for the cleanup are more likely to succeed but that sounds like an even worse solution.

Having said that, maybe the memory limit the user has "set" for the scripts might then be too high for their environment and they rather should reduce that.

But alas, it is attempting not to crash the game and with the "extreme" (usually buggy) allocations it will do that job quite nicely. When your system is severely memory starved, yes... then this is not guaranteed going to help but there is not much that can be done about that. And if it is that memory starved, even the standard crash handling routines would probably crash as well as there is no memory to allocate the window for an message box, even the string to show in the error window, or anything related to making crash saves.

@LordAro LordAro added the backport requested This PR should be backport to current release (RC / stable) label Apr 17, 2021
Copy link
Member

@LordAro LordAro left a comment

Choose a reason for hiding this comment

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

At any rate, it's better than what was there previously, so sure

@michicc michicc merged commit e5fedcd into OpenTTD:master Apr 17, 2021
@rubidium42 rubidium42 deleted the issue-6322 branch April 18, 2021 06:29
@LordAro LordAro added backported This PR is backported to a current release (RC / stable) and removed backport requested This PR should be backport to current release (RC / stable) labels Apr 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backported This PR is backported to a current release (RC / stable)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants