layers: Check an offset names an instruction before decoding - #13150
spencer-lunarg merged 1 commit into
Conversation
|
Author apollo-2006 not on autobuild list. Waiting for curator authorization before starting CI build. |
spencer-lunarg
left a comment
There was a problem hiding this comment.
- Did we not have a test for this
- Instead of returning "(specific instruction not recorded)" lets return a
booland the caller can see if it isfalseto know it needs to print a specific error message (in this case the shared memory array is likely being accessed OOB) - instead of making IsInstructionBoundary reloop the entire SPIR-V again (this is a hot code path here), lets have the
if (instruction_position_offset < kModuleStartingOffset || instruction_position_offset >= instructions.size()) {
check inside FindShaderSource
then instead of looping you can have GetDebugLineOffset set instruction_position_offset to a value like static constexpr uint32_t kInvalidValue = vvl::kNoIndex32; and then inside FindShaderSource it can go
} else if (instruction_position_offset == invalid) {
// the offset is OOB and the caller needs to handle this case
ss << "(instruction offset is larger than SPIR-V)";
return false;
} else if (instruction_position_offset != 0) {
spirv::Instruction target_inst(instructions.data() + instruction_position_offset);
if (target_inst.Length() == 0) {
ss << "(instruction offset is malformed)";
return false;
}
}
this both will save the extra loop, but also provide a more accurate "what is wrong" error message rather then just a general "(specific instruction not recorded)"
|
2 and 3 are done. Need clarity on point 1 though. The word read back from outside the shadow array is whatever LDS holds, and it is usually zero, which is indistinguishable from the "not recorded" marker. Five runs of the out of bounds test, three detections each:
The out of bounds message printed in one run out of five, so the test cannot assert it and users would rarely see it. In the shader the condition is exact, Which do you want? The host side is built and verified against the crash; the shader side report is a small change I have not written yet. |
|
For the test, you don't need to test the specific error, just make sure it prints "some error" (just do |
|
Author apollo-2006 not on autobuild list. Waiting for curator authorization before starting CI build. |
|
Pushed. Test does what you said, asserts an error prints and the description says it is there to prove building the message does not crash, with the issue link. Passes 6 runs out of 6 here. Rest of the review is in the same commit: Suites here: |
|
Author apollo-2006 not on autobuild list. Waiting for curator authorization before starting CI build. |
| // The walk here already visits every instruction boundary, so it can also tell the caller whether | ||
| // instruction_position_offset landed on one. An offset that does not is reported back as kNoIndex32 | ||
| // rather than costing a second pass over the module. | ||
| static uint32_t GetDebugLineOffset(const std::vector<uint32_t>& instructions, uint32_t& instruction_position_offset) { |
There was a problem hiding this comment.
sorry, 100% my fault, but since this is a static function, lets just something like
struct DebugLineInfo {
uint last_line_offset;
bool valid_offset;
};
static DebugLineInfo GetDebugLineOffset(const std::vector<uint32_t>& instructions, uint32_t instruction_position_offset)
const DebugLineInfo debug_line_info = GetDebugLineOffset(instructions, instruction_position_offset);
const uint32_t last_line_offset = debug_line_info.last_line_offset;
instead of doing half the values returned and half a constant ref
| // This offset is unpacked from the previous contents of the shadow slot. If the | ||
| // application indexed its shared memory out of bounds, the slot was out of range | ||
| // too and that word was never a packed offset. | ||
| strm << "The shared memory array is likely being accessed out of bounds, which would " |
There was a problem hiding this comment.
| strm << "The shared memory array is likely being accessed out of bounds, which would " | |
| strm << "Unable to detect source code, most likely because it did an OOB access on the shared memory array.\nNote: The shared memory data race report is not unreliable now." |
|
Author apollo-2006 not on autobuild list. Waiting for curator authorization before starting CI build. |
|
Done, |
| assert(length >= operand_offset); | ||
| const uint32_t remaining_words = length - operand_offset; | ||
| for (uint32_t i = 0; i < remaining_words; i++) { | ||
| // An opcode the grammar table does not know falls back to OpNop, which has no operands. |
There was a problem hiding this comment.
lets just do
if (opcode == spv::OpEntryPoint) {
ss << " " << string_SpvExecutionModel(Word(1)) << " %" << Word(2) << " [Unknown]";
} else if (opcode == spv::OpNop) {
ss << "OpNop found, which means something else has gone wrong";
}
|
Author apollo-2006 not on autobuild list. Waiting for curator authorization before starting CI build. |
|
All four taken. One word changed in your suggestion: "is not unreliable now" reads as a double negative, so it went in as "is now unreliable". Say the word if you meant it the other way. |
|
CI Vulkan-ValidationLayers build queued with queue ID 125329. |
|
CI Vulkan-ValidationLayers build # 24579 running. |
|
CI Vulkan-ValidationLayers build # 24579 failed. |
6556947 to
80e9932
Compare
|
Author apollo-2006 not on autobuild list. Waiting for curator authorization before starting CI build. |
|
CI Vulkan-ValidationLayers build queued with queue ID 125358. |
|
CI Vulkan-ValidationLayers build # 24580 running. |
|
CI Vulkan-ValidationLayers build # 24580 failed. |
80e9932 to
dcce77c
Compare
|
Author apollo-2006 not on autobuild list. Waiting for curator authorization before starting CI build. |
Fixes the crash in #13134.
FindShaderSourceis called with an offset that, for one caller, is not one GPU-AV wrote. The shared memory data race check recovers it from the previous contents of a shadow slot, so when an application indexes its shared memory out of bounds the slot is out of range too and the word read back was never a packed offset. It can still land inside the module without naming an instruction, and decoding it underflowsremaining_wordsinInstruction::Describe()and indexes an empty operand list.Per the discussion on the issue, the check lives in
spirv_logging.cppandDescribe()asserts the invariant rather than checking it. TheFindOpStructFromBDApath is left alone: it only ever receives the offset from the error record header, which was a valid instruction boundary in 2826 of 2826 reports I measured.Verified by forcing
instruction_position_offsetto a mid instruction offset inside the module, on a build with-D_GLIBCXX_ASSERTIONS, with only these two files differing:exit=134,back() [with _Tp = OperandKind]exit=0, "(specific instruction not recorded)", 5292/5292 backend tests pass--gtest_filter='*SharedMemoryDataRace*'71 passed, 6 skipped, 0 failed, and*GpuAVBufferDeviceAddress*86 passed, 0 failed.No test here. The out of bounds shared memory access that produces the bad offset also produces false data race reports, and a test for that belongs with the change that detects and reports it, which is the follow up discussed on the issue.