Status Update
Comments
ra...@gmail.com <ra...@gmail.com> #2
ad...@google.com <ad...@google.com>
ad...@google.com <ad...@google.com> #3
Thanks again for the feedback! Our product and engineering teams have evaluated the request and responded:
Hello,
An MTE-specific strlen
(and all string.h
family of functions) is necessary and desirable to detect buffer-overflow bugs.
The example you mention, vector.push_back(toCppString(**).c_str())
, is a great example of the type of
Some of those specific examples of use-after-free bugs are begnign, however they're still undefined behaviour. MTE catching them is the desired functionality.
to...@gmail.com <to...@gmail.com> #4
Thanks for your reply. I can understand that and I found a new case that the __memchr_aarch64_mte called by strstr could give a MTE error if the input pointer has not been aligned by 16 bytes. I think it is a bug,right? test case:
char *data = (char*)calloc(128,1);
memset(data, 'A', 128);
char *str2 = "BBbbbaa";
char *res = strstr(data+2, str2);
ra...@gmail.com <ra...@gmail.com> #5
The backtrace for the test case:
signal 11 (SIGSEGV), code 9 (SEGV_MTESERR), fault addr 0x0200007b9b4e67e0
Cause: [MTE]: Buffer Overflow, 0 bytes right of a 128-byte allocation at 0x7b9b4e6760
***
backtrace:
#00 pc 0000000000092fe0 /apex/com.android.runtime/lib64/bionic/libc.so (__strchr_aarch64_mte+96) (BuildId: cfc293be733954571ce0dc79a9917039)
#01 pc 00000000000e8f88 /apex/com.android.runtime/lib64/bionic/libc.so (strstr+24) (BuildId: cfc293be733954571ce0dc79a9917039)
#02 pc 00000000000017d8 /data/local/tmp/a.out (main+116)
Oh,sorry that the case I give above is for the __strchr_aarch64_mte, and the case for __memchr_aarch64_mte is below.
char *data = (char*)calloc(128,1);
memset(data, 'A', 128);
char *str2 = "ABbbbAa";
char *res = strstr(data+2, str2);
and the backtrace of the __memchr_aarch64_mte case:
signal 11 (SIGSEGV), code 9 (SEGV_MTESERR), fault addr 0x0f000074794803b0
Cause: [MTE]: Buffer Overflow, 0 bytes right of a 128-byte allocation at 0x7479480330
***
backtrace:
#00 pc 000000000009268c /apex/com.android.runtime/lib64/bionic/libc.so (__memchr_aarch64_mte+12) (BuildId: cfc293be733954571ce0dc79a9917039)
#01 pc 00000000000e92c0 /apex/com.android.runtime/lib64/bionic/libc.so (twoway_strstr+464) (BuildId: cfc293be733954571ce0dc79a9917039)
I think the __strchr_aarch64_mte and __memchr_aarch64_mte has the same problem that they preread 16 bytes and if there are two MTE tag in a single bytes chunk, a MTE exception would raise. And that is caused without any wrong operations by developer.
to...@gmail.com <to...@gmail.com> #6
Sorry and I give another issue for that. 317403451
[Deleted User] <[Deleted User]> #7
For further update please see
[Deleted User] <[Deleted User]> #8
[Deleted User] <[Deleted User]> #9
2018-12-20 16:54:19.708 9948-9948/
Process:
java.lang.RuntimeException: Unable to start activity ComponentInfo{
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.IllegalArgumentException: Executor must not be null
at android.hardware.biometrics.BiometricPrompt$Builder.setNegativeButton(BiometricPrompt.java:182)
at androidx.biometric.BiometricFragment.onCreate(BiometricFragment.java:201)
at androidx.fragment.app.Fragment.performCreate(Fragment.java:2414)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManager.java:1418)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1784)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManager.java:1861)
at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3269)
at androidx.fragment.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:3223)
at androidx.fragment.app.FragmentController.dispatchCreate(FragmentController.java:190)
at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:369)
at androidx.appcompat.app.AppCompatActivity.onCreate(AppCompatActivity.java:85)
at com.varomoney.varo.presentation.base.BaseActivity.onCreate(BaseActivity.kt:73)
at com.varomoney.varo.presentation.launcher.LauncherActivity.onCreate(LauncherActivity.kt:59)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
I've looked and I'm passing com.google.android.gms.tasks.TaskExecutors.MAIN_THREAD for the executor in my use of the API.
[Deleted User] <[Deleted User]> #10
[Deleted User] <[Deleted User]> #11
1) Perform an authenticate
2) Hit home button before applying fingerprint
3) Return to the app, and apply fingerprint
I get:
Process:
java.lang.NullPointerException: Attempt to invoke interface method 'void java.util.concurrent.Executor.execute(java.lang.Runnable)' on a null object reference
at androidx.biometric.FingerprintHelperFragment$1.onAuthenticationSucceeded(FingerprintHelperFragment.java:128)
at androidx.core.hardware.fingerprint.FingerprintManagerCompat$1.onAuthenticationSucceeded(FingerprintManagerCompat.java:176)
at android.hardware.fingerprint.FingerprintManager$MyHandler.sendAuthenticatedSucceeded(FingerprintManager.java:961)
at android.hardware.fingerprint.FingerprintManager$MyHandler.handleMessage(FingerprintManager.java:882)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Doesn't seem to be remembering the executor that is passed in for some reason. Arguably this is probably a new bug and not the same one as in alpha02.
[Deleted User] <[Deleted User]> #12
1) Display authenticate dialog on Oreo. Don't apply fingerprint.
2) Go to developer settings > Running Services > Show Cached Processes, and kill the app in question.
3) Re-launch the app and just tap off the authenticate dialog.
4) App will crash with:
java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.biometric.BiometricPrompt$AuthenticationCallback.onAuthenticationError(int, java.lang.CharSequence)' on a null object reference
at androidx.biometric.FingerprintHelperFragment.sendErrorToClient(FingerprintHelperFragment.java:268)
at androidx.biometric.FingerprintHelperFragment.cancel(FingerprintHelperFragment.java:224)
at androidx.biometric.FingerprintDialogFragment.onCancel(FingerprintDialogFragment.java:215)
at android.app.Dialog$ListenersHandler.handleMessage(Dialog.java:1364)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
[Deleted User] <[Deleted User]> #13
[Deleted User] <[Deleted User]> #14
What I'm seeing when I run the test app:
1) App will begin with an Enroll button showing.
2) Tap the Enroll button.
3) Present fingerprint.
4) Should be able to tap the Login button to authenticate.
5) Login dialog will show.
6) Present fingerprint.
7) App will crash because the callback being used will be the 'Enroll' callback, not the 'Login' callback.
Open App again.
8) Login dialog will show.
9) Present fingerprint
10) Should see Authenticated toast.
11) Tap Login Button
12) Present Fingerprint
13) App will crash because the login callback (from the initial run) is being reused again, and it's stale.
I fully expect I could be doing something wrong with the API. I also apologize in advance, the app is code extracted from a much larger project and I removed all of the Dagger 2, and just brought in what I needed, so it's a bit of a mess.
I really can't explain the behaviour I'm seeing on alpha03, I hope this helps find out what's wrong.
ad...@google.com <ad...@google.com> #15
kc...@google.com <kc...@google.com> #16
1) clear steps to repro
2) full bugreport (not just the stacktrace), "adb bugreport issue.zip"
3) optionally, attach sample code to repro (less necessary if bugreport is there, but is still useful so I can see what you're doing / trying to do)
Comments on the previous comments
Starting on
#9) It's possible for your executor to be null, how did you reproduce this issue, was it the same as the steps in
aosp-androidx/samples/BiometricDemos/src/main/java/com/example/android/biometric/BiometricPromptDemo.java
#11) looks like a similar issue, can you try the suggestion above?
#12) I need more info, can you clarify what " tap off the authenticate dialog." means? Can you attach a full bugreport? Bits of stack traces are not enough to understand the issue
[Deleted User] <[Deleted User]> #18
In alpha03, as long as the library correctly handles fragments, and also correctly handles the callbacks given to it, I believe this bug report can be closed. Though the app attached in
to...@gmail.com <to...@gmail.com> #19
bi...@gmail.com <bi...@gmail.com> #20
Steps to reproduce:
1) Display authenticate dialog on Oreo. Don't apply fingerprint.
2) Perform 2a) or 2b). Performing 2a) I was able to reproduce issue more often comparing with similar 2b).
2a) Open bunch of heavy apps: youtube, gmail, playstore etc;
2b) Go to developer settings > Running Services > Show Cached Processes, and kill the app in question. ()
3) Re-launch the app and just tap off the authenticate dialog.
4) App will crash with:
java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.biometric.BiometricPrompt$AuthenticationCallback.onAuthenticationError(int, java.lang.CharSequence)' on a null object reference
In my opinion the reason of AuthenticationCallback == null is that
a) this callback is assigned in 2 places only (see code below);
b) when application is restored after process death, the retained fragment mFingerprintHelperFragment is also restored and sometimes when mLifecycleObserver.onResume() is called (see Place 1):
1) After this call:
mFingerprintHelperFragment = (FingerprintHelperFragment) mFragmentActivity.getSupportFragmentManager().findFragmentByTag(FINGERPRINT_HELPER_FRAGMENT_TAG);
mFingerprintHelperFragment sometimes is null because mFingerprintHelperFragment fragment transaction sometimes is not started yet and FragmentManager doesn't reference such a fragment;
2) Therefore this check returns false and callback is not set:
if (mFingerprintDialogFragment != null && mFingerprintHelperFragment != null) {
mFingerprintHelperFragment.setCallback(mExecutor, mAuthenticationCallback);
c) when BiometricPrompt.authenticate() is called (see Place 2), mFingerprintHelperFragment fragment transaction is already started and this check
if (mFingerprintHelperFragment == null)
fails (because mFingerprintHelperFragment is not null) and again callback is not set:
mFingerprintHelperFragment.setCallback(mExecutor, mAuthenticationCallback);
androidx.biometric.BiometricPrompt.java
Place 1.
private final LifecycleObserver mLifecycleObserver = new LifecycleObserver() {
...
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void onResume() {
...
mFingerprintHelperFragment = (FingerprintHelperFragment) mFragmentActivity
.getSupportFragmentManager().findFragmentByTag(
FINGERPRINT_HELPER_FRAGMENT_TAG);
if (mFingerprintDialogFragment != null && mFingerprintHelperFragment != null) {
mFingerprintHelperFragment.setCallback(mExecutor, mAuthenticationCallback);
Place 2.
private void authenticateInternal(@NonNull PromptInfo info, @Nullable CryptoObject crypto) {
...
if (mFingerprintHelperFragment == null) {
mFingerprintHelperFragment = FingerprintHelperFragment.newInstance();
mFingerprintHelperFragment.setCallback(mExecutor, mAuthenticationCallback);
}
ch...@gmail.com <ch...@gmail.com> #21
Let's get this fixed! These are some serious fragment bugs.
ja...@gmail.com <ja...@gmail.com> #22
```
override fun onPause() {
// WORKAROUND for
supportFragmentManager.fragments.filter { it is BiometricFragment || it is FingerprintDialogFragment }.forEach {
supportFragmentManager.beginTransaction().remove(it).commitNow()
}
super.onPause()
}
```
jo...@ori.berlin <jo...@ori.berlin> #23
```
fun FragmentManager.removeBiometricFragments() {
fragments.filter {
it is BiometricFragment || it is FingerprintDialogFragment || it is FingerprintHelperFragment
}.forEach {
beginTransaction().remove(it).commitNow()
}
}
```
jo...@ori.berlin <jo...@ori.berlin> #24
```
fun FragmentManager.removeBiometricFragments() {
findFragmentByTag("FingerprintDialogFragment")?.let {
(it as DialogFragment).dismiss()
}
findFragmentByTag("FingerprintHelperFragment")?.let {
beginTransaction().remove(it).commitNow()
}
findFragmentByTag("BiometricFragment")?.let {
beginTransaction().remove(it).commitNow()
}
}
```
gi...@googlemail.com <gi...@googlemail.com> #25
kc...@google.com <kc...@google.com> #26
sa...@stcpay.com.sa <sa...@stcpay.com.sa> #27
Besides, the workaround from #24 needs improvements. Before removing fragments;
fragment.setRetainInstance(false);
to avoid state saving for the biometric fragments.
kc...@google.com <kc...@google.com> #28
me...@gmail.com <me...@gmail.com> #29
ph...@gmail.com <ph...@gmail.com> #30
ph...@gmail.com <ph...@gmail.com> #31
ph...@gmail.com <ph...@gmail.com> #32
[Deleted User] <[Deleted User]> #33
[Deleted User] <[Deleted User]> #34
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getString(int)' on a null object reference
at androidx.biometric.BiometricFragment$2$1.run(BiometricFragment.java:86)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7045)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)
Description
Devices/Android versions reproduced on: Android P, and also Android M-O
Sometimes, when the Biometric dialogs are displayed, and then the app is exited (with back button), the code inside the various fragments, BiometricFragment, FingerprintHelperFragment, and FingerprintDialogFragment doesn't properly clean up.
For example this code in FingerprintHelperFragment:
/**
* Remove the fragment so that resources can be freed.
*/
void cleanup() {
if (getActivity() != null) {
getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
}
}
can be instrumented with a breakpoint, and shown that sometimes getActivity() is null, and so the fragment transaction to remove the fragment never happens. So, the next time the app tries to authenticate with the BiometricPrompt, the app will crash because the fragment has already been added.
The exception on Android M-O looks like:
java.lang.IllegalStateException: Fragment already added: FingerprintDialogFragment{8d6c61c #3 FingerprintDialogFragment}
at androidx.fragment.app.FragmentManagerImpl.addFragment(FragmentManager.java:1916)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:765)
at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
at androidx.fragment.app.FragmentManagerImpl$1.run(FragmentManager.java:733)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
and on Android P it will reference BiometricFragment instead.
Attempts to search for and remove fragments at runtime, before a Biometric prompt operation were attempted:
val fm = (context as FragmentActivity).supportFragmentManager
fm.fragments.find {
it is BiometricFragment || it is FingerprintDialogFragment
}.also { fragment ->
fragment?.let {
fm.beginTransaction().remove(it).commitAllowingStateLoss()
}
}
However, then the exception on Android M-O mentions FingerprintHelperFragment instead.
Which led to an attempt to mitigate this exception:
fm.fragments.find {
it is FingerprintHelperFragment
}.also { fragment ->
fragment?.let { fm.beginTransaction().remove(it).commitAllowingStateLoss() }
}
Which caused the fingerprint operations to exit with an error 5, that the operation had been cancelled.
Here is examples of how we are using the API:
fun enroll(context: Activity, username: String, refreshToken: String): Completable =
Completable.create { emitter ->
// AN-939
cleanupBiometricsFragment(context)
val callback = makeAuthenticationCallback {
try {
storeCredentials(username, refreshToken)
preferenceManager.putBoolean(PREF_BIOMETRICS_ENROLLED, true)
preferenceManager.putString(PREF_ENROLLED_EMAIL, username)
// This is what the Settings switch uses
preferenceManager.biometricsEnabled.set(true)
emitter.onComplete()
} catch (ex: Throwable) {
emitter.onError(ex)
}
}
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(context.getString(R.string.biometrics_enroll_title))
.setDescription(context.getString(R.string.biometrics_enroll_subtitle))
.setNegativeButtonText(context.getString(R.string.biometrics_cancel))
.build()
try {
BiometricPrompt(context as FragmentActivity, MAIN_THREAD, callback)
.authenticate(promptInfo)
} catch (ex: Throwable) {
preferenceManager.putBoolean(PREF_BIOMETRICS_ENROLLED, false)
emitter.onError(ex)
}
}
Not sure, from the API if there's some way to ensure that everything is cleaned up properly at the end of the callback. I tried calling biometricPrompt.cancelAuthentication() but that also does not fix this issue.
If required, will try to make a reproduction in a small test app to demonstrate the problem.