Android 12 "fatal" crash solution

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!

Keywords: Java webkit Chromium

Added by pbdude23 on Wed, 16 Feb 2022 05:50:36 +0200