Validate undefined Ids and builtin set ids in release builds#3817
Validate undefined Ids and builtin set ids in release builds#3817idubinov wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
@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.
| if (!BM->getErrorLog().checkError( | ||
| LenExpr && isConstantOpCode(LenExpr->getOpCode()), | ||
| SPIRVEC_InvalidModule, | ||
| "OpTypeArray length must reference an integer constant")) |
There was a problem hiding this comment.
this should be an assertion imho
There was a problem hiding this comment.
this runtime check prevents OOB data read
There was a problem hiding this comment.
I also fail to see how this can result into OOB data read. A test case showing it would be helpful.
| getErrorLog().checkError(false, SPIRVEC_InvalidModule, | ||
| "OpTypeArray length must reference a constant"); |
There was a problem hiding this comment.
this should be an assertion imho
There was a problem hiding this comment.
this check should be runtime check because it prevents potential uncontrollable data read
There was a problem hiding this comment.
@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?
| if (!P) { | ||
| I.M.getErrorLog().checkError(false, SPIRVEC_InvalidModule, | ||
| "operand references an undefined Id"); | ||
| I.M.setInvalid(); | ||
| } |
There was a problem hiding this comment.
The check belongs to getEntry implementation
There was a problem hiding this comment.
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
There was a problem hiding this comment.
@idubinov is it really meaningful to mark the module invalid? Are we not terminating the process with I.M.getErrorLog().checkError(...)?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@maarquitos14 , it can be aborted, asserted, or ignored, so, in general, the app is not ends in case of check fail
SPIRVError.h:156
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@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
There was a problem hiding this comment.
Thanks for the explanation and the reference.
Me too |
|
|
||
| SPIRVExtInstSetKind SPIRVModuleImpl::getBuiltinSet(SPIRVId SetId) const { | ||
| auto Loc = IdToInstSetMap.find(SetId); | ||
| assert(Loc != IdToInstSetMap.end() && "Invalid builtin set id"); |
There was a problem hiding this comment.
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.
| if (!P) { | ||
| I.M.getErrorLog().checkError(false, SPIRVEC_InvalidModule, | ||
| "operand references an undefined Id"); | ||
| I.M.setInvalid(); | ||
| } |
There was a problem hiding this comment.
@idubinov is it really meaningful to mark the module invalid? Are we not terminating the process with I.M.getErrorLog().checkError(...)?
| getErrorLog().checkError(false, SPIRVEC_InvalidModule, | ||
| "OpTypeArray length must reference a constant"); |
There was a problem hiding this comment.
@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?
| if (!BM->getErrorLog().checkError( | ||
| LenExpr && isConstantOpCode(LenExpr->getOpCode()), | ||
| SPIRVEC_InvalidModule, | ||
| "OpTypeArray length must reference an integer constant")) |
There was a problem hiding this comment.
I also fail to see how this can result into OOB data read. A test case showing it would be helpful.
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.