Skip to content

[SandboxVectorizer][NFCI] Fix use of possibly-uninitialized scalar. #122201

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

Merged
merged 5 commits into from
Jan 10, 2025

Conversation

tylanphear
Copy link
Contributor

@tylanphear tylanphear commented Jan 9, 2025

The EraseCallbackID field is not always initialized in the ctor for SeedCollector; if not, it will be used uninitialized by its dtor. This could potentially lead to the erasure of a random callback, leading to a bug.

Fixed by making CallbackID an opaque type, which is always default-initialized to an invalid ID.

The `EraseCallbackID` field is not always initialized in the ctor for
SeedCollector, if not, it will be used uninitialized by its dtor. This
could potentially lead to the erasure of a random callback, leading to a
bug.

Fixed by initializing to a purposefully invalid ID
(`std::numeric_limits<ContextID>::max()`)
@llvmbot
Copy link
Member

llvmbot commented Jan 9, 2025

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-vectorizers

Author: Tyler Lanphear (tylanphear)

Changes

The EraseCallbackID field is not always initialized in the ctor for SeedCollector; if not, it will be used uninitialized by its dtor. This could potentially lead to the erasure of a random callback, leading to a bug.

Fixed by initializing to a purposefully invalid ID (std::numeric_limits&lt;ContextID&gt;::max())


Full diff: https://github.com/llvm/llvm-project/pull/122201.diff

1 Files Affected:

  • (modified) llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h (+3-1)
diff --git a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h
index 6e16a84d832e5e..cc4ebb5c508414 100644
--- a/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h
+++ b/llvm/include/llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h
@@ -292,7 +292,9 @@ class SeedCollector {
   SeedContainer StoreSeeds;
   SeedContainer LoadSeeds;
   Context &Ctx;
-  Context::CallbackID EraseCallbackID;
+  Context::CallbackID EraseCallbackID =
+      std::numeric_limits<Context::CallbackID>::max();
+
   /// \Returns the number of SeedBundle groups for all seed types.
   /// This is to be used for limiting compilation time.
   unsigned totalNumSeedGroups() const {

@vporpo
Copy link
Contributor

vporpo commented Jan 9, 2025

@Sterling-Augustine shouldn't we always register the callback in the constructor, before the early return?

@tylanphear Setting it to max() may not be the correct fix, because Ctx.unregisterEraseInstrCallback() in the SeedCollector's constructor may not work well with it.

@vporpo vporpo requested a review from slackito January 9, 2025 19:20
@slackito
Copy link
Collaborator

slackito commented Jan 9, 2025

Nice find! I think the right place to fix this is in the sandboxir::Context class, so that client code doesn't have to worry about initializing the callback ID to an invalid value. Right now a CallbackID is just an integer, but it's used as an opaque ID so we could have something like:

class CallbackID {
  uint64_t id = std::numeric_limits<uint64_t>::max();
};

and this way unregistering an uninitialized ID from anywhere would always trigger an assertion failure (in an asserts-enabled build), or at least not collide with any registered IDs.

What do you think?

@tylanphear
Copy link
Contributor Author

It seems like unconditionally setting the callback ID is a reasonable solution. Using an opaque ID (with a default initializer set to an invalid ID) also seems reasonable, though perhaps a broader change than I'd be comfortable committing without further approvals.

@vporpo
Copy link
Contributor

vporpo commented Jan 9, 2025

The fact that CallbackID is a uint64_t is an implementation detail, so we should discourage having the user initialize it with an integer value. Also the user should not have to worry about initializing it at all. So I am in favor of the opaque ID fix.

@tylanphear
Copy link
Contributor Author

@slackito, @vporpo, thank you both for your prompt review. I've modified the patch according to your suggestions to use an opaque ID type. Please let me know how you think the patch looks in its current state.

Copy link
Contributor

@vporpo vporpo left a comment

Choose a reason for hiding this comment

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

Thanks for the patch! It looks good in general, just a few minor comments.

public:
// Uses a 64-bit integer so we don't have to worry about the unlikely case
// of overflowing a 32-bit counter.
using ReprTy = uint64_t;
Copy link
Contributor

Choose a reason for hiding this comment

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

What does "Repr" stand for in ReprTy?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Representation, as uint64_t is the underlying representation. Open to suggestion on the naming here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Wdyt about ValTy?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems reasonable to me.

// Uses a 64-bit integer so we don't have to worry about the unlikely case
// of overflowing a 32-bit counter.
using ReprTy = uint64_t;

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I would define the reserved uninitialized value here as a public value:

static constexpr const ReprTy UninitVal = std::numeric_limits<ReprTy>::max();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, called it InvalidVal.

@@ -686,7 +686,7 @@ void Context::runMoveInstrCallbacks(Instruction *I, const BBIterator &WhereIt) {
Context::CallbackID Context::registerEraseInstrCallback(EraseInstrCallback CB) {
assert(EraseInstrCallbacks.size() <= MaxRegisteredCallbacks &&
"EraseInstrCallbacks size limit exceeded");
CallbackID ID = NextCallbackID++;
CallbackID ID{NextCallbackID++};
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably have a check here that NextCallbackID is not equal to the reserved value, with something like:

assert(NextCallbackID != CallbackID::UninitVal && "Matches the reserved value!");

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm it's probably better to put this check in the constructor.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense; put an assert in the constructor.

Copy link
Contributor

@vporpo vporpo left a comment

Choose a reason for hiding this comment

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

Thanks for the changes! LGTM. @slackito any other comments?

Copy link
Collaborator

@slackito slackito left a comment

Choose a reason for hiding this comment

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

Looks good, thank you!

@tylanphear tylanphear merged commit 4c0a0f7 into llvm:main Jan 10, 2025
8 checks passed
@tylanphear tylanphear deleted the fix_uninitialized_scalar branch January 10, 2025 19:06
BaiXilin pushed a commit to BaiXilin/llvm-fix-vnni-instr-types that referenced this pull request Jan 12, 2025
…lvm#122201)

The `EraseCallbackID` field is not always initialized in the ctor for
SeedCollector; if not, it will be used uninitialized by its dtor. This
could potentially lead to the erasure of a random callback, leading to a
bug.

Fixed by making `CallbackID` an opaque type, which is always
default-initialized to an invalid ID.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants