lin
2025-07-31 065ea569db06206874bbfa18eb25ff6121aec09b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#ifndef ART_RUNTIME_VERIFIER_REGISTER_LINE_INL_H_
#define ART_RUNTIME_VERIFIER_REGISTER_LINE_INL_H_
 
#include "register_line.h"
 
#include "base/logging.h"  // For VLOG.
#include "debug_print.h"
#include "method_verifier.h"
#include "reg_type_cache-inl.h"
 
namespace art {
namespace verifier {
 
// Should we dump a warning on failures to verify balanced locking? That would be an indication to
// developers that their code will be slow.
static constexpr bool kDumpLockFailures = true;
 
inline const RegType& RegisterLine::GetRegisterType(MethodVerifier* verifier, uint32_t vsrc) const {
  // The register index was validated during the static pass, so we don't need to check it here.
  DCHECK_LT(vsrc, num_regs_);
  return verifier->GetRegTypeCache()->GetFromId(line_[vsrc]);
}
 
template <LockOp kLockOp>
inline bool RegisterLine::SetRegisterType(MethodVerifier* verifier, uint32_t vdst,
                                          const RegType& new_type) {
  DCHECK_LT(vdst, num_regs_);
  if (new_type.IsLowHalf() || new_type.IsHighHalf()) {
    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Expected category1 register type not '"
        << new_type << "'";
    return false;
  } else {
    // Note: previously we failed when asked to set a conflict. However, conflicts are OK as long
    //       as they are not accessed, and our backends can handle this nowadays.
    line_[vdst] = new_type.GetId();
  }
  switch (kLockOp) {
    case LockOp::kClear:
      // Clear the monitor entry bits for this register.
      ClearAllRegToLockDepths(vdst);
      break;
    case LockOp::kKeep:
      // Should only be doing this with reference types.
      DCHECK(new_type.IsReferenceTypes());
      break;
  }
  return true;
}
 
inline bool RegisterLine::SetRegisterTypeWide(MethodVerifier* verifier, uint32_t vdst,
                                              const RegType& new_type1,
                                              const RegType& new_type2) {
  DCHECK_LT(vdst + 1, num_regs_);
  if (!new_type1.CheckWidePair(new_type2)) {
    verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Invalid wide pair '"
        << new_type1 << "' '" << new_type2 << "'";
    return false;
  } else {
    line_[vdst] = new_type1.GetId();
    line_[vdst + 1] = new_type2.GetId();
  }
  // Clear the monitor entry bits for this register.
  ClearAllRegToLockDepths(vdst);
  ClearAllRegToLockDepths(vdst + 1);
  return true;
}
 
inline void RegisterLine::SetResultTypeToUnknown(RegTypeCache* reg_types) {
  result_[0] = reg_types->Undefined().GetId();
  result_[1] = result_[0];
}
 
inline void RegisterLine::SetResultRegisterType(MethodVerifier* verifier, const RegType& new_type) {
  DCHECK(!new_type.IsLowHalf());
  DCHECK(!new_type.IsHighHalf());
  result_[0] = new_type.GetId();
  result_[1] = verifier->GetRegTypeCache()->Undefined().GetId();
}
 
inline void RegisterLine::SetResultRegisterTypeWide(const RegType& new_type1,
                                                    const RegType& new_type2) {
  DCHECK(new_type1.CheckWidePair(new_type2));
  result_[0] = new_type1.GetId();
  result_[1] = new_type2.GetId();
}
 
inline void RegisterLine::CopyRegister1(MethodVerifier* verifier, uint32_t vdst, uint32_t vsrc,
                                 TypeCategory cat) {
  DCHECK(cat == kTypeCategory1nr || cat == kTypeCategoryRef);
  const RegType& type = GetRegisterType(verifier, vsrc);
  if (!SetRegisterType<LockOp::kClear>(verifier, vdst, type)) {
    return;
  }
  if (!type.IsConflict() &&                                  // Allow conflicts to be copied around.
      ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
       (cat == kTypeCategoryRef && !type.IsReferenceTypes()))) {
    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy1 v" << vdst << "<-v" << vsrc << " type=" << type
                                                 << " cat=" << static_cast<int>(cat);
  } else if (cat == kTypeCategoryRef) {
    CopyRegToLockDepth(vdst, vsrc);
  }
}
 
inline void RegisterLine::CopyRegister2(MethodVerifier* verifier, uint32_t vdst, uint32_t vsrc) {
  const RegType& type_l = GetRegisterType(verifier, vsrc);
  const RegType& type_h = GetRegisterType(verifier, vsrc + 1);
 
  if (!type_l.CheckWidePair(type_h)) {
    verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy2 v" << vdst << "<-v" << vsrc
                                                 << " type=" << type_l << "/" << type_h;
  } else {
    SetRegisterTypeWide(verifier, vdst, type_l, type_h);
  }
}
 
inline bool RegisterLine::VerifyRegisterType(MethodVerifier* verifier, uint32_t vsrc,
                                             const RegType& check_type) {
  // Verify the src register type against the check type refining the type of the register
  const RegType& src_type = GetRegisterType(verifier, vsrc);
  if (UNLIKELY(!check_type.IsAssignableFrom(src_type, verifier))) {
    enum VerifyError fail_type;
    if (!check_type.IsNonZeroReferenceTypes() || !src_type.IsNonZeroReferenceTypes()) {
      // Hard fail if one of the types is primitive, since they are concretely known.
      fail_type = VERIFY_ERROR_BAD_CLASS_HARD;
    } else if (check_type.IsUninitializedTypes() || src_type.IsUninitializedTypes()) {
      // Hard fail for uninitialized types, which don't match anything but themselves.
      fail_type = VERIFY_ERROR_BAD_CLASS_HARD;
    } else if (check_type.IsUnresolvedTypes() || src_type.IsUnresolvedTypes()) {
      fail_type = VERIFY_ERROR_NO_CLASS;
    } else {
      fail_type = VERIFY_ERROR_BAD_CLASS_SOFT;
    }
    verifier->Fail(fail_type) << "register v" << vsrc << " has type "
                               << src_type << " but expected " << check_type;
    if (check_type.IsNonZeroReferenceTypes() &&
        !check_type.IsUnresolvedTypes() &&
        check_type.HasClass() &&
        src_type.IsNonZeroReferenceTypes() &&
        !src_type.IsUnresolvedTypes() &&
        src_type.HasClass()) {
      DumpB77342775DebugData(check_type.GetClass(), src_type.GetClass());
    }
    return false;
  }
  if (check_type.IsLowHalf()) {
    const RegType& src_type_h = GetRegisterType(verifier, vsrc + 1);
    if (UNLIKELY(!src_type.CheckWidePair(src_type_h))) {
      verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register v" << vsrc << " has type "
                                                   << src_type << "/" << src_type_h;
      return false;
    }
  }
  // The register at vsrc has a defined type, we know the lower-upper-bound, but this is less
  // precise than the subtype in vsrc so leave it for reference types. For primitive types
  // if they are a defined type then they are as precise as we can get, however, for constant
  // types we may wish to refine them. Unfortunately constant propagation has rendered this useless.
  return true;
}
 
inline void RegisterLine::VerifyMonitorStackEmpty(MethodVerifier* verifier) const {
  if (MonitorStackDepth() != 0) {
    verifier->Fail(VERIFY_ERROR_LOCKING);
    if (kDumpLockFailures) {
      VLOG(verifier) << "expected empty monitor stack in "
                     << verifier->GetMethodReference().PrettyMethod();
    }
  }
}
 
inline size_t RegisterLine::ComputeSize(size_t num_regs) {
  return OFFSETOF_MEMBER(RegisterLine, line_) + num_regs * sizeof(uint16_t);
}
 
inline RegisterLine* RegisterLine::Create(size_t num_regs,
                                          ScopedArenaAllocator& allocator,
                                          RegTypeCache* reg_types) {
  void* memory = allocator.Alloc(ComputeSize(num_regs));
  return new (memory) RegisterLine(num_regs, allocator, reg_types);
}
 
inline RegisterLine::RegisterLine(size_t num_regs,
                                  ScopedArenaAllocator& allocator,
                                  RegTypeCache* reg_types)
    : num_regs_(num_regs),
      monitors_(allocator.Adapter(kArenaAllocVerifier)),
      reg_to_lock_depths_(std::less<uint32_t>(),
                          allocator.Adapter(kArenaAllocVerifier)),
      this_initialized_(false) {
  std::uninitialized_fill_n(line_, num_regs_, 0u);
  SetResultTypeToUnknown(reg_types);
}
 
inline void RegisterLine::ClearRegToLockDepth(size_t reg, size_t depth) {
  CHECK_LT(depth, 32u);
  DCHECK(IsSetLockDepth(reg, depth));
  auto it = reg_to_lock_depths_.find(reg);
  DCHECK(it != reg_to_lock_depths_.end());
  uint32_t depths = it->second ^ (1 << depth);
  if (depths != 0) {
    it->second = depths;
  } else {
    reg_to_lock_depths_.erase(it);
  }
  // Need to unlock every register at the same lock depth. These are aliased locks.
  uint32_t mask = 1 << depth;
  for (auto& pair : reg_to_lock_depths_) {
    if ((pair.second & mask) != 0) {
      VLOG(verifier) << "Also unlocking " << pair.first;
      pair.second ^= mask;
    }
  }
}
 
inline void RegisterLineArenaDelete::operator()(RegisterLine* ptr) const {
  if (ptr != nullptr) {
    ptr->~RegisterLine();
    ProtectMemory(ptr, RegisterLine::ComputeSize(ptr->NumRegs()));
  }
}
 
}  // namespace verifier
}  // namespace art
 
#endif  // ART_RUNTIME_VERIFIER_REGISTER_LINE_INL_H_