• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16template <typename CflowHandler>
17class CflowIterInstructionHandler {
18public:
19    CflowIterInstructionHandler(const uint8_t* pc, const uint8_t* from, const uint8_t* to, CflowHandler handler)
20        : inst_(pc, from, to), handler_(std::move(handler)) {
21    }
22
23    uint8_t GetPrimaryOpcode()
24    {
25        return inst_.GetPrimaryOpcode();
26    }
27
28    uint8_t GetSecondaryOpcode()
29    {
30        return inst_.GetSecondaryOpcode();
31    }
32
33    bool IsPrimaryOpcodeValid() const
34    {
35        return inst_.IsPrimaryOpcodeValid();
36    }
37
38% array_first_funcs_names = []
39% dispatch_table_hash = Hash.new()
40% Panda::instructions.uniq{|i| i.mnemonic}.each do |i|
41%   combination_flags = ""
42%   mnemonic = i.mnemonic.split('.').map { |p| p == '64' ? 'Wide' : p.capitalize }.join
43%   value_dispatch = %(template <BytecodeInstructionSafe::Format FORMAT> \n) +
44%                    %(std::optional<VerificationStatus> Handle) + mnemonic + %(())
45%   combination_flags += "Valid_"
46%   if !i.exceptions.include?('x_none')
47%       combination_flags += "Except_"
48%   else
49%       combination_flags += "ExceptNo_"
50%   end
51%   if i.properties.include?('jump')
52%       if i.properties.include?('conditional')
53%           combination_flags += "CondJump_"
54%       else
55%           combination_flags += "NoncondJump_"
56%       end
57%   elsif i.stripped_mnemonic == 'throw'
58%       combination_flags += "InstTypeThrow_"
59%   elsif i.properties.include?('return')
60%       combination_flags += "InstTypeReturn_"
61%   else
62%       combination_flags += "InstTypeNormal_"
63%   end
64%   if i.properties.include?('jump')
65%       combination_flags += "HasJump_"
66%   else
67%       combination_flags += "HasNoJump_"
68%   end
69%   combination_flags += "GetNext_"
70%   func_name = ("Handle_" + (combination_flags.sub("Valid_", "")).sub("GetNext_", "")).chomp("_")
71%   flag = dispatch_table_hash.include?(combination_flags)
72%   if flag == false
73%       dispatch_table_hash[combination_flags] = []
74%       array_first_funcs_names.push(func_name)
75%   end
76%   dispatch_table_hash[combination_flags].push(value_dispatch)
77% end
78%
79% body_gen_parts = Hash.new()
80% body_gen_parts = {
81%    "Valid_" => %(
82%        if (!inst_.IsValid()) {
83%            LOG(DEBUG, VERIFIER) << "Next instruction offset is out of bounds of method body.";
84%            return VerificationStatus::ERROR;
85%        }
86%        const uint8_t* pc = inst_.GetAddress();
87%        const size_t sz = inst_.Size(FORMAT);
88%    ),
89%    "Except_" => %(
90%        constexpr bool EXC_SRC = true;
91%    ),
92%    "ExceptNo_" => %(
93%        constexpr bool  EXC_SRC = false;
94%    ),
95%    "CondJump_" => %(
96%        const InstructionType instType = InstructionType::COND_JUMP;
97%        LOG_INST();
98%    ),
99%    "NoncondJump_" => %(
100%        const InstructionType instType = InstructionType::JUMP;
101%        LOG_INST();
102%    ),
103%    "InstTypeThrow_" => %(
104%        const InstructionType instType = InstructionType::THROW;
105%        LOG_INST();
106%    ),
107%    "InstTypeReturn_" => %(
108%        const InstructionType instType = InstructionType::RETURN;
109%        LOG_INST();
110%    ),
111%    "InstTypeNormal_" => %(
112%        const InstructionType instType = InstructionType::NORMAL;
113%        LOG_INST() << (EXC_SRC ? " (exception source)" : "" );
114%    ),
115%    "HasJump_" => %(
116%        auto imm = inst_.GetImm<FORMAT>();
117%        if (!inst_.IsValid()) {
118%            LOG(ERROR, VERIFIER) << "Jump instruction imm field is out of bounds of method body. "
119%                                 << "Jump instruction offset: " << std::hex << static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc) - reinterpret_cast<uintptr_t>(inst_.GetFrom()));
120%            return VerificationStatus::ERROR;
121%        }
122%        auto targetInst = inst_.JumpTo(imm);
123%        const uint8_t* jumpPc = targetInst.GetAddress();
124%        if (!targetInst.IsValid()) {
125%            auto offset = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc) - reinterpret_cast<uintptr_t>(inst_.GetFrom()));
126%            LOG(ERROR, VERIFIER) << "Jump offset is out of bounds of method body. "
127%                                 << "Jump instruction offset: 0x" << std::hex << offset
128%                                 << ". Jump target offset: 0x" << std::hex << (imm + offset);
129%            return VerificationStatus::ERROR;
130%        }
131%    ),
132%    "HasNoJump_" => %(
133%        const uint8_t* jumpPc = nullptr;
134%    ),
135%    "GetNext_" => %(
136%        inst_ = inst_.GetNext<FORMAT>();
137%        return handler_(instType, pc, sz, EXC_SRC, jumpPc);
138%    )
139% }
140%
141% full_code_hash = Hash.new("")
142% dispatch_table_hash.each { |key_comb, value_comb|
143%    body_gen_parts.each { |key_parts, value_parts|
144%        string_to_compare_with = key_comb.to_s
145%        flag_compare = string_to_compare_with.include?(key_parts.to_s)
146%        if flag_compare == true
147%            full_code_hash[key_comb] += value_parts
148%        end
149%    }
150% }
151%
152%
153% array_first_funcs_names.zip(full_code_hash.values).each { |func_name, code|
154    template <BytecodeInstructionSafe::Format FORMAT>
155    // NOLINTNEXTLINE(readability-identifier-naming)
156    std::optional<VerificationStatus> <%= func_name %>() {
157    <%= code%>
158    }
159%}
160private:
161    BytecodeInstructionSafe inst_;
162    CflowHandler handler_;
163};
164
165template <typename CflowHandler>
166// NOLINTNEXTLINE(readability-function-size)
167VerificationStatus IterateOverInstructions(const uint8_t* pc, const uint8_t* from, const uint8_t* to, CflowHandler cflowHandler) {
168#if defined(__clang__)
169#pragma clang diagnostic push
170#pragma clang diagnostic ignored "-Wvoid-ptr-dereference"
171#pragma clang diagnostic ignored "-Wgnu-label-as-value"
172#elif defined(__GNUC__)
173#pragma GCC diagnostic push
174#pragma GCC diagnostic ignored "-Wpedantic"
175#endif
176
177    // NOLINTNEXTLINE(readability-magic-numbers)
178    std::array<const void*, <%= Panda::dispatch_table.handler_names.size %>> dispatchTable{
179% Panda::dispatch_table.handler_names.each do |name|
180        &&HANDLE_<%= name %>,
181% end
182    };
183
184    CflowIterInstructionHandler handler(pc, from, to, std::move(cflowHandler));
185    std::optional<VerificationStatus> status;
186    uint8_t secondaryOpcode;
187
188    if (!handler.IsPrimaryOpcodeValid()) {
189        LOG(ERROR, VERIFIER) << "Incorrect opcode";
190        return VerificationStatus::ERROR;
191    }
192    // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto)
193    goto* dispatchTable[handler.GetPrimaryOpcode()];
194
195% hash_handle_variants = Hash.new()
196% array_to_paste_zip = array_first_funcs_names.zip(dispatch_table_hash.values)
197% Panda::instructions.each do |i|
198%   mnemonic = i.mnemonic.split('.').map { |p| p == '64' ? 'Wide' : p.capitalize }.join
199%   array_to_paste_zip.each { | func, name_array|
200%   flag = false
201%   name_to_past = ""
202%   name_array.each { |item |
203%       flag = item.include?("Handle" + mnemonic.to_s)
204%       if flag == true
205%           name_to_past = func
206%           break
207%       end
208%   }
209%   if flag == true
210%       status = "handler.template " + name_to_past + %(<BytecodeInstructionSafe::Format::) + i.format.pretty.upcase + %(>();)
211%       flag_array_handle_variants = hash_handle_variants.include?(status)
212%       if flag_array_handle_variants == false
213%           hash_handle_variants[status] = []
214%       end
215%       hash_handle_variants[status].push("HANDLE_" + i.handler_name)
216%       break
217%   end
218% }
219% end
220% hash_handle_variants.each { |key_status, labels_arr|
221%   labels_arr.each { |item|
222<%= item%>:;
223%   }
224    status = <%= key_status%>
225    // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto)
226    goto COMMON_BODY;
227% }
228
229COMMON_BODY:
230{
231    if (status) { return *status; }
232    if (!handler.IsPrimaryOpcodeValid()) {
233        LOG(DEBUG, VERIFIER) << "Opcode value is out of range. "
234                             << "Current value is: " << static_cast<int>(handler.GetPrimaryOpcode())
235                             << ". Allowed value is in the interval: [0, <%= Panda::dispatch_table.invalid_non_prefixed_interval.min - 1 %>] U "
236                             << "[<%= Panda::dispatch_table.invalid_non_prefixed_interval.max + 1 %>, <%= Panda::dispatch_table.invalid_prefixes_interval.min + 1 %>] U "
237                             << "[<%= Panda::dispatch_table.invalid_prefixes_interval.max + 1 %>, 255]";
238        return VerificationStatus::ERROR;
239    }
240    // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto)
241    goto* dispatchTable[handler.GetPrimaryOpcode()];
242}
243HANDLE_INVALID:
244    LOG(DEBUG, VERIFIER) << "Opcode value is out of range. "
245                         << "Current value is: " << static_cast<int>(handler.GetPrimaryOpcode())
246                         << ". Allowed value is in the interval: [0, <%= Panda::dispatch_table.invalid_non_prefixed_interval.min - 1 %>] U "
247                         << "[<%= Panda::dispatch_table.invalid_non_prefixed_interval.max + 1 %>, <%= Panda::dispatch_table.invalid_prefixes_interval.min + 1 %>] U "
248                         << "[<%= Panda::dispatch_table.invalid_prefixes_interval.max + 1 %>, 255]";
249    return VerificationStatus::ERROR;
250% Panda::prefixes.each do |p|
251HANDLE_<%= p.handler_name %>:
252    secondaryOpcode = handler.GetSecondaryOpcode();
253    LOG(DEBUG, VERIFIER) << "CFLOW: Prefix subdispatch: " << "<%= p.name %>, " << static_cast<int32_t>(secondaryOpcode);
254
255    // NOLINTNEXTLINE(readability-magic-numbers)
256    if (secondaryOpcode > <%= Panda::dispatch_table.secondary_opcode_bound(p) %> ) {
257        LOG(ERROR, VERIFIER) << "Incorrect opcode";
258        return VerificationStatus::ERROR;
259    }
260    // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto,readability-magic-numbers)
261    goto *dispatchTable[<%= Panda::dispatch_table.secondary_opcode_offset(p) %> + secondaryOpcode];
262% end
263
264#if defined(__clang__)
265#pragma clang diagnostic pop
266#elif defined(__GNUC__)
267#pragma GCC diagnostic pop
268#endif
269}
270