Author: Lin Zuojian
UC kernel found a fatal crash on Android 12. About 10% of users will encounter this problem during cold start, which seriously affects the release of UC kernel. Its call stack is as follows:
10-12 19:03:21.461 1038 2723 I id.AlipayGphon: Rejecting re-init on previously-failed class java.lang.Class<com.uc.webkit.impl.WebViewChromiumFactoryProvider>: java.lang.VerifyError: Verifier rejected class com.uc.webkit.impl.WebViewChromiumFactoryProvider: com.uc.webkit.an com.uc.webkit.impl.WebViewChromiumFactoryProvider.g() failed to verify: com.uc.webkit.an com.uc.webkit.impl.WebViewChromiumFactoryProvider.g(): [0x15] can't resolve returned type 'Unresolved Reference: com.uc.webkit.an' or 'Unresolved Reference: com.uc.webkit.impl.ak' (declaration of 'com.uc.webkit.impl.WebViewChromiumFactoryProvider' appears in /data/user/0/com.eg.android.AlipayGphone/app_h5container/uc/3.22.2.28.21092218119_64/so/core.jar) 10-12 19:03:21.461 1038 2723 I id.AlipayGphon: (Throwable with empty stack trace) 10-12 19:03:21.464 1038 2723 E WebViewEntry: init error and prepare native crash 10-12 19:03:21.464 1038 2723 E WebViewEntry: java.lang.NoClassDefFoundError: com.uc.webkit.impl.WebViewChromiumFactoryProvider 10-12 19:03:21.464 1038 2723 E WebViewEntry: at com.uc.webkit.impl.WebViewChromiumFactoryProvider.i(Unknown Source:0) 10-12 19:03:21.464 1038 2723 E WebViewEntry: at com.uc.webkit.WebViewEntry.p(U4Source:193) 10-12 19:03:21.464 1038 2723 E WebViewEntry: at com.uc.webkit.bg.run(Unknown Source:0) 10-12 19:03:21.464 1038 2723 E WebViewEntry: at android.os.Handler.handleCallback(Handler.java:938) 10-12 19:03:21.464 1038 2723 E WebViewEntry: at android.os.Handler.dispatchMessage(Handler.java:99) 10-12 19:03:21.464 1038 2723 E WebViewEntry: at android.os.Looper.loopOnce(Looper.java:201) 10-12 19:03:21.464 1038 2723 E WebViewEntry: at android.os.Looper.loop(Looper.java:288) 10-12 19:03:21.464 1038 2723 E WebViewEntry: at android.os.HandlerThread.run(HandlerThread.java:67) 10-12 19:03:21.464 1038 2723 E WebViewEntry: Caused by: java.lang.VerifyError: Verifier rejected class com.uc.webkit.impl.WebViewChromiumFactoryProvider: com.uc.webkit.an com.uc.webkit.impl.WebViewChromiumFactoryProvider.g() failed to verify: com.uc.webkit.an com.uc.webkit.impl.WebViewChromiumFactoryProvider.g(): [0x15] can't resolve returned type 'Unresolved Reference: com.uc.webkit.an' or 'Unresolved Reference: com.uc.webkit.impl.ak' (declaration of 'com.uc.webkit.impl.WebViewChromiumFactoryProvider' appears in /data/user/0/com.eg.android.AlipayGphone/app_h5container/uc/3.22.2.28.21092218119_64/so/core.jar)
Without solving this problem, our kernel may not be enabled on Android 12, which is a matter of life and death for the kernel. This problem cannot be reproduced in normal operation. It can only occur occasionally through monkey's crazy cold start.
Another background is that UC browser raised the sdk level to 30, which caused this problem.
Call stack analysis
From the information of the call stack, we can see that the top-level Error is NoClassDefFoundError, but it is caused by the following VerifyError. This call stack shows that the normal startup process is in progress.
Rejecting re init on previously failed class displays com uc. webkit. impl. Webviewchromium factoryprovider should have tried to Verify, but the Error was. According to common sense, there should also be a VerifyError thrown. However, I found multiple crash logs and did not find the location where VerifyError was thrown for the first time.
In addition, this VerifyError caused by: Java The lang. VerifyError location should be followed by its first Verify call stack, but it displays (Throwable with empty stack trace).
Black technology analysis: means I
With the above questions, we find that the current data is not enough for us to analyze. We need more information related to Verify to deal with the problem.
The art virtual machine of Android comes with verbose log. It is classified by module and will not be opened at ordinary times. When you need to start art, let it open by passing parameters.
We tried the wrapper technology, that is, add the file wrapper in the lib directory SH, the system will use wrapper SH starts the virtual machine instead of through Zygote. Unfortunately, this method doesn't work. I analyzed the Android runtime After the source code in CPP, we found that the virtual machine entered by wrapper will be filtered out and completely ignored.
We can only use methods other than serious ways.
The above figure shows the structure of verbose log. We can see that there is a global variable gLogVerbosity to control their switches. Can we start verbose log by modifying gLogVerbosity?
UC kernel has a series of powerful black technology combinations. The black technology to meet this demand is symbol_resolver module. This technology can analyze the location of the named so mapping from the / proc/self/maps file, and get all the symbols through elf analysis, and then we can find the location of the desired symbols from the key value pair.
Using this technology, we quickly located libart The location of gLogVerbosity in so, and take verifier and verifier as a bool array_ Set the debug item to true. So we have a new log:
Verification failed on class org.chromium.ui.base.WindowAndroid in /data/user/0/com.eg.android.AlipayGphone/app_h5container/uc/3.22.2.31.10191532_64/so/core.jar because: Verifier rejected class org.chromium.ui.base.WindowAndroid: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken() failed to verify: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken(): [0x10] can't resolve returned type 'Unresolved Reference: android.os.IBinder' or 'Reference: android.os.IBinder' VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x0] : Processing const/4 v1, #+0 0:[Undefined],1:[Undefined],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x1] : Processing iget-object v0, v2, Ljava/lang/ref/WeakReference; org.chromium.ui.base.WindowAndroid.e // field@7982 0:[Undefined],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x3] : Processing invoke-virtual {v0}, java.lang.Object java.lang.ref.WeakReference.get() // method@7347 0:[Reference: java.lang.ref.WeakReference],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x6] : Processing move-result-object v0 0:[Reference: java.lang.ref.WeakReference],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x7] : Processing check-cast v0, android.content.Context // type@TypeIndex[61] 0:[Reference: java.lang.Object],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x9] : Processing invoke-static {v0}, android.app.Activity org.chromium.ui.base.WindowAndroid.a(android.content.Context) // method@17017 0:[Reference: android.content.Context],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0xc] : Processing move-result-object v0 0:[Reference: android.content.Context],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0xd] : Processing if-nez v0, +4 0:[Reference: android.app.Activity],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0xf] : Processing move-object v0, v1 0:[Reference: android.app.Activity],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x10] : Processing return-object v0 0:[Zero/null],1:[Conflict],2:[Conflict], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x11] : Processing invoke-virtual {v0}, android.view.Window android.app.Activity.getWindow() // method@26 0:[Reference: android.app.Activity],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x14] : Processing move-result-object v0 0:[Reference: android.app.Activity],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x15] : Processing if-nez v0, +4 0:[Reference: android.view.Window],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x17] : Processing move-object v0, v1 0:[Reference: android.view.Window],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x18] : Processing goto -8 0:[Zero/null],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x19] : Processing invoke-virtual {v0}, android.view.View android.view.Window.peekDecorView() // method@1459 0:[Reference: android.view.Window],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x1c] : Processing move-result-object v0 0:[Reference: android.view.Window],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x1d] : Processing if-nez v0, +4 0:[Reference: android.view.View],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x1f] : Processing move-object v0, v1 0:[Reference: android.view.View],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x20] : Processing goto -16 0:[Zero/null],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x21] : Processing invoke-virtual {v0}, android.os.IBinder android.view.View.getWindowToken() // method@1318 0:[Reference: android.view.View],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x24] : Processing move-result-object v0 0:[Reference: android.view.View],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x25] : Processing goto -21 0:[Reference: android.os.IBinder],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x25] : Merging at [0x25] to [0x10]: 0:[Zero/null],1:[Conflict],2:[Conflict], MERGE 0:[Reference: android.os.IBinder],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], == 0:[Reference: android.os.IBinder],1:[Conflict],2:[Conflict], VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x10] : Processing return-object v0 0:[Reference: android.os.IBinder],1:[Conflict],2:[Conflict], Rejecting opcode return-object v0 Register Types: 0: Undefined 1: Conflict 2: null 3: Boolean 4: Byte 5: Short 6: Char 7: Integer 8: Long (Low Half) 9: Long (High Half) 10: Float 11: Double (Low Half) 12: Double (High Half) 13: Precise Constant: -1 14: Zero/null 15: Precise Constant: 1 16: Precise Constant: 2 17: Precise Constant: 3 18: Precise Constant: 4 19: Reference: org.chromium.ui.base.WindowAndroid 20: Reference: java.lang.Object 21: Reference: java.lang.ref.WeakReference 22: Reference: java.lang.ref.Reference 23: Reference: android.content.Context 24: Reference: android.app.Activity 25: Unresolved Reference: android.os.IBinder 26: Reference: android.view.Window 27: Reference: android.view.View 28: Reference: android.os.IBinder Dumping instructions and register lines: 0:[Undefined],1:[Undefined],2:[Reference: org.chromium.ui.base.WindowAndroid], 0x0000: V-O-B-- const/4 v1, #+0 0x0001: V-O---- iget-object v0, v2, Ljava/lang/ref/WeakReference; org.chromium.ui.base.WindowAndroid.e // field@7982 0x0003: V-O---- invoke-virtual {v0}, java.lang.Object java.lang.ref.WeakReference.get() // method@7347 0x0006: V-O---- move-result-object v0 0x0007: V-O--G- check-cast v0, android.content.Context // type@TypeIndex[61] 0x0009: V-O---- invoke-static {v0}, android.app.Activity org.chromium.ui.base.WindowAndroid.a(android.content.Context) // method@17017 0x000c: V-O---- move-result-object v0 0x000d: V-O---- if-nez v0, +4 0x000f: V-O---- move-object v0, v1 0:[Reference: android.os.IBinder],1:[Conflict],2:[Conflict], 0x0010: VCO-B-R return-object v0 0:[Reference: android.app.Activity],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], 0x0011: V-O-B-- invoke-virtual {v0}, android.view.Window android.app.Activity.getWindow() // method@26 0x0014: V-O---- move-result-object v0 0x0015: V-O---- if-nez v0, +4 0x0017: V-O---- move-object v0, v1 0x0018: V-O---- goto -8 0:[Reference: android.view.Window],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], 0x0019: V-O-B-- invoke-virtual {v0}, android.view.View android.view.Window.peekDecorView() // method@1459 0x001c: V-O---- move-result-object v0 0x001d: V-O---- if-nez v0, +4 0x001f: V-O---- move-object v0, v1 0x0020: V-O---- goto -16 0:[Reference: android.view.View],1:[Zero/null],2:[Reference: org.chromium.ui.base.WindowAndroid], 0x0021: V-O-B-- invoke-virtual {v0}, android.os.IBinder android.view.View.getWindowToken() // method@1318 0x0024: V-O---- move-result-object v0 0x0025: V-O---- goto -21 Setting org.chromium.ui.base.WindowAndroid to erroneous.
There are two points worth paying attention to in this log:
1,[0x10] can't resolve returned type 'Unresolved Reference: android.os.IBinder' or 'Reference: android.os.IBinder' VFY: android.os.IBinder org.chromium.ui.base.WindowAndroid.getWindowToken()[0x0] : Processing const/4 v1, #+0
According to the code of log ging, we see return_type corresponds to 'unresolved reference: Android os. IBinder'.
But return_ The source of type is:
GetMethodReturnType:
Will call FromDescriptor:
ResolveClass will be called. ResolveClass will call ClassLinker::FindClass. FindClass has an obvious failure. The premise is:
That is, when the current thread is a RuntimeThread, FindClass will be rejected. Because this may cause class to enter the initialization process, causing it to call the class initialization function in the static block in class. The RuntimeThread lacks an environment that allows java functions and cannot be allowed to do so.
Is it because the current Thread is a Runtime Thread? If yes, which Runtime Thread is this Thread? Is it gc thread?
2. Analyze the Verify actions before and after this log. It is found that all threads that can be verified normally have a log of load class. But the thread with the problem has no log of load class. Later, it failed to Verify several classes for the same reason. This is more certain that the failed thread is a Runtime Thread. In addition, the phenomenon that the VerifyError mentioned above has no call stack record also confirms that it is a Runtime Thread. Because the Runtime Thread has no Java environment and cannot call Java functions, there is no record. But we still need to find out what this thread is. To this end, we used the second black technology.
Black technology analysis: means 2
By observing the code, we find that VerifyError is thrown through the same function:
We can also find its global symbol, so we just need to add the code that executes the immediate crash to the position of the symbol, and then let monkey trigger the problem to deal with it.
Here's a problem: android forbids us to change the permission of the code segment to writable for security reasons.
How to safely change the code segment? We use / prof/self/mem Technology: open the / proc/self/mem file, and then use pwrite api to write code to the symbol location.
In this way, we found the thread that failed Verify:
Root cause analysis
We got the Thread name Verification th. I also got the call stack started by the Thread. He started from ThreadPool. The threads in ThreadPool are runtimethreads, which confirms the previous speculation. The task that the Thread runs is the BackgroundVerificationTask. You can quickly find where it starts:
Let's look again at the problem submitted:
commit 0d5f6402ff925ac1385ccb349f8a2798a4816458 Author: Nicolas Geoffray ngeoffray@google.com Date: Tue Apr 13 13:05:36 2021 +0100 Only run background verification when dexPathList is set. Otherwise, the runtime will not be able to find the classes. Test: 692-vdex-secondary-loader Bug: 185088679 Change-Id: Idd39eabe00faa017aa5254f7188e7adbcaa23c74 diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java index 710a88cc6d0..afbc9ec9de7 100644 --- a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java +++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java @@ -128,6 +128,9 @@ public class BaseDexClassLoader extends ClassLoader { : Arrays.copyOf(sharedLibraryLoaders, sharedLibraryLoaders.length); this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted); + // Run background verification after having set 'pathList'. + this.pathList.maybeRunBackgroundVerification(this); + reportClassLoaderChain(); } @@ -186,6 +189,8 @@ public class BaseDexClassLoader extends ClassLoader { this.sharedLibraryLoaders = null; this.pathList = new DexPathList(this, librarySearchPath); this.pathList.initByteBufferDexPath(dexFiles); + // Run background verification after having set 'pathList'. + this.pathList.maybeRunBackgroundVerification(this); } @Override
After searching with the git tag --contain command, I found that it was really brought with android 12 beta.
Solution
In addition to reporting problems and complaining to Google, we still need to find a solution. Google said that its next December update of android 12 will solve this problem, but many old machines don't update at all, so they can't count on it.
We must find a method from the OatFileManager::RunBackgroundVerification function to force it not to start the background verification thread. Our eyes soon fell on:
above. Because we can still control the file name. The previous logic also judges the sdk level. As long as the sdk level is < = 29, this thread will not be started, but UC browser has opened the sdk level to 30 (which also confirms the background that UC browser only appears when it increases the sdk level to 30).
After observing the function DexLocationToOdexFilename, I found that one line is very helpful:
// Get the base part of the file without the extension. std::string file = location.substr(pos+1); pos = file.rfind('.'); if (pos == std::string::npos) { *error_msg = "Dex location " + location + " has no extension."; return false; }
As long as we keep it from finding the suffix separator "." Can force it to quit.
result
Soft link core is used for android 12 After jar is the method of corejar, this problem disappears. The monster that threatened the UC kernel was defeated, and the world returned to its former peace.
Focus on Alibaba mobile technology WeChat official account, 3 mobile technology practices dry cargo per week to give you thought!