[X] This issue is not security related and can safely be disclosed publicly on GitHub
Java version
8
Android version
SDK 31
Android SDK version
13.0.0
Installation platform & version
Gradle 7.0.0
Package
Core & AppEvents
Goals
No blocking HTTP calls on the main thread
Expected results
The application is free of ANRs.
Actual results
We occasionally see ANRs reported in our error collection service with the following relevant callstacks.
Thread 2 - MainThread-UE4 - (TIMED_WAITING)
at jdk.internal.misc.Unsafe.park(Unsafe.java:-2)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:234)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2211)
at n1.c.c(OidcSecurityUtil.kt:7)
at com.facebook.AuthenticationToken.a(AuthenticationToken.kt:1)
at com.facebook.AuthenticationToken.<init>(AuthenticationToken.kt:13)
at com.facebook.login.LoginMethodHandler$a.d(LoginMethodHandler.kt:4)
at com.facebook.login.NativeAppLoginMethodHandler.w(NativeAppLoginMethodHandler.kt:4)
at com.facebook.login.NativeAppLoginMethodHandler.x(NativeAppLoginMethodHandler.kt:3)
at com.facebook.login.NativeAppLoginMethodHandler.j(NativeAppLoginMethodHandler.kt:15)
at com.facebook.login.LoginClient.u(LoginClient.kt:8)
at com.facebook.login.j.onActivityResult(LoginFragment.kt:2)
at androidx.fragment.app.FragmentManager$j.b(FragmentManager.java:9)
at androidx.fragment.app.FragmentManager$j.a(FragmentManager.java:1)
at androidx.activity.result.ActivityResultRegistry.d(ActivityResultRegistry.java:3)
at androidx.activity.result.ActivityResultRegistry.b(ActivityResultRegistry.java:2)
at androidx.activity.ComponentActivity.onActivityResult(ComponentActivity.java:1)
at androidx.fragment.app.d.onActivityResult(FragmentActivity.java:2)
at android.app.Activity.dispatchActivityResult(Activity.java:8630)
at android.app.ActivityThread.deliverResults(ActivityThread.java:5446)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:5492)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:54)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2282)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:210)
at android.os.Looper.loop(Looper.java:299)
at android.app.ActivityThread.main(ActivityThread.java:8250)
at java.lang.reflect.Method.invoke(Method.java:-2)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1045)
Thread 985 - pool-13-thread-1 - (RUNNABLE)
at libcore.io.Linux.connect(Linux.java:-2)
at libcore.io.ForwardingOs.connect(ForwardingOs.java:201)
at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:158)
at libcore.io.ForwardingOs.connect(ForwardingOs.java:201)
at libcore.io.IoBridge.connectErrno(IoBridge.java:201)
at libcore.io.IoBridge.connect(IoBridge.java:179)
at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:142)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
at java.net.Socket.connect(Socket.java:646)
at com.android.okhttp.internal.Platform.connectSocket(Platform.java:182)
at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:145)
at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:116)
at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:186)
at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128)
at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:289)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:232)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:131)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:262)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:219)
at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:30)
at com.facebook.GraphRequest$c.K(GraphRequest.kt:19)
at com.facebook.GraphRequest$c.M(GraphRequest.kt:7)
at com.facebook.GraphRequest$c.j(GraphRequest.kt:2)
at com.facebook.GraphRequest$c.i(GraphRequest.kt:1)
at com.facebook.GraphRequest$c.k(GraphRequest.kt:1)
at com.facebook.GraphRequest$c.h(GraphRequest.kt:1)
at com.facebook.GraphRequest.k(GraphRequest.kt:1)
at p0.l.u(AppEventQueue.kt:11)
at p0.l.n(AppEventQueue.kt:3)
at p0.l.o(AppEventQueue.kt:3)
at p0.l.b(Unknown)
at p0.k.run(Unknown)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:463)
at java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:307)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
at java.lang.Thread.run(Thread.java:1012)
It appears that what is happening is that during a login attempt the following happens.
On the main thread the OS calls onActivityResult() in facebook-common/src/main/java/com/facebook/login/LoginFragment.kt
This eventually results in the NativeLoginMethodHandler creating an AuthenticationToken via one of the LoginMethodHandler's static factory methods
The isSignatureValid() check calls into OidcSecurityUtil.getRawKeyFromEndpoint()
The OidcSecurityUtil.getRawKeyFromEndpoint() function looks like this:
fun getRawKeyFromEndPoint(kid: String): String? {
val host = "www.${FacebookSdk.getFacebookDomain()}"
val openIdKeyUrl = URL("https", host, OPENID_KEYS_PATH)
val lock = ReentrantLock()
val condition = lock.newCondition()
var result: String? = null
FacebookSdk.getExecutor().execute {
val connection = openIdKeyUrl.openConnection() as HttpURLConnection
try {
val data = connection.inputStream.bufferedReader().readText()
connection.inputStream.close()
result = JSONObject(data).optString(kid)
} catch (_ex: Exception) {
// return null if ANY exception happens
Log.d(OidcSecurityUtil.javaClass.name, _ex.message ?: "Error getting public key")
} finally {
connection.disconnect()
lock.withLock { condition.signal() }
}
}
lock.withLock { condition.await(TIMEOUT_IN_MILLISECONDS, TimeUnit.MILLISECONDS) }
return result
}
This results in a blocking wait on the main thread until the HTTP request can be completed. It seems that this sometimes takes more than 5 seconds to complete and results in the reported ANR.
Checklist before submitting a bug report
Java version
8
Android version
SDK 31
Android SDK version
13.0.0
Installation platform & version
Gradle 7.0.0
Package
Core & AppEvents
Goals
No blocking HTTP calls on the main thread
Expected results
The application is free of ANRs.
Actual results
We occasionally see ANRs reported in our error collection service with the following relevant callstacks.
It appears that what is happening is that during a login attempt the following happens.
The OidcSecurityUtil.getRawKeyFromEndpoint() function looks like this:
This results in a blocking wait on the main thread until the HTTP request can be completed. It seems that this sometimes takes more than 5 seconds to complete and results in the reported ANR.
Steps to reproduce
No response
Code samples & details
No response