1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_ITERATOR_H_
18 #define ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_ITERATOR_H_
19 
20 #include <iterator>
21 
22 #include <android-base/logging.h>
23 
24 #include "base/macros.h"
25 #include "dex_instruction.h"
26 
27 namespace art {
28 
29 class DexInstructionPcPair {
30  public:
Inst()31   ALWAYS_INLINE const Instruction& Inst() const {
32     return *Instruction::At(instructions_ + DexPc());
33   }
34 
35   ALWAYS_INLINE const Instruction* operator->() const {
36     return &Inst();
37   }
38 
DexPc()39   ALWAYS_INLINE uint32_t DexPc() const {
40     return dex_pc_;
41   }
42 
Instructions()43   ALWAYS_INLINE const uint16_t* Instructions() const {
44     return instructions_;
45   }
46 
47  protected:
DexInstructionPcPair(const uint16_t * instructions,uint32_t dex_pc)48   explicit DexInstructionPcPair(const uint16_t* instructions, uint32_t dex_pc)
49       : instructions_(instructions), dex_pc_(dex_pc) {}
50 
51   const uint16_t* instructions_ = nullptr;
52   uint32_t dex_pc_ = 0;
53 
54   friend class DexInstructionIteratorBase;
55   friend class DexInstructionIterator;
56   friend class SafeDexInstructionIterator;
57 };
58 
59 // Base helper class to prevent duplicated comparators.
60 class DexInstructionIteratorBase {
61  public:
62   using iterator_category = std::forward_iterator_tag;
63   using value_type = DexInstructionPcPair;
64   using difference_type = ptrdiff_t;
65   using pointer = void;
66   using reference = void;
67 
DexInstructionIteratorBase(const Instruction * inst,uint32_t dex_pc)68   explicit DexInstructionIteratorBase(const Instruction* inst, uint32_t dex_pc)
69       : data_(reinterpret_cast<const uint16_t*>(inst), dex_pc) {}
70 
Inst()71   const Instruction& Inst() const {
72     return data_.Inst();
73   }
74 
75   // Return the dex pc for an iterator compared to the code item begin.
DexPc()76   ALWAYS_INLINE uint32_t DexPc() const {
77     return data_.DexPc();
78   }
79 
80   // Instructions from the start of the code item.
Instructions()81   ALWAYS_INLINE const uint16_t* Instructions() const {
82     return data_.Instructions();
83   }
84 
85  protected:
86   DexInstructionPcPair data_;
87 };
88 
89 static ALWAYS_INLINE inline bool operator==(const DexInstructionIteratorBase& lhs,
90                                             const DexInstructionIteratorBase& rhs) {
91   DCHECK_EQ(lhs.Instructions(), rhs.Instructions()) << "Comparing different code items.";
92   return lhs.DexPc() == rhs.DexPc();
93 }
94 
95 static inline bool operator!=(const DexInstructionIteratorBase& lhs,
96                               const DexInstructionIteratorBase& rhs) {
97   return !(lhs == rhs);
98 }
99 
100 static inline bool operator<(const DexInstructionIteratorBase& lhs,
101                              const DexInstructionIteratorBase& rhs) {
102   DCHECK_EQ(lhs.Instructions(), rhs.Instructions()) << "Comparing different code items.";
103   return lhs.DexPc() < rhs.DexPc();
104 }
105 
106 static inline bool operator>(const DexInstructionIteratorBase& lhs,
107                              const DexInstructionIteratorBase& rhs) {
108   return rhs < lhs;
109 }
110 
111 static inline bool operator<=(const DexInstructionIteratorBase& lhs,
112                               const DexInstructionIteratorBase& rhs) {
113   return !(rhs < lhs);
114 }
115 
116 static inline bool operator>=(const DexInstructionIteratorBase& lhs,
117                               const DexInstructionIteratorBase& rhs) {
118   return !(lhs < rhs);
119 }
120 
121 // A helper class for a code_item's instructions using range based for loop syntax.
122 class DexInstructionIterator : public DexInstructionIteratorBase {
123  public:
124   using DexInstructionIteratorBase::DexInstructionIteratorBase;
125 
DexInstructionIterator(const uint16_t * inst,uint32_t dex_pc)126   explicit DexInstructionIterator(const uint16_t* inst, uint32_t dex_pc)
127       : DexInstructionIteratorBase(inst != nullptr ? Instruction::At(inst) : nullptr, dex_pc) {}
128 
DexInstructionIterator(const DexInstructionPcPair & pair)129   explicit DexInstructionIterator(const DexInstructionPcPair& pair)
130       : DexInstructionIterator(pair.Instructions(), pair.DexPc()) {}
131 
132   // Value after modification.
133   DexInstructionIterator& operator++() {
134     data_.dex_pc_ += Inst().SizeInCodeUnits();
135     return *this;
136   }
137 
138   // Value before modification.
139   DexInstructionIterator operator++(int) {
140     DexInstructionIterator temp = *this;
141     ++*this;
142     return temp;
143   }
144 
145   const value_type& operator*() const {
146     return data_;
147   }
148 
149   const Instruction* operator->() const {
150     return &data_.Inst();
151   }
152 
153   // Return the dex pc for the iterator.
DexPc()154   ALWAYS_INLINE uint32_t DexPc() const {
155     return data_.DexPc();
156   }
157 };
158 
159 // A safe version of DexInstructionIterator that is guaranteed to not go past the end of the code
160 // item.
161 class SafeDexInstructionIterator : public DexInstructionIteratorBase {
162  public:
SafeDexInstructionIterator(const DexInstructionIteratorBase & start,const DexInstructionIteratorBase & end)163   explicit SafeDexInstructionIterator(const DexInstructionIteratorBase& start,
164                                       const DexInstructionIteratorBase& end)
165       : DexInstructionIteratorBase(&start.Inst(), start.DexPc())
166       , num_code_units_(end.DexPc()) {
167     DCHECK_EQ(start.Instructions(), end.Instructions())
168         << "start and end must be in the same code item.";
169   }
170 
171   // Value after modification, does not read past the end of the allowed region. May increment past
172   // the end of the code item though.
173   SafeDexInstructionIterator& operator++() {
174     AssertValid();
175     const size_t size_code_units = Inst().CodeUnitsRequiredForSizeComputation();
176     const size_t available = NumCodeUnits() - DexPc();
177     if (UNLIKELY(size_code_units > available)) {
178       error_state_ = true;
179       return *this;
180     }
181     const size_t instruction_code_units = Inst().SizeInCodeUnits();
182     if (UNLIKELY(instruction_code_units > available)) {
183       error_state_ = true;
184       return *this;
185     }
186     data_.dex_pc_ += instruction_code_units;
187     return *this;
188   }
189 
190   // Value before modification.
191   SafeDexInstructionIterator operator++(int) {
192     SafeDexInstructionIterator temp = *this;
193     ++*this;
194     return temp;
195   }
196 
197   const value_type& operator*() const {
198     AssertValid();
199     return data_;
200   }
201 
202   const Instruction* operator->() const {
203     AssertValid();
204     return &data_.Inst();
205   }
206 
207   // Return the current instruction of the iterator.
Inst()208   ALWAYS_INLINE const Instruction& Inst() const {
209     return data_.Inst();
210   }
211 
Instructions()212   const uint16_t* Instructions() const {
213     return data_.Instructions();
214   }
215 
216   // Returns true if the iterator is in an error state. This occurs when an instruction couldn't
217   // have its size computed without reading past the end iterator.
IsErrorState()218   bool IsErrorState() const {
219     return error_state_;
220   }
221 
222  private:
AssertValid()223   ALWAYS_INLINE void AssertValid() const {
224     DCHECK(!IsErrorState());
225     DCHECK_LT(DexPc(), NumCodeUnits());
226   }
227 
NumCodeUnits()228   ALWAYS_INLINE uint32_t NumCodeUnits() const {
229     return num_code_units_;
230   }
231 
232   const uint32_t num_code_units_ = 0;
233   bool error_state_ = false;
234 };
235 
236 }  // namespace art
237 
238 #endif  // ART_LIBDEXFILE_DEX_DEX_INSTRUCTION_ITERATOR_H_
239