Fix possible dlist sync crash, improved debugging.

Fixes #12795
main
Greyson Parrelli 2023-02-22 14:18:20 -05:00
rodzic b689ea62a6
commit 691ab353da
4 zmienionych plików z 89 dodań i 42 usunięć

Wyświetl plik

@ -73,7 +73,7 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign
}
}
private object ListTable {
object ListTable {
const val TABLE_NAME = "distribution_list"
const val ID = "_id"

Wyświetl plik

@ -1170,36 +1170,47 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
* @return All storage IDs for synced records, excluding the ones that need to be deleted.
*/
fun getContactStorageSyncIdsMap(): Map<RecipientId, StorageId> {
val inPart = "(?, ?)"
val args = SqlUtil.buildArgs(GroupType.NONE.id, Recipient.self().id, GroupType.SIGNAL_V1.id, GroupType.DISTRIBUTION_LIST.id)
val query = """
$STORAGE_SERVICE_ID NOT NULL AND (
($GROUP_TYPE = ? AND $SERVICE_ID NOT NULL AND $ID != ?)
OR
$GROUP_TYPE IN $inPart
)
""".trimIndent()
val out: MutableMap<RecipientId, StorageId> = HashMap()
readableDatabase.query(TABLE_NAME, arrayOf(ID, STORAGE_SERVICE_ID, GROUP_TYPE), query, args, null, null, null).use { cursor ->
while (cursor != null && cursor.moveToNext()) {
val id = RecipientId.from(cursor.requireLong(ID))
val encodedKey = cursor.requireNonNullString(STORAGE_SERVICE_ID)
val groupType = GroupType.fromId(cursor.requireInt(GROUP_TYPE))
val key = Base64.decodeOrThrow(encodedKey)
readableDatabase
.select(ID, STORAGE_SERVICE_ID, GROUP_TYPE)
.from(TABLE_NAME)
.where(
"""
$STORAGE_SERVICE_ID NOT NULL AND (
($GROUP_TYPE = ? AND $SERVICE_ID NOT NULL AND $ID != ?)
OR
$GROUP_TYPE = ?
OR
$DISTRIBUTION_LIST_ID NOT NULL AND $DISTRIBUTION_LIST_ID IN (
SELECT ${DistributionListTables.ListTable.ID}
FROM ${DistributionListTables.ListTable.TABLE_NAME}
)
)
""".toSingleLine(),
GroupType.NONE.id,
Recipient.self().id,
GroupType.SIGNAL_V1.id
)
.run()
.use { cursor ->
while (cursor.moveToNext()) {
val id = RecipientId.from(cursor.requireLong(ID))
val encodedKey = cursor.requireNonNullString(STORAGE_SERVICE_ID)
val groupType = GroupType.fromId(cursor.requireInt(GROUP_TYPE))
val key = Base64.decodeOrThrow(encodedKey)
when (groupType) {
GroupType.NONE -> out[id] = StorageId.forContact(key)
GroupType.SIGNAL_V1 -> out[id] = StorageId.forGroupV1(key)
GroupType.DISTRIBUTION_LIST -> out[id] = StorageId.forStoryDistributionList(key)
else -> throw AssertionError()
when (groupType) {
GroupType.NONE -> out[id] = StorageId.forContact(key)
GroupType.SIGNAL_V1 -> out[id] = StorageId.forGroupV1(key)
GroupType.DISTRIBUTION_LIST -> out[id] = StorageId.forStoryDistributionList(key)
else -> throw AssertionError()
}
}
}
}
for (id in groups.getAllGroupV2Ids()) {
val recipient = Recipient.externalGroupExact(id!!)
val recipient = Recipient.externalGroupExact(id)
val recipientId = recipient.id
val existing: RecipientRecord = getRecordForSync(recipientId) ?: throw AssertionError()
val key = existing.storageId ?: throw AssertionError()

Wyświetl plik

@ -9,7 +9,6 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Base64;
import org.signal.core.util.SetUtil;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.storage.SignalContactRecord;
import org.whispersystems.signalservice.api.storage.SignalStorageManifest;
import org.whispersystems.signalservice.api.storage.SignalStorageRecord;
@ -103,22 +102,6 @@ public final class StorageSyncValidations {
}
private static void validateManifestAndInserts(@NonNull SignalStorageManifest manifest, @NonNull List<SignalStorageRecord> inserts, @NonNull Recipient self) {
Set<StorageId> allSet = new HashSet<>(manifest.getStorageIds());
Set<StorageId> insertSet = new HashSet<>(Stream.of(inserts).map(SignalStorageRecord::getId).toList());
Set<ByteBuffer> rawIdSet = Stream.of(allSet).map(id -> ByteBuffer.wrap(id.getRaw())).collect(Collectors.toSet());
if (allSet.size() != manifest.getStorageIds().size()) {
throw new DuplicateStorageIdError();
}
if (rawIdSet.size() != allSet.size()) {
throw new DuplicateRawIdError();
}
if (inserts.size() > insertSet.size()) {
throw new DuplicateInsertInWriteError();
}
int accountCount = 0;
for (StorageId id : manifest.getStorageIds()) {
accountCount += id.getType() == ManifestRecord.Identifier.Type.ACCOUNT_VALUE ? 1 : 0;
@ -132,6 +115,43 @@ public final class StorageSyncValidations {
throw new MissingAccountError();
}
Set<StorageId> allSet = new HashSet<>(manifest.getStorageIds());
Set<StorageId> insertSet = new HashSet<>(Stream.of(inserts).map(SignalStorageRecord::getId).toList());
Set<ByteBuffer> rawIdSet = Stream.of(allSet).map(id -> ByteBuffer.wrap(id.getRaw())).collect(Collectors.toSet());
if (allSet.size() != manifest.getStorageIds().size()) {
throw new DuplicateStorageIdError();
}
if (rawIdSet.size() != allSet.size()) {
List<StorageId> ids = manifest.getStorageIdsByType().get(ManifestRecord.Identifier.Type.CONTACT_VALUE);
if (ids.size() != new HashSet<>(ids).size()) {
throw new DuplicateContactIdError();
}
ids = manifest.getStorageIdsByType().get(ManifestRecord.Identifier.Type.GROUPV1_VALUE);
if (ids.size() != new HashSet<>(ids).size()) {
throw new DuplicateGroupV1IdError();
}
ids = manifest.getStorageIdsByType().get(ManifestRecord.Identifier.Type.GROUPV2_VALUE);
if (ids.size() != new HashSet<>(ids).size()) {
throw new DuplicateGroupV2IdError();
}
ids = manifest.getStorageIdsByType().get(ManifestRecord.Identifier.Type.STORY_DISTRIBUTION_LIST_VALUE);
if (ids.size() != new HashSet<>(ids).size()) {
throw new DuplicateDistributionListIdError();
}
throw new DuplicateRawIdAcrossTypesError();
}
if (inserts.size() > insertSet.size()) {
throw new DuplicateInsertInWriteError();
}
for (SignalStorageRecord insert : inserts) {
if (!allSet.contains(insert.getId())) {
throw new InsertNotPresentInFullIdSetError();
@ -161,7 +181,19 @@ public final class StorageSyncValidations {
private static final class DuplicateStorageIdError extends Error {
}
private static final class DuplicateRawIdError extends Error {
private static final class DuplicateRawIdAcrossTypesError extends Error {
}
private static final class DuplicateContactIdError extends Error {
}
private static final class DuplicateGroupV1IdError extends Error {
}
private static final class DuplicateGroupV2IdError extends Error {
}
private static final class DuplicateDistributionListIdError extends Error {
}
private static final class DuplicateInsertInWriteError extends Error {

Wyświetl plik

@ -69,6 +69,10 @@ public class SignalStorageManifest {
}
}
public Map<Integer, List<StorageId>> getStorageIdsByType() {
return storageIdsByType;
}
public byte[] serialize() {
List<ManifestRecord.Identifier> ids = new ArrayList<>(storageIds.size());