Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix backup looping same keys #6585

Merged
merged 3 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/6585.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix backup saving several times the same keys
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
Expand Down Expand Up @@ -56,7 +55,6 @@ import java.util.concurrent.CountDownLatch
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
@LargeTest
@Ignore
class KeysBackupTest : InstrumentedTest {

@get:Rule val rule = RetryTestRule(3)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,10 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.logger.LoggerTag
import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import timber.log.Timber
import java.util.Timer
import java.util.TimerTask
import javax.inject.Inject

internal data class InboundGroupSessionHolder(
Expand Down Expand Up @@ -57,18 +54,13 @@ internal class InboundGroupSessionStore @Inject constructor(
if (oldValue != null) {
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
Timber.tag(loggerTag.value).v("## Inbound: entryRemoved ${oldValue.wrapper.roomId}-${oldValue.wrapper.senderKey}")
store.storeInboundGroupSessions(listOf(oldValue).map { it.wrapper })
// store.storeInboundGroupSessions(listOf(oldValue).map { it.wrapper })
oldValue.wrapper.session.releaseSession()
}
}
}
}

private val timer = Timer()
private var timerTask: TimerTask? = null

private val dirtySession = mutableListOf<InboundGroupSessionHolder>()

@Synchronized
fun clear() {
sessionCache.evictAll()
Expand All @@ -90,7 +82,6 @@ internal class InboundGroupSessionStore @Inject constructor(
@Synchronized
fun replaceGroupSession(old: InboundGroupSessionHolder, new: InboundGroupSessionHolder, sessionId: String, senderKey: String) {
Timber.tag(loggerTag.value).v("## Replacing outdated session ${old.wrapper.roomId}-${old.wrapper.senderKey}")
dirtySession.remove(old)
store.removeInboundGroupSession(sessionId, senderKey)
sessionCache.remove(CacheKey(sessionId, senderKey))

Expand All @@ -107,33 +98,14 @@ internal class InboundGroupSessionStore @Inject constructor(

private fun internalStoreGroupSession(holder: InboundGroupSessionHolder, sessionId: String, senderKey: String) {
Timber.tag(loggerTag.value).v("## Inbound: getInboundGroupSession mark as dirty ${holder.wrapper.roomId}-${holder.wrapper.senderKey}")
// We want to batch this a bit for performances
dirtySession.add(holder)

if (sessionCache[CacheKey(sessionId, senderKey)] == null) {
// first time seen, put it in memory cache while waiting for batch insert
// If it's already known, no need to update cache it's already there
sessionCache.put(CacheKey(sessionId, senderKey), holder)
}

timerTask?.cancel()
timerTask = object : TimerTask() {
override fun run() {
batchSave()
}
}
timer.schedule(timerTask!!, 300)
}

@Synchronized
private fun batchSave() {
val toSave = mutableListOf<InboundGroupSessionHolder>().apply { addAll(dirtySession) }
dirtySession.clear()
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
Timber.tag(loggerTag.value).v("## Inbound: getInboundGroupSession batching save of ${toSave.size}")
tryOrNull {
store.storeInboundGroupSessions(toSave.map { it.wrapper })
}
store.storeInboundGroupSessions(listOf(holder.wrapper))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,6 @@ internal class MXOlmDevice @Inject constructor(
}
replayAttackMap[messageIndexKey] = eventId
}
inboundGroupSessionStore.storeInBoundGroupSession(sessionHolder, sessionId, senderKey)
val payload = try {
val adapter = MoshiProvider.providesMoshi().adapter<JsonDict>(JSON_DICT_PARAMETERIZED_TYPE)
val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,8 @@ internal class DefaultKeysBackupService @Inject constructor(

// Mark keys as backed up
cryptoStore.markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers)
// we can release the sessions now
olmInboundGroupSessionWrappers.onEach { it.session.releaseSession() }

if (olmInboundGroupSessionWrappers.size < KEY_BACKUP_SEND_KEYS_MAX_COUNT) {
Timber.v("backupKeys: All keys have been backed up")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -763,11 +763,17 @@ internal class RealmCryptoStore @Inject constructor(
// } ?: false
val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionIdentifier, wrapper.sessionData.senderKey)

val existing = realm.where<OlmInboundGroupSessionEntity>()
.equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key)
.findFirst()

val realmOlmInboundGroupSession = OlmInboundGroupSessionEntity().apply {
primaryKey = key
store(wrapper)
backedUp = existing?.backedUp ?: false
}
Timber.i("## CRYPTO | shouldShareHistory: ${wrapper.sessionData.sharedHistory} for $key")

Timber.v("## CRYPTO | shouldShareHistory: ${wrapper.sessionData.sharedHistory} for $key")
realm.insertOrUpdate(realmOlmInboundGroupSession)
}
}
Expand Down