From 5a12eedc2c27c3946216e9fd35128e99255038b6 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 6 Jul 2020 09:40:03 -0700 Subject: [PATCH] Prevent possible deadlock with LiveRecipientCache. Thread A: DirectoryHelper#updateContactsDatabase() acquires database lock Thread B: LiveRecipientCache#getSelf() acquires lock on LiveRecipientCache Thread A: DirectoryHelper#updateContactsDatabase() calls Recipient.externalContact(), which eventually needs LiveRecipientCache lock Thread B: Needs to read the database (e.g. line 120) to get information about itself So A has the DB lock but needs the LiveRecipientCache lock, and B has the LiveRecipientCache lock but needs the DB lock. In general, we need to avoid acquiring any new locks in a transaction, but for now, this specific instance looks like it could be solved by using a unique lock for LiveRecipientCache#getSelf(). --- .../securesms/recipients/LiveRecipientCache.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipientCache.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipientCache.java index 267198cb6..2ad40e741 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipientCache.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/LiveRecipientCache.java @@ -4,6 +4,7 @@ import android.annotation.SuppressLint; import android.content.Context; import androidx.annotation.AnyThread; +import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.lifecycle.MutableLiveData; @@ -33,11 +34,14 @@ public final class LiveRecipientCache { private static final int CACHE_MAX = 1000; private static final int CACHE_WARM_MAX = 500; + private static final Object SELF_LOCK = new Object(); + private final Context context; private final RecipientDatabase recipientDatabase; private final Map recipients; private final LiveRecipient unknown; + @GuardedBy("SELF_LOCK") private RecipientId localRecipientId; private boolean warmedUp; @@ -111,7 +115,7 @@ public final class LiveRecipientCache { } @NonNull Recipient getSelf() { - synchronized (this) { + synchronized (SELF_LOCK) { if (localRecipientId == null) { UUID localUuid = TextSecurePreferences.getLocalUuid(context); String localE164 = TextSecurePreferences.getLocalNumber(context);