Index: runtime/vm/precompiler.cc |
diff --git a/runtime/vm/precompiler.cc b/runtime/vm/precompiler.cc |
index 44b1c3ec04b442c2cc751db14b15dd08ed726655..6eec3f89b011c7738c2a438065cbd012399d2ddf 100644 |
--- a/runtime/vm/precompiler.cc |
+++ b/runtime/vm/precompiler.cc |
@@ -489,6 +489,7 @@ void Precompiler::DoCompileAll( |
BindStaticCalls(); |
SwitchICCalls(); |
+ Obfuscate(); |
rmacnak
2017/08/23 01:16:50
We should clear the obfuscation table from the obj
Vyacheslav Egorov (Google)
2017/08/23 15:54:52
This is done at the end of Obfuscate()
|
ProgramVisitor::Dedup(); |
@@ -610,30 +611,7 @@ void Precompiler::PrecompileConstructors() { |
} |
} |
-void Precompiler::AddRoots(Dart_QualifiedFunctionName embedder_entry_points[]) { |
- // Note that <rootlibrary>.main is not a root. The appropriate main will be |
- // discovered through _getMainClosure. |
- |
- AddSelector(Symbols::NoSuchMethod()); |
- |
- AddSelector(Symbols::Call()); // For speed, not correctness. |
- |
- // Allocated from C++. |
- Class& cls = Class::Handle(Z); |
- for (intptr_t cid = kInstanceCid; cid < kNumPredefinedCids; cid++) { |
- ASSERT(isolate()->class_table()->IsValidIndex(cid)); |
- if (!isolate()->class_table()->HasValidClassAt(cid)) { |
- continue; |
- } |
- if ((cid == kDynamicCid) || (cid == kVoidCid) || |
- (cid == kFreeListElement) || (cid == kForwardingCorpse)) { |
- continue; |
- } |
- cls = isolate()->class_table()->At(cid); |
- AddInstantiatedClass(cls); |
- } |
- |
- Dart_QualifiedFunctionName vm_entry_points[] = { |
+static Dart_QualifiedFunctionName vm_entry_points[] = { |
// Functions |
{"dart:core", "::", "_completeDeferredLoads"}, |
{"dart:core", "::", "identityHashCode"}, |
@@ -675,7 +653,30 @@ void Precompiler::AddRoots(Dart_QualifiedFunctionName embedder_entry_points[]) { |
{"dart:core", "Error", "_stackTrace"}, |
{"dart:math", "_Random", "_state"}, |
{NULL, NULL, NULL} // Must be terminated with NULL entries. |
- }; |
+}; |
+ |
+void Precompiler::AddRoots(Dart_QualifiedFunctionName embedder_entry_points[]) { |
+ // Note that <rootlibrary>.main is not a root. The appropriate main will be |
+ // discovered through _getMainClosure. |
+ |
+ AddSelector(Symbols::NoSuchMethod()); |
+ |
+ AddSelector(Symbols::Call()); // For speed, not correctness. |
+ |
+ // Allocated from C++. |
+ Class& cls = Class::Handle(Z); |
+ for (intptr_t cid = kInstanceCid; cid < kNumPredefinedCids; cid++) { |
+ ASSERT(isolate()->class_table()->IsValidIndex(cid)); |
+ if (!isolate()->class_table()->HasValidClassAt(cid)) { |
+ continue; |
+ } |
+ if ((cid == kDynamicCid) || (cid == kVoidCid) || |
+ (cid == kFreeListElement) || (cid == kForwardingCorpse)) { |
+ continue; |
+ } |
+ cls = isolate()->class_table()->At(cid); |
+ AddInstantiatedClass(cls); |
+ } |
AddEntryPoints(vm_entry_points); |
AddEntryPoints(embedder_entry_points); |
@@ -2278,6 +2279,13 @@ void Precompiler::SwitchICCalls() { |
#endif |
} |
+void Precompiler::Obfuscate() { |
+ // TODO(dartbug.com/XXX) remove URIs from Scripts and Libraries. |
+ |
+ // Discard obfuscation mappings to avoid including them into snapshot. |
+ I->object_store()->set_obfuscation_map(Array::Handle(Z)); |
+} |
+ |
void Precompiler::FinalizeAllClasses() { |
Library& lib = Library::Handle(Z); |
Class& cls = Class::Handle(Z); |
@@ -3330,6 +3338,365 @@ RawError* Precompiler::CompileFunction(Precompiler* precompiler, |
return PrecompileFunctionHelper(precompiler, &pipeline, function, optimized); |
} |
+Obfuscator::Obfuscator(Thread* thread, const String& private_key) |
+ : state_(NULL) { |
+ Isolate* isolate = thread->isolate(); |
+ Zone* zone = thread->zone(); |
+ |
+ if (isolate->obfuscate()) { |
+ ObjectStore* store = thread->isolate()->object_store(); |
+ Array& obfuscation_state = Array::Handle(zone, store->obfuscation_map()); |
+ |
+ if (store->obfuscation_map() == Array::null()) { |
+ const int kInitialPrivateCapacity = 256; |
+ obfuscation_state = Array::New(kSavedStateSize); |
+ obfuscation_state.SetAt( |
+ 1, Array::Handle(zone, HashTables::New<ObfuscationMap>( |
+ kInitialPrivateCapacity, Heap::kOld))); |
+ } |
+ |
+ state_ = |
+ new (zone) ObfuscationState(thread, obfuscation_state, private_key); |
+ |
+ if (store->obfuscation_map() == Array::null()) { |
+ InitializeRenamingMap(isolate); |
+ } |
+ } |
+} |
+ |
+Obfuscator::~Obfuscator() { |
+ if (state_ != NULL) { |
+ state_->SaveState(); |
+ } |
+} |
+ |
+void Obfuscator::InitializeRenamingMap(Isolate* isolate) { |
+ // Prevent renaming of classes and method names mentioned in the |
+ // entry points lists. |
+ PreventRenaming(vm_entry_points); |
+ PreventRenaming(isolate->embedder_entry_points()); |
+ |
+// Prevent renaming of all pseudo-keywords and operators. |
+// Note: not all pseudo-keywords are mentioned in DART_KEYWORD_LIST |
+// (for example 'hide', 'show' and async related keywords are omitted). |
+// Those are protected from renaming as part of all symbols. |
+#define PREVENT_RENAMING(name, value, priority, attr) \ |
+ do { \ |
+ if (Token::CanBeOverloaded(Token::name) || \ |
+ ((Token::attr & Token::kPseudoKeyword) != 0)) { \ |
+ PreventRenaming(value); \ |
+ } \ |
+ } while (0); |
+ |
+ DART_TOKEN_LIST(PREVENT_RENAMING) |
+ DART_KEYWORD_LIST(PREVENT_RENAMING) |
+#undef PREVENT_RENAMING |
+ |
+// Protect all symbols from renaming. |
+#define PREVENT_RENAMING(name, value) PreventRenaming(value); |
+ PREDEFINED_SYMBOLS_LIST(PREVENT_RENAMING) |
+#undef PREVENT_RENAMING |
+ |
+ // Protect NativeFieldWrapperClassX names from being obfuscated. Those |
+ // classes are created manually by the runtime system. |
+ // TODO(dartbug.com/XXX) instead call to Obfuscator::Rename from a place |
+ // where these are created. |
+ PreventRenaming("NativeFieldWrapperClass1"); |
+ PreventRenaming("NativeFieldWrapperClass2"); |
+ PreventRenaming("NativeFieldWrapperClass3"); |
+ PreventRenaming("NativeFieldWrapperClass4"); |
+ |
+// Prevent renaming of ClassID.cid* fields. These fields are injected by |
+// runtime. |
+// TODO(dartbug.com/XXX) instead call to Obfuscator::Rename from a place |
+// where these are created. |
+#define CLASS_LIST_WITH_NULL(V) \ |
+ V(Null) \ |
+ CLASS_LIST_NO_OBJECT(V) |
+#define PREVENT_RENAMING(clazz) PreventRenaming("cid" #clazz); |
+ CLASS_LIST_WITH_NULL(PREVENT_RENAMING) |
+#undef PREVENT_RENAMING |
+#undef CLASS_LIST_WITH_NULL |
+ |
+// Prevent renaming of methods that are looked up by method recognizer. |
+// TODO(dartbug.com/XXX) instead call to Obfuscator::Rename from a place |
+// where these are looked up. |
+#define PREVENT_RENAMING(class_name, function_name, recognized_enum, \ |
+ result_type, fingerprint) \ |
+ do { \ |
+ PreventRenaming(#class_name); \ |
+ PreventRenaming(#function_name); \ |
+ } while (0); |
+ RECOGNIZED_LIST(PREVENT_RENAMING) |
+#undef PREVENT_RENAMING |
+ |
+// Prevent renaming of methods that are looked up by method recognizer. |
+// TODO(dartbug.com/XXX) instead call to Obfuscator::Rename from a place |
+// where these are looked up. |
+#define PREVENT_RENAMING(class_name, function_name, recognized_enum, \ |
+ fingerprint) \ |
+ do { \ |
+ PreventRenaming(#class_name); \ |
+ PreventRenaming(#function_name); \ |
+ } while (0); |
+ INLINE_WHITE_LIST(PREVENT_RENAMING) |
+ INLINE_BLACK_LIST(PREVENT_RENAMING) |
+ POLYMORPHIC_TARGET_LIST(PREVENT_RENAMING) |
+#undef PREVENT_RENAMING |
+ |
+ // These are not mentioned by entry points but are still looked up by name. |
rmacnak
2017/08/23 01:16:50
They should be mentioned by the entry points. We'r
Vyacheslav Egorov (Google)
2017/08/23 15:54:52
Done.
|
+ PreventRenaming("_resolveScriptUri"); |
+ PreventRenaming("_printClosure"); |
+ PreventRenaming("_uriBaseClosure"); |
+ PreventRenaming("_isolateId"); |
+ PreventRenaming("_loadPort"); |
+ PreventRenaming("_ip"); |
+ PreventRenaming("_port"); |
+ PreventRenaming("_autoStart"); |
+ PreventRenaming("_originCheckDisabled"); |
+ PreventRenaming("_isWindows"); |
+ PreventRenaming("_isFuchsia"); |
+ PreventRenaming("_traceLoading"); |
+ PreventRenaming("_getWatchSignalInternal"); |
+ PreventRenaming("_signalWatch"); |
+ PreventRenaming("main"); |
+ |
+ // Fast path for common conditional import. See Deobfuscate method. |
+ PreventRenaming("dart"); |
+ PreventRenaming("library"); |
+ PreventRenaming("io"); |
+ PreventRenaming("html"); |
+} |
+ |
+RawString* Obfuscator::ObfuscationState::RenameImpl(const String& name, |
+ bool atomic) { |
+ ASSERT(name.IsSymbol()); |
+ |
+ renamed_ ^= renames_.GetOrNull(name); |
+ if (renamed_.IsNull()) { |
+ renamed_ = BuildRename(name, atomic); |
+ renames_.UpdateOrInsert(name, renamed_); |
+ } |
+ return renamed_.raw(); |
+} |
+ |
+void Obfuscator::PreventRenaming(Dart_QualifiedFunctionName entry_points[]) { |
+ for (intptr_t i = 0; entry_points[i].function_name != NULL; i++) { |
+ const char* class_name = entry_points[i].class_name; |
+ const char* function_name = entry_points[i].function_name; |
+ |
+ const size_t class_name_len = strlen(class_name); |
+ if (strncmp(function_name, class_name, class_name_len) == 0 && |
+ function_name[class_name_len] == '.') { |
+ const char* ctor_name = function_name + class_name_len + 1; |
+ if (ctor_name[0] != '\0') { |
+ PreventRenaming(ctor_name); |
+ } |
+ } else { |
+ PreventRenaming(function_name); |
+ } |
+ PreventRenaming(class_name); |
+ } |
+} |
+ |
+static const char* const kGetterPrefix = "get:"; |
+static const intptr_t kGetterPrefixLength = strlen(kGetterPrefix); |
+static const char* const kSetterPrefix = "set:"; |
+static const intptr_t kSetterPrefixLength = strlen(kSetterPrefix); |
+ |
+void Obfuscator::PreventRenaming(const char* name) { |
+ const char* dot = strchr(name, '.'); |
+ if (dot != NULL) { |
+ name = dot + 1; |
+ } |
+ |
+ if (name[0] == '\0') { |
+ return; |
+ } |
+ |
+ if (strncmp(name, kGetterPrefix, kGetterPrefixLength) == 0) { |
+ name = name + kGetterPrefixLength; |
+ } else if (strncmp(name, kSetterPrefix, kSetterPrefixLength) == 0) { |
+ name = name + kSetterPrefixLength; |
+ } |
+ |
+ state_->PreventRenaming(name); |
+} |
+ |
+void Obfuscator::ObfuscationState::SaveState() { |
+ saved_state_.SetAt(kSavedStateNameIndex, String::Handle(String::New(name_))); |
+ saved_state_.SetAt(kSavedStateRenamesIndex, renames_.Release()); |
+ thread_->isolate()->object_store()->set_obfuscation_map(saved_state_); |
+} |
+ |
+void Obfuscator::ObfuscationState::PreventRenaming(const char* name) { |
+ string_ = Symbols::New(thread_, name); |
+ PreventRenaming(string_); |
+} |
+ |
+void Obfuscator::ObfuscationState::PreventRenaming(const String& name) { |
+ renames_.UpdateOrInsert(name, name); |
+} |
+ |
+void Obfuscator::ObfuscationState::NextName() { |
+ for (intptr_t i = 0;; i++) { |
+ const char digit = name_[i]; |
+ if (digit == '\0') { |
+ name_[i] = 'a'; |
+ } else if (digit < 'Z') { |
+ name_[i]++; |
+ } else if (digit == 'Z') { |
+ name_[i] = 'a'; |
+ continue; |
+ } else if (digit < 'z') { |
+ name_[i]++; |
+ } else { |
+ name_[i] = 'A'; |
+ } |
+ break; |
+ } |
+} |
+ |
+RawString* Obfuscator::ObfuscationState::NewAtomicRename( |
+ bool should_be_private) { |
+ do { |
+ NextName(); |
+ renamed_ = Symbols::NewFormatted(thread_, "%s%s", |
+ should_be_private ? "_" : "", name_); |
+ } while (renames_.ContainsKey(renamed_)); |
+ return renamed_.raw(); |
+} |
+ |
+RawString* Obfuscator::ObfuscationState::BuildRename(const String& name, |
+ bool atomic) { |
+ const bool is_private = name.CharAt(0) == '_'; |
+ if (!atomic && is_private) { |
+ intptr_t i = 0; |
+ while (i < name.Length() && name.CharAt(i) != '@') { |
+ i++; |
+ } |
+ const intptr_t end = i; |
+ |
+ string_ = Symbols::New(thread_, name, 0, end); |
+ |
+ string_ = RenameImpl(string_, /*atomic=*/true); |
+ return Symbols::FromConcat(thread_, string_, private_key_); |
+ } else { |
+ return NewAtomicRename(is_private); |
+ } |
+} |
+ |
+void Obfuscator::ObfuscateSymbolInstance(Thread* thread, |
+ const Instance& symbol) { |
+ const intptr_t kSymbolNameOffset = kWordSize; |
+ |
+ Object& name_value = String::Handle(); |
+ name_value = symbol.RawGetFieldAtOffset(kSymbolNameOffset); |
+ if (!name_value.IsString()) { |
+ // dart.internal.Symbol constructor does not validate its input. |
+ return; |
+ } |
+ |
+ String& name = String::Handle(); |
+ name ^= name_value.raw(); |
+ |
+ // TODO(vegorov) it is quite wasteful to create an obfuscator per-symbol. |
+ Obfuscator obfuscator(thread, /*private_key=*/String::Handle()); |
+ |
+ String& component = String::Handle(); |
+ GrowableHandlePtrArray<const String> renamed(thread->zone(), 2); |
+ |
+ const intptr_t length = name.Length(); |
+ intptr_t i = 0, start = 0; |
+ while (i < length) { |
+ // First look for a '.' in the symbol. |
+ start = i; |
+ while (i < length && name.CharAt(i) != '.') { |
+ i++; |
+ } |
+ const intptr_t end = i; |
+ if (end == length) { |
+ break; |
+ } |
+ |
+ if (start != end) { |
+ component = Symbols::New(thread, name, start, end - start); |
+ component = obfuscator.Rename(component, /*atomic=*/true); |
+ renamed.Add(component); |
+ } |
+ |
+ renamed.Add(Symbols::Dot()); |
+ i++; // Skip '.' |
+ } |
+ |
+ // Handle the last component [start, length). |
+ // If symbol ends up at = and it is not one of '[]=', '==', '<=' or |
+ // '>=' then we treat it as a setter symbol of form 'name=' and |
+ // obfuscate to rename('name') + '='. |
+ const bool is_setter = (length - start) > 1 && |
+ name.CharAt(length - 1) == '=' && |
+ !(name.Equals(Symbols::AssignIndexToken()) || |
+ name.Equals(Symbols::EqualOperator()) || |
+ name.Equals(Symbols::GreaterEqualOperator()) || |
+ name.Equals(Symbols::LessEqualOperator())); |
+ const intptr_t end = length - (is_setter ? 1 : 0); |
+ |
+ if ((start == 0) && (end == length) && name.IsSymbol()) { |
+ component = name.raw(); |
+ } else { |
+ component = Symbols::New(thread, name, start, end - start); |
+ } |
+ component = obfuscator.Rename(component, /*atomic=*/true); |
+ renamed.Add(component); |
+ |
+ if (is_setter) { |
+ renamed.Add(Symbols::Equals()); |
+ } |
+ |
+ name = Symbols::FromConcatAll(thread, renamed); |
+ symbol.RawSetFieldAtOffset(kSymbolNameOffset, name); |
+} |
+ |
+void Obfuscator::Deobfuscate(Thread* thread, |
+ const GrowableObjectArray& pieces) { |
+ const Array& obfuscation_state = Array::Handle( |
+ thread->zone(), thread->isolate()->object_store()->obfuscation_map()); |
+ if (obfuscation_state.IsNull()) { |
+ return; |
+ } |
+ |
+ const Array& renames = Array::Handle( |
+ thread->zone(), GetRenamesFromSavedState(obfuscation_state)); |
+ |
+ ObfuscationMap renames_map(renames.raw()); |
+ String& piece = String::Handle(); |
+ for (intptr_t i = 0; i < pieces.Length(); i++) { |
+ piece ^= pieces.At(i); |
+ ASSERT(piece.IsSymbol()); |
+ |
+ // Fast path: skip '.' |
+ if (piece.raw() == Symbols::Dot().raw()) { |
+ continue; |
+ } |
+ |
+ // Fast path: check if piece has an identity obfuscation. |
+ if (renames_map.GetOrNull(piece) == piece.raw()) { |
+ continue; |
+ } |
+ |
+ ObfuscationMap::Iterator it(&renames_map); |
+ while (it.MoveNext()) { |
+ const intptr_t entry = it.Current(); |
+ if (renames_map.GetPayload(entry, 0) == piece.raw()) { |
+ piece ^= renames_map.GetKey(entry); |
+ pieces.SetAt(i, piece); |
+ break; |
+ } |
+ } |
+ } |
+ renames_map.Release(); |
+} |
+ |
#endif // DART_PRECOMPILER |
} // namespace dart |