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 global cache destruction in IdString class #3081

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Icenowy
Copy link
Contributor

@Icenowy Icenowy commented Nov 18, 2021

When enabling LTO on GCC, the sequence of destructing static members in
IdString class is not promised, which can lead to the cache itself being
destructed but the destruct_guard not destructed.

Put all static members of IdString to a subclass similar to current
desturct_guard_t, to mmakes the guard a real guard.

This fixes SEGV when exiting a LTO-ed GCC-built yosys binary.

Signed-off-by: Icenowy Zheng icenowy@aosc.io

@DanielG
Copy link
Contributor

DanielG commented Aug 27, 2023

@Icenowy could you rebase this patch? The hacky workaround that is b59c717 seems to have caused conflicts.

@dragonmux we're sucessfully using this patch in Debian to fix LTO-enabled yosys, you might want to pick this one up :)

@dragonmux
Copy link
Contributor

Our fix isn't that hacky - it fixes a legitimate bit of UB albeit with some different UB that the compiler can't actually notice to optimise out. It should work to fix LTO-enabled yosys on Debian just the same (that's how we're using it!).

That said, if Icenowy is happy for us to, we'd be happy to look at rebasing this patch and championing it to help see it merged. It looks to be a much more holistic approach to the problem.

@whitequark
Copy link
Member

I would describe intentionally invoking UB as "hacky".

@DanielG
Copy link
Contributor

DanielG commented Aug 27, 2023

My thoughts exactly ;)

When enabling LTO on GCC, the sequence of destructing static members in
IdString class is not promised, which can lead to the cache itself being
destructed but the destruct_guard not destructed.

Put all static members of IdString to a subclass similar to current
desturct_guard_t, to mmakes the guard a real guard.

This fixes SEGV when exiting a LTO-ed GCC-built yosys binary.

Signed-off-by: Icenowy Zheng <uwu@icenowy.me>
@Icenowy
Copy link
Contributor Author

Icenowy commented Aug 29, 2023

well I think my code just reduces UB instead of removing it...

Preventing UB here is just too difficult...

log("#X# DB-DUMP index %d: '%s' (ref %d)\n", idx, global_id_storage_.at(idx), global_refcount_storage_.at(idx));
}
static struct global_cache_t {
bool destruct_guard; // POD, will be initialized to zero
Copy link
Member

Choose a reason for hiding this comment

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

I think accessing destruct_guard of a destroyed object isn't legal. Neither is calling a method of a destroyed object. However, if you put static bool destruct_guard; outside of global_cache_t that would make it accessible until the program dies, and if you keep all methods accessing it static calling them would also be fine.

Does that sound reasonable to you?

Copy link
Contributor

@dragonmux dragonmux Aug 29, 2023

Choose a reason for hiding this comment

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

You are correct that this re-introduces the exact piece of UB that our original patch was aimed at fixing. The compiler can reasonably assume that destruct_guard is always true because it cannot be false because accessing it after destruction is strictly invalid. That's why the compiler/LTO was able to optimise this out before our PR.

The trick is that the reason for this guard existing is itself due to other much more difficult to fix UB fundamental to how yosys is written, so the best one can do is reduce this to something the compiler cannot reason about in that way, so while the lifetime is still violated, it's done so in a way that is well-enough defined and non-optimisable.

Copy link
Member

Choose a reason for hiding this comment

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

Which other UB?

Copy link
Contributor

@dragonmux dragonmux Aug 29, 2023

Choose a reason for hiding this comment

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

Calling any and all functions on this class after it's already been destroyed is itself UB. This guard needing to exist in any form is itself indicative of UB in the way yosys tears down it structures and state as it exits.

Copy link
Member

Choose a reason for hiding this comment

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

I did offer a solution to this, though:

and if you keep all methods accessing it static calling them would also be fine.

Copy link
Member

@whitequark whitequark left a comment

Choose a reason for hiding this comment

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

See inline comments--I think this PR also replaces UB with more UB.

@Icenowy
Copy link
Contributor Author

Icenowy commented Aug 30, 2023 via email

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.

None yet

4 participants