• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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 #include "indirect_reference_table.h"
18 #include "jni_internal.h"
19 #include "reference_table.h"
20 #include "runtime.h"
21 #include "scoped_thread_state_change.h"
22 #include "thread.h"
23 #include "utils.h"
24 
25 #include <cstdlib>
26 
27 namespace art {
28 
AbortMaybe()29 static void AbortMaybe() {
30   // If -Xcheck:jni is on, it'll give a more detailed error before aborting.
31   if (!Runtime::Current()->GetJavaVM()->check_jni) {
32     // Otherwise, we want to abort rather than hand back a bad reference.
33     LOG(FATAL) << "JNI ERROR (app bug): see above.";
34   }
35 }
36 
IndirectReferenceTable(size_t initialCount,size_t maxCount,IndirectRefKind desiredKind)37 IndirectReferenceTable::IndirectReferenceTable(size_t initialCount,
38                                                size_t maxCount, IndirectRefKind desiredKind) {
39   CHECK_GT(initialCount, 0U);
40   CHECK_LE(initialCount, maxCount);
41   CHECK_NE(desiredKind, kSirtOrInvalid);
42 
43   table_ = reinterpret_cast<const mirror::Object**>(malloc(initialCount * sizeof(const mirror::Object*)));
44   CHECK(table_ != NULL);
45   memset(table_, 0xd1, initialCount * sizeof(const mirror::Object*));
46 
47   slot_data_ = reinterpret_cast<IndirectRefSlot*>(calloc(initialCount, sizeof(IndirectRefSlot)));
48   CHECK(slot_data_ != NULL);
49 
50   segment_state_.all = IRT_FIRST_SEGMENT;
51   alloc_entries_ = initialCount;
52   max_entries_ = maxCount;
53   kind_ = desiredKind;
54 }
55 
~IndirectReferenceTable()56 IndirectReferenceTable::~IndirectReferenceTable() {
57   free(table_);
58   free(slot_data_);
59   table_ = NULL;
60   slot_data_ = NULL;
61   alloc_entries_ = max_entries_ = -1;
62 }
63 
64 // Make sure that the entry at "idx" is correctly paired with "iref".
CheckEntry(const char * what,IndirectRef iref,int idx) const65 bool IndirectReferenceTable::CheckEntry(const char* what, IndirectRef iref, int idx) const {
66   const mirror::Object* obj = table_[idx];
67   IndirectRef checkRef = ToIndirectRef(obj, idx);
68   if (UNLIKELY(checkRef != iref)) {
69     LOG(ERROR) << "JNI ERROR (app bug): attempt to " << what
70                << " stale " << kind_ << " " << iref
71                << " (should be " << checkRef << ")";
72     AbortMaybe();
73     return false;
74   }
75   return true;
76 }
77 
Add(uint32_t cookie,const mirror::Object * obj)78 IndirectRef IndirectReferenceTable::Add(uint32_t cookie, const mirror::Object* obj) {
79   IRTSegmentState prevState;
80   prevState.all = cookie;
81   size_t topIndex = segment_state_.parts.topIndex;
82 
83   DCHECK(obj != NULL);
84   // TODO: stronger sanity check on the object (such as in heap)
85   DCHECK_ALIGNED(reinterpret_cast<uintptr_t>(obj), 8);
86   DCHECK(table_ != NULL);
87   DCHECK_LE(alloc_entries_, max_entries_);
88   DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
89 
90   if (topIndex == alloc_entries_) {
91     // reached end of allocated space; did we hit buffer max?
92     if (topIndex == max_entries_) {
93       LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow "
94                  << "(max=" << max_entries_ << ")\n"
95                  << MutatorLockedDumpable<IndirectReferenceTable>(*this);
96     }
97 
98     size_t newSize = alloc_entries_ * 2;
99     if (newSize > max_entries_) {
100       newSize = max_entries_;
101     }
102     DCHECK_GT(newSize, alloc_entries_);
103 
104     table_ = reinterpret_cast<const mirror::Object**>(realloc(table_, newSize * sizeof(const mirror::Object*)));
105     slot_data_ = reinterpret_cast<IndirectRefSlot*>(realloc(slot_data_,
106                                                             newSize * sizeof(IndirectRefSlot)));
107     if (table_ == NULL || slot_data_ == NULL) {
108       LOG(FATAL) << "JNI ERROR (app bug): unable to expand "
109                  << kind_ << " table (from "
110                  << alloc_entries_ << " to " << newSize
111                  << ", max=" << max_entries_ << ")\n"
112                  << MutatorLockedDumpable<IndirectReferenceTable>(*this);
113     }
114 
115     // Clear the newly-allocated slot_data_ elements.
116     memset(slot_data_ + alloc_entries_, 0, (newSize - alloc_entries_) * sizeof(IndirectRefSlot));
117 
118     alloc_entries_ = newSize;
119   }
120 
121   // We know there's enough room in the table.  Now we just need to find
122   // the right spot.  If there's a hole, find it and fill it; otherwise,
123   // add to the end of the list.
124   IndirectRef result;
125   int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles;
126   if (numHoles > 0) {
127     DCHECK_GT(topIndex, 1U);
128     // Find the first hole; likely to be near the end of the list.
129     const mirror::Object** pScan = &table_[topIndex - 1];
130     DCHECK(*pScan != NULL);
131     while (*--pScan != NULL) {
132       DCHECK_GE(pScan, table_ + prevState.parts.topIndex);
133     }
134     UpdateSlotAdd(obj, pScan - table_);
135     result = ToIndirectRef(obj, pScan - table_);
136     *pScan = obj;
137     segment_state_.parts.numHoles--;
138   } else {
139     // Add to the end.
140     UpdateSlotAdd(obj, topIndex);
141     result = ToIndirectRef(obj, topIndex);
142     table_[topIndex++] = obj;
143     segment_state_.parts.topIndex = topIndex;
144   }
145   if (false) {
146     LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.parts.topIndex
147               << " holes=" << segment_state_.parts.numHoles;
148   }
149 
150   DCHECK(result != NULL);
151   return result;
152 }
153 
AssertEmpty()154 void IndirectReferenceTable::AssertEmpty() {
155   if (UNLIKELY(begin() != end())) {
156     ScopedObjectAccess soa(Thread::Current());
157     LOG(FATAL) << "Internal Error: non-empty local reference table\n"
158                << MutatorLockedDumpable<IndirectReferenceTable>(*this);
159   }
160 }
161 
162 // Verifies that the indirect table lookup is valid.
163 // Returns "false" if something looks bad.
GetChecked(IndirectRef iref) const164 bool IndirectReferenceTable::GetChecked(IndirectRef iref) const {
165   if (UNLIKELY(iref == NULL)) {
166     LOG(WARNING) << "Attempt to look up NULL " << kind_;
167     return false;
168   }
169   if (UNLIKELY(GetIndirectRefKind(iref) == kSirtOrInvalid)) {
170     LOG(ERROR) << "JNI ERROR (app bug): invalid " << kind_ << " " << iref;
171     AbortMaybe();
172     return false;
173   }
174 
175   int topIndex = segment_state_.parts.topIndex;
176   int idx = ExtractIndex(iref);
177   if (UNLIKELY(idx >= topIndex)) {
178     LOG(ERROR) << "JNI ERROR (app bug): accessed stale " << kind_ << " "
179                << iref << " (index " << idx << " in a table of size " << topIndex << ")";
180     AbortMaybe();
181     return false;
182   }
183 
184   if (UNLIKELY(table_[idx] == NULL)) {
185     LOG(ERROR) << "JNI ERROR (app bug): accessed deleted " << kind_ << " " << iref;
186     AbortMaybe();
187     return false;
188   }
189 
190   if (UNLIKELY(!CheckEntry("use", iref, idx))) {
191     return false;
192   }
193 
194   return true;
195 }
196 
Find(mirror::Object * direct_pointer,int bottomIndex,int topIndex,const mirror::Object ** table)197 static int Find(mirror::Object* direct_pointer, int bottomIndex, int topIndex, const mirror::Object** table) {
198   for (int i = bottomIndex; i < topIndex; ++i) {
199     if (table[i] == direct_pointer) {
200       return i;
201     }
202   }
203   return -1;
204 }
205 
ContainsDirectPointer(mirror::Object * direct_pointer) const206 bool IndirectReferenceTable::ContainsDirectPointer(mirror::Object* direct_pointer) const {
207   return Find(direct_pointer, 0, segment_state_.parts.topIndex, table_) != -1;
208 }
209 
210 // Removes an object. We extract the table offset bits from "iref"
211 // and zap the corresponding entry, leaving a hole if it's not at the top.
212 // If the entry is not between the current top index and the bottom index
213 // specified by the cookie, we don't remove anything. This is the behavior
214 // required by JNI's DeleteLocalRef function.
215 // This method is not called when a local frame is popped; this is only used
216 // for explicit single removals.
217 // Returns "false" if nothing was removed.
Remove(uint32_t cookie,IndirectRef iref)218 bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) {
219   IRTSegmentState prevState;
220   prevState.all = cookie;
221   int topIndex = segment_state_.parts.topIndex;
222   int bottomIndex = prevState.parts.topIndex;
223 
224   DCHECK(table_ != NULL);
225   DCHECK_LE(alloc_entries_, max_entries_);
226   DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
227 
228   int idx = ExtractIndex(iref);
229 
230   JavaVMExt* vm = Runtime::Current()->GetJavaVM();
231   if (GetIndirectRefKind(iref) == kSirtOrInvalid &&
232       Thread::Current()->SirtContains(reinterpret_cast<jobject>(iref))) {
233     LOG(WARNING) << "Attempt to remove local SIRT entry from IRT, ignoring";
234     return true;
235   }
236   if (GetIndirectRefKind(iref) == kSirtOrInvalid && vm->work_around_app_jni_bugs) {
237     mirror::Object* direct_pointer = reinterpret_cast<mirror::Object*>(iref);
238     idx = Find(direct_pointer, bottomIndex, topIndex, table_);
239     if (idx == -1) {
240       LOG(WARNING) << "Trying to work around app JNI bugs, but didn't find " << iref << " in table!";
241       return false;
242     }
243   }
244 
245   if (idx < bottomIndex) {
246     // Wrong segment.
247     LOG(WARNING) << "Attempt to remove index outside index area (" << idx
248                  << " vs " << bottomIndex << "-" << topIndex << ")";
249     return false;
250   }
251   if (idx >= topIndex) {
252     // Bad --- stale reference?
253     LOG(WARNING) << "Attempt to remove invalid index " << idx
254                  << " (bottom=" << bottomIndex << " top=" << topIndex << ")";
255     return false;
256   }
257 
258   if (idx == topIndex-1) {
259     // Top-most entry.  Scan up and consume holes.
260 
261     if (!vm->work_around_app_jni_bugs && !CheckEntry("remove", iref, idx)) {
262       return false;
263     }
264 
265     table_[idx] = NULL;
266     int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles;
267     if (numHoles != 0) {
268       while (--topIndex > bottomIndex && numHoles != 0) {
269         if (false) {
270           LOG(INFO) << "+++ checking for hole at " << topIndex-1
271                     << " (cookie=" << cookie << ") val=" << table_[topIndex - 1];
272         }
273         if (table_[topIndex-1] != NULL) {
274           break;
275         }
276         if (false) {
277           LOG(INFO) << "+++ ate hole at " << (topIndex - 1);
278         }
279         numHoles--;
280       }
281       segment_state_.parts.numHoles = numHoles + prevState.parts.numHoles;
282       segment_state_.parts.topIndex = topIndex;
283     } else {
284       segment_state_.parts.topIndex = topIndex-1;
285       if (false) {
286         LOG(INFO) << "+++ ate last entry " << topIndex - 1;
287       }
288     }
289   } else {
290     // Not the top-most entry.  This creates a hole.  We NULL out the
291     // entry to prevent somebody from deleting it twice and screwing up
292     // the hole count.
293     if (table_[idx] == NULL) {
294       LOG(INFO) << "--- WEIRD: removing null entry " << idx;
295       return false;
296     }
297     if (!vm->work_around_app_jni_bugs && !CheckEntry("remove", iref, idx)) {
298       return false;
299     }
300 
301     table_[idx] = NULL;
302     segment_state_.parts.numHoles++;
303     if (false) {
304       LOG(INFO) << "+++ left hole at " << idx << ", holes=" << segment_state_.parts.numHoles;
305     }
306   }
307 
308   return true;
309 }
310 
VisitRoots(RootVisitor * visitor,void * arg)311 void IndirectReferenceTable::VisitRoots(RootVisitor* visitor, void* arg) {
312   for (auto ref : *this) {
313     visitor(*ref, arg);
314   }
315 }
316 
Dump(std::ostream & os) const317 void IndirectReferenceTable::Dump(std::ostream& os) const {
318   os << kind_ << " table dump:\n";
319   std::vector<const mirror::Object*> entries(table_, table_ + Capacity());
320   // Remove NULLs.
321   for (int i = entries.size() - 1; i >= 0; --i) {
322     if (entries[i] == NULL) {
323       entries.erase(entries.begin() + i);
324     }
325   }
326   ReferenceTable::Dump(os, entries);
327 }
328 
329 }  // namespace art
330