1 // Copyright (c) 2015-2016 The Khronos Group Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a 4 // copy of this software and/or associated documentation files (the 5 // "Materials"), to deal in the Materials without restriction, including 6 // without limitation the rights to use, copy, modify, merge, publish, 7 // distribute, sublicense, and/or sell copies of the Materials, and to 8 // permit persons to whom the Materials are furnished to do so, subject to 9 // the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included 12 // in all copies or substantial portions of the Materials. 13 // 14 // MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS 15 // KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS 16 // SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT 17 // https://www.khronos.org/registry/ 18 // 19 // THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 26 27 #include "validate.h" 28 29 #include <cassert> 30 #include <cstdio> 31 32 #include <algorithm> 33 #include <functional> 34 #include <iterator> 35 #include <sstream> 36 #include <string> 37 #include <vector> 38 39 #include "binary.h" 40 #include "diagnostic.h" 41 #include "instruction.h" 42 #include "opcode.h" 43 #include "operand.h" 44 #include "spirv-tools/libspirv.h" 45 #include "spirv_constant.h" 46 #include "spirv_endian.h" 47 #include "val/Construct.h" 48 #include "val/Function.h" 49 #include "val/ValidationState.h" 50 51 using std::function; 52 using std::ostream_iterator; 53 using std::placeholders::_1; 54 using std::string; 55 using std::stringstream; 56 using std::transform; 57 using std::vector; 58 59 using libspirv::CfgPass; 60 using libspirv::InstructionPass; 61 using libspirv::ModuleLayoutPass; 62 using libspirv::SsaPass; 63 using libspirv::ValidationState_t; 64 65 spv_result_t spvValidateIDs( 66 const spv_instruction_t* pInsts, const uint64_t count, 67 const spv_opcode_table opcodeTable, const spv_operand_table operandTable, 68 const spv_ext_inst_table extInstTable, const ValidationState_t& state, 69 spv_position position, spv_diagnostic* pDiagnostic) { 70 auto undefd = state.usedefs().FindUsesWithoutDefs(); 71 for (auto id : undefd) { 72 DIAGNOSTIC << "Undefined ID: " << id; 73 } 74 position->index = SPV_INDEX_INSTRUCTION; 75 spvCheckReturn(spvValidateInstructionIDs(pInsts, count, opcodeTable, 76 operandTable, extInstTable, state, 77 position, pDiagnostic)); 78 return undefd.empty() ? SPV_SUCCESS : SPV_ERROR_INVALID_ID; 79 } 80 81 namespace { 82 83 // TODO(umar): Validate header 84 // TODO(umar): The Id bound should be validated also. But you can only do that 85 // after you've seen all the instructions in the module. 86 // TODO(umar): The binary parser validates the magic word, and the length of the 87 // header, but nothing else. 88 spv_result_t setHeader(void* user_data, spv_endianness_t endian, uint32_t magic, 89 uint32_t version, uint32_t generator, uint32_t id_bound, 90 uint32_t reserved) { 91 (void)user_data; 92 (void)endian; 93 (void)magic; 94 (void)version; 95 (void)generator; 96 (void)id_bound; 97 (void)reserved; 98 return SPV_SUCCESS; 99 } 100 101 // Improves diagnostic messages by collecting names of IDs 102 // NOTE: This function returns void and is not involved in validation 103 void DebugInstructionPass(ValidationState_t& _, 104 const spv_parsed_instruction_t* inst) { 105 switch (inst->opcode) { 106 case SpvOpName: { 107 const uint32_t target = *(inst->words + inst->operands[0].offset); 108 const char* str = 109 reinterpret_cast<const char*>(inst->words + inst->operands[1].offset); 110 _.AssignNameToId(target, str); 111 } break; 112 case SpvOpMemberName: { 113 const uint32_t target = *(inst->words + inst->operands[0].offset); 114 const char* str = 115 reinterpret_cast<const char*>(inst->words + inst->operands[2].offset); 116 _.AssignNameToId(target, str); 117 } break; 118 case SpvOpSourceContinued: 119 case SpvOpSource: 120 case SpvOpSourceExtension: 121 case SpvOpString: 122 case SpvOpLine: 123 case SpvOpNoLine: 124 125 default: 126 break; 127 } 128 } 129 130 // Collects use-def info about an instruction's IDs. 131 void ProcessIds(ValidationState_t& _, const spv_parsed_instruction_t& inst) { 132 if (inst.result_id) { 133 _.usedefs().AddDef( 134 {inst.result_id, inst.type_id, static_cast<SpvOp>(inst.opcode), 135 std::vector<uint32_t>(inst.words, inst.words + inst.num_words)}); 136 } 137 for (auto op = inst.operands; op != inst.operands + inst.num_operands; ++op) { 138 if (spvIsIdType(op->type)) _.usedefs().AddUse(inst.words[op->offset]); 139 } 140 } 141 142 spv_result_t ProcessInstruction(void* user_data, 143 const spv_parsed_instruction_t* inst) { 144 ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data)); 145 _.increment_instruction_count(); 146 if (static_cast<SpvOp>(inst->opcode) == SpvOpEntryPoint) 147 _.entry_points().push_back(inst->words[2]); 148 149 DebugInstructionPass(_, inst); 150 // TODO(umar): Perform data rules pass 151 ProcessIds(_, *inst); 152 spvCheckReturn(ModuleLayoutPass(_, inst)); 153 spvCheckReturn(CfgPass(_, inst)); 154 spvCheckReturn(SsaPass(_, inst)); 155 spvCheckReturn(InstructionPass(_, inst)); 156 157 return SPV_SUCCESS; 158 } 159 160 } // anonymous namespace 161 162 spv_result_t spvValidate(const spv_const_context context, 163 const spv_const_binary binary, 164 spv_diagnostic* pDiagnostic) { 165 if (!pDiagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC; 166 167 spv_endianness_t endian; 168 spv_position_t position = {}; 169 if (spvBinaryEndianness(binary, &endian)) { 170 DIAGNOSTIC << "Invalid SPIR-V magic number."; 171 return SPV_ERROR_INVALID_BINARY; 172 } 173 174 spv_header_t header; 175 if (spvBinaryHeaderGet(binary, endian, &header)) { 176 DIAGNOSTIC << "Invalid SPIR-V header."; 177 return SPV_ERROR_INVALID_BINARY; 178 } 179 180 // NOTE: Parse the module and perform inline validation checks. These 181 // checks do not require the the knowledge of the whole module. 182 ValidationState_t vstate(pDiagnostic, context); 183 spvCheckReturn(spvBinaryParse(context, &vstate, binary->code, 184 binary->wordCount, setHeader, 185 ProcessInstruction, pDiagnostic)); 186 187 if (vstate.in_function_body()) 188 return vstate.diag(SPV_ERROR_INVALID_LAYOUT) 189 << "Missing OpFunctionEnd at end of module."; 190 191 // TODO(umar): Add validation checks which require the parsing of the entire 192 // module. Use the information from the ProcessInstruction pass to make the 193 // checks. 194 if (vstate.unresolved_forward_id_count() > 0) { 195 stringstream ss; 196 vector<uint32_t> ids = vstate.UnresolvedForwardIds(); 197 198 transform(begin(ids), end(ids), ostream_iterator<string>(ss, " "), 199 bind(&ValidationState_t::getIdName, vstate, _1)); 200 201 auto id_str = ss.str(); 202 return vstate.diag(SPV_ERROR_INVALID_ID) 203 << "The following forward referenced IDs have not be defined:\n" 204 << id_str.substr(0, id_str.size() - 1); 205 } 206 207 // CFG checks are performed after the binary has been parsed 208 // and the CFGPass has collected information about the control flow 209 spvCheckReturn(PerformCfgChecks(vstate)); 210 211 // NOTE: Copy each instruction for easier processing 212 std::vector<spv_instruction_t> instructions; 213 uint64_t index = SPV_INDEX_INSTRUCTION; 214 while (index < binary->wordCount) { 215 uint16_t wordCount; 216 uint16_t opcode; 217 spvOpcodeSplit(spvFixWord(binary->code[index], endian), &wordCount, 218 &opcode); 219 spv_instruction_t inst; 220 spvInstructionCopy(&binary->code[index], static_cast<SpvOp>(opcode), 221 wordCount, endian, &inst); 222 instructions.push_back(inst); 223 index += wordCount; 224 } 225 226 position.index = SPV_INDEX_INSTRUCTION; 227 spvCheckReturn(spvValidateIDs(instructions.data(), instructions.size(), 228 context->opcode_table, context->operand_table, 229 context->ext_inst_table, vstate, &position, 230 pDiagnostic)); 231 232 return SPV_SUCCESS; 233 } 234