Skip to content

Validate undefined Ids and builtin set ids in release builds#3817

Closed
idubinov wants to merge 4 commits into
KhronosGroup:mainfrom
idubinov:t_ASSERT_E2_id_lookup
Closed

Validate undefined Ids and builtin set ids in release builds#3817
idubinov wants to merge 4 commits into
KhronosGroup:mainfrom
idubinov:t_ASSERT_E2_id_lookup

Conversation

@idubinov

@idubinov idubinov commented Jun 29, 2026

Copy link
Copy Markdown

Malformed input runs an unchecked static_cast on objects leading to type confusion/null-pointer deref and out-of-bound reads that can leak heap memory into the translated output.

This change adds checks and rejection of module, closing the leak and the type-confusion.

@idubinov
idubinov marked this pull request as draft June 29, 2026 15:45
@idubinov idubinov changed the title SPIR-V reader: validate undefined Ids and builtin set ids in release builds Validate undefined Ids and builtin set ids in release builds Jun 29, 2026
@idubinov idubinov closed this Jun 29, 2026
@idubinov idubinov reopened this Jun 29, 2026
@idubinov
idubinov marked this pull request as ready for review June 30, 2026 09:16
@idubinov

Copy link
Copy Markdown
Author

@idubinov idubinov closed this Jul 7, 2026

@MrSidims MrSidims left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@idubinov I believe the PR should be re-opened.

Please humanize the description. May be it's just me. but I've re-read it several times and didn't understood it. Meanwhile guarding getEntry result seem to fix a potential and real problem.

Comment thread lib/SPIRV/SPIRVReader.cpp
Comment on lines +359 to +362
if (!BM->getErrorLog().checkError(
LenExpr && isConstantOpCode(LenExpr->getOpCode()),
SPIRVEC_InvalidModule,
"OpTypeArray length must reference an integer constant"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this should be an assertion imho

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

this runtime check prevents OOB data read

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I also fail to see how this can result into OOB data read. A test case showing it would be helpful.

Comment on lines +59 to +60
getErrorLog().checkError(false, SPIRVEC_InvalidModule,
"OpTypeArray length must reference a constant");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this should be an assertion imho

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

this check should be runtime check because it prevents potential uncontrollable data read

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@idubinov do you mean it could happen within the translator? I don't see any calls to getArrayLength() that could be dangerous. Am I missing anything?

Comment on lines +156 to +160
if (!P) {
I.M.getErrorLog().checkError(false, SPIRVEC_InvalidModule,
"operand references an undefined Id");
I.M.setInvalid();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The check belongs to getEntry implementation

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Disagree. The main idea of this code is to label the module invalid in case of try to get incorrect Entry. The getter should not change state of class. I also think that both asserts inside getEntry function were placed by mistake - it is normal behavior for getter to return nullptr in case of no item found

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It's not a getter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@idubinov is it really meaningful to mark the module invalid? Are we not terminating the process with I.M.getErrorLog().checkError(...)?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Disagree. The main idea of this code is to label the module invalid in case of try to get incorrect Entry. The getter should not change state of class. I also think that both asserts inside getEntry function were placed by mistake - it is normal behavior for getter to return nullptr in case of no item found

This contradicts your other patch in https://github.com/KhronosGroup/SPIRV-LLVM-Translator/pull/3798/changes#r3544542206 which adds getEntry error out.

@idubinov idubinov Jul 8, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@maarquitos14 , it can be aborted, asserted, or ignored, so, in general, the app is not ends in case of check fail
SPIRVError.h:156

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Theoretically, yes, but what I've seen is that it's set to SPIRVDbgErrorHandlingKinds::Exit, and there is no mechanism to change that value. I'm not sure if any downstream tool would ever use that, otherwise we could even think of removing the other two options, since they're not used anywhere. Obviously, that would be a different patch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@maarquitos14 SPIRVDbgErrorHandlingKinds::Exit is set in this project. but was added for translator to be able to be used by "some custom clang toolchains" (#561). So, I expect that in some custom toolchains the SPIRVDbgError may have different value and checkError may have different behavior

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the explanation and the reference.

@idubinov idubinov reopened this Jul 8, 2026
@idubinov

idubinov commented Jul 8, 2026

Copy link
Copy Markdown
Author

@MrSidims ,

May be it's just me

Me too


SPIRVExtInstSetKind SPIRVModuleImpl::getBuiltinSet(SPIRVId SetId) const {
auto Loc = IdToInstSetMap.find(SetId);
assert(Loc != IdToInstSetMap.end() && "Invalid builtin set id");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm happy to keep the assert too for debugging purposes if this is something that should not happen. To be clear, I mean keeping the assert additionally to the new changes you added. The new changes should be absolutely kept.

Comment on lines +156 to +160
if (!P) {
I.M.getErrorLog().checkError(false, SPIRVEC_InvalidModule,
"operand references an undefined Id");
I.M.setInvalid();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@idubinov is it really meaningful to mark the module invalid? Are we not terminating the process with I.M.getErrorLog().checkError(...)?

Comment on lines +59 to +60
getErrorLog().checkError(false, SPIRVEC_InvalidModule,
"OpTypeArray length must reference a constant");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@idubinov do you mean it could happen within the translator? I don't see any calls to getArrayLength() that could be dangerous. Am I missing anything?

Comment thread lib/SPIRV/SPIRVReader.cpp
Comment on lines +359 to +362
if (!BM->getErrorLog().checkError(
LenExpr && isConstantOpCode(LenExpr->getOpCode()),
SPIRVEC_InvalidModule,
"OpTypeArray length must reference an integer constant"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I also fail to see how this can result into OOB data read. A test case showing it would be helpful.

@idubinov idubinov closed this Jul 9, 2026
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.

3 participants