Matrix crates to 0.19: drops a double free and a to-device panic (#249)
matrix-sdk-crypto, -common and -sqlite 0.18 -> 0.19.1, with the ruma versions they pin (common 0.20, client-api 0.25, events 0.35). Clears: - RUSTSEC-2026-0292 (imbl-sized-chunks 0.1.3, double free / use after free on a panicking Drop) - now 0.2.0 through imbl 7 - RUSTSEC-2025-0167 / RUSTSEC-2026-0247 (bitmaps 3.2.1) - no longer in the tree - RUSTSEC-2026-0318 (matrix-sdk-crypto 0.18.0, custom to-device sends can panic - the feature Element Call's keys use) matrix-sdk-sqlite 0.19 needs rusqlite 0.40, and only one bundled SQLite can link, so rusqlite moves 0.37 -> 0.40 and arti 0.44 -> 0.47 (the first tor-dirmgr that accepts it). No code changes for either. Code changes: OlmMachine::with_store is gone, OlmMachineBuilder replaces it; vodozemac 0.11's CheckCode::to_digit takes a DigitMode, and the ECIES (MSC4108) QR sign-in keeps the rendering it had, AllowLeadingZero. arti 0.47 uses saturating-time 0.5.0, which still spins forever on Windows. Re-vendored at 0.5.0 with the same fix. Checked: OSV finds none of the four left and nothing new. A copy of a real crypto store opens under 0.19 with the same identity keys and all 32 room keys, and 0.18 can still open it after 0.19's schema migration (17 -> 19), so a rollback does not strand it. Tor bootstraps and reaches an onion over TLS on Linux and, cross-built, on the Windows VM. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Generated
+792
-475
File diff suppressed because it is too large
Load Diff
+12
-12
@@ -17,7 +17,7 @@ socket2 = "0.6"
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros", "net", "sync", "time", "io-util", "fs", "signal"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
rusqlite = { version = "0.37", features = ["bundled"] }
|
||||
rusqlite = { version = "0.40", features = ["bundled"] }
|
||||
toml = "0.8"
|
||||
fs2 = "0.4"
|
||||
tracing = "0.1"
|
||||
@@ -57,8 +57,8 @@ image = { version = "0.25", default-features = false, features = ["png", "gif"]
|
||||
chrono = { version = "0.4", default-features = false, features = ["std"] }
|
||||
|
||||
# Sneedchat backend (SneedChat over embedded Tor)
|
||||
arti-client = { version = "0.44", default-features = false, features = ["tokio", "rustls", "onion-service-client", "compression"] }
|
||||
tor-rtcompat = { version = "0.44", features = ["tokio", "rustls"] }
|
||||
arti-client = { version = "0.47", default-features = false, features = ["tokio", "rustls", "onion-service-client", "compression"] }
|
||||
tor-rtcompat = { version = "0.47", features = ["tokio", "rustls"] }
|
||||
tokio-socks = "0.5"
|
||||
hyper = { version = "1", features = ["client", "http1"] }
|
||||
hyper-util = { version = "0.1", features = ["tokio", "client-legacy", "http1"] }
|
||||
@@ -80,22 +80,22 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["ring",
|
||||
# Element distributes them - see backend/matrix/crypto.rs.
|
||||
# `qrcode` is what lets a verification be done by scanning rather than by
|
||||
# reading emoji aloud - the method Element offers first and most people use.
|
||||
matrix-sdk-crypto = { version = "0.18", features = ["experimental-send-custom-to-device", "qrcode"] }
|
||||
# Pinned to exactly what matrix-sdk-crypto 0.18 resolves to, for the same
|
||||
matrix-sdk-crypto = { version = "=0.19.1", features = ["experimental-send-custom-to-device", "qrcode"] }
|
||||
# Pinned to exactly what matrix-sdk-crypto 0.19 resolves to, for the same
|
||||
# reason the ruma crates below are: this is where its own response types live
|
||||
# - the verification state a decrypted message carries among them - and a
|
||||
# version skew here is a type mismatch rather than a missing feature.
|
||||
matrix-sdk-common = "=0.18.0"
|
||||
matrix-sdk-sqlite = { version = "0.18", default-features = false, features = ["crypto-store", "bundled"] }
|
||||
# Pinned exactly to whatever matrix-sdk-crypto 0.18.0 itself resolves to
|
||||
matrix-sdk-common = "=0.19.1"
|
||||
matrix-sdk-sqlite = { version = "=0.19.1", default-features = false, features = ["crypto-store", "bundled"] }
|
||||
# Pinned exactly to whatever matrix-sdk-crypto 0.19.1 itself resolves to
|
||||
# (confirmed via `cargo tree -p matrix-sdk-crypto`) - OlmMachine's public
|
||||
# API takes ruma-common types (UserId/RoomId/DeviceId) and wraps
|
||||
# ruma-client-api request structs directly, and Cargo will happily
|
||||
# resolve two incompatible ruma-common versions side by side without
|
||||
# erroring until the specific call sites that mix them - i.e. everywhere.
|
||||
ruma-common = "=0.19.0"
|
||||
ruma-client-api = "=0.24.0"
|
||||
ruma-events = "=0.34.0"
|
||||
ruma-common = "=0.20.0"
|
||||
ruma-client-api = "=0.25.0"
|
||||
ruma-events = "=0.35.0"
|
||||
js_int = "=0.2.2"
|
||||
# Discord voice is this crate's own connection (backend/discord/voiceconn.rs):
|
||||
# cpal for the sound devices, opus2 for the codec. Songbird carried calls until
|
||||
@@ -117,7 +117,7 @@ libc = "0.2"
|
||||
|
||||
# Tor's certificate validation hangs on Windows without this.
|
||||
#
|
||||
# saturating-time 0.4.0 finds a type's limit by halving a step until the
|
||||
# saturating-time 0.5.0 (as 0.4.0 did) finds a type's limit by halving a step until the
|
||||
# arithmetic fails, and only shrinks that step when it sees None. On Windows a
|
||||
# SystemTime is a FILETIME with 100ns granularity, so subtracting 1ns returns
|
||||
# Some(unchanged) rather than None - the None branch becomes unreachable at
|
||||
|
||||
@@ -36,7 +36,7 @@ use js_int::UInt;
|
||||
use matrix_sdk_crypto::olm::EncryptionSettings;
|
||||
use matrix_sdk_crypto::types::requests::AnyOutgoingRequest;
|
||||
use matrix_sdk_crypto::{
|
||||
CollectStrategy, DecryptionSettings, EncryptionSyncChanges, OlmMachine, TrustRequirement,
|
||||
CollectStrategy, DecryptionSettings, EncryptionSyncChanges, OlmMachine, OlmMachineBuilder, TrustRequirement,
|
||||
};
|
||||
use matrix_sdk_sqlite::SqliteCryptoStore;
|
||||
// For `event_type()` on a verification step's content - the wire needs the
|
||||
@@ -128,7 +128,7 @@ impl CryptoSession {
|
||||
let dir = data_dir.join("matrix-crypto").join(sanitize_account_id(account_id));
|
||||
tokio::fs::create_dir_all(&dir).await.context("creating matrix crypto store directory")?;
|
||||
let store = SqliteCryptoStore::open(&dir, None).await.context("opening matrix crypto store")?;
|
||||
let machine = match OlmMachine::with_store(user_id, device_id, store, None).await {
|
||||
let machine = match OlmMachineBuilder::new(user_id, device_id).with_crypto_store(store).build().await {
|
||||
Ok(machine) => machine,
|
||||
// A store belonging to a different device, which happens whenever
|
||||
// somebody signs in again: the homeserver mints a new device, and
|
||||
@@ -149,7 +149,7 @@ impl CryptoSession {
|
||||
forget(data_dir, account_id);
|
||||
tokio::fs::create_dir_all(&dir).await.context("recreating matrix crypto store directory")?;
|
||||
let store = SqliteCryptoStore::open(&dir, None).await.context("reopening matrix crypto store")?;
|
||||
OlmMachine::with_store(user_id, device_id, store, None)
|
||||
OlmMachineBuilder::new(user_id, device_id).with_crypto_store(store).build()
|
||||
.await
|
||||
.context("initializing OlmMachine on a fresh store")?
|
||||
}
|
||||
@@ -184,7 +184,7 @@ impl CryptoSession {
|
||||
let dir = data_dir.join("matrix-crypto").join(sanitize_account_id(account_id));
|
||||
tokio::fs::create_dir_all(&dir).await.context("creating matrix crypto store directory")?;
|
||||
let store = SqliteCryptoStore::open(&dir, None).await.context("opening matrix crypto store")?;
|
||||
let machine = OlmMachine::with_store(user_id, device_id, store, Some(account))
|
||||
let machine = OlmMachineBuilder::new(user_id, device_id).with_crypto_store(store).with_custom_account(Some(account)).build()
|
||||
.await
|
||||
.context("initializing OlmMachine with the QR sign-in's account")?;
|
||||
if let Some(secrets) = secrets {
|
||||
|
||||
@@ -33,6 +33,7 @@ use anyhow::{bail, Context, Result};
|
||||
use matrix_sdk_crypto::types::qr_login::{Msc4108IntentData, QrCodeData};
|
||||
use matrix_sdk_crypto::types::SecretsBundle;
|
||||
use matrix_sdk_crypto::vodozemac::ecies::{Ecies, EstablishedEcies, InitialMessage, Message};
|
||||
use matrix_sdk_crypto::vodozemac::hpke::DigitMode;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use super::*;
|
||||
@@ -139,7 +140,10 @@ async fn try_qr_login(state: &AppState, login_id: &str, homeserver_url: &str) ->
|
||||
.await
|
||||
.context("nobody entered the code in time")?
|
||||
.context("the sign-in was cancelled")?;
|
||||
if typed != channel.ecies.check_code().to_digit() {
|
||||
// The original MSC4108 rendering, which is the one this ECIES channel
|
||||
// speaks: a leading zero allowed. (MSC4388's HPKE channel forbids one;
|
||||
// vodozemac now asks which is meant.)
|
||||
if typed != channel.ecies.check_code().to_digit(DigitMode::AllowLeadingZero) {
|
||||
let _ = channel.send_json(failure("user_cancelled")).await;
|
||||
bail!("those digits do not match the ones on your phone - start again, and check you scanned this screen");
|
||||
}
|
||||
@@ -496,7 +500,7 @@ mod tests {
|
||||
let phone = Ecies::new().establish_outbound_channel(moho_key, LOGIN_INITIATE.as_bytes()).unwrap();
|
||||
let inbound = moho.establish_inbound_channel(&InitialMessage::decode(&phone.message.encode()).unwrap()).unwrap();
|
||||
assert_eq!(inbound.message, LOGIN_INITIATE.as_bytes());
|
||||
assert_eq!(inbound.ecies.check_code().to_digit(), phone.ecies.check_code().to_digit());
|
||||
assert_eq!(inbound.ecies.check_code().to_digit(DigitMode::AllowLeadingZero), phone.ecies.check_code().to_digit(DigitMode::AllowLeadingZero));
|
||||
|
||||
let (mut moho, mut phone) = (inbound.ecies, phone.ecies);
|
||||
let sealed = moho.encrypt(LOGIN_OK.as_bytes()).encode();
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/target
|
||||
Vendored
+48
-8
@@ -1,14 +1,54 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
name = "saturating-time"
|
||||
version = "0.4.0"
|
||||
authors = ["Clara Engler"]
|
||||
edition = "2024"
|
||||
rust-version = "1.85"
|
||||
rust-version = "1.92"
|
||||
name = "saturating-time"
|
||||
version = "0.5.0"
|
||||
authors = [
|
||||
"The Tor Project, Inc.",
|
||||
"Clara Engler <[email protected]>",
|
||||
]
|
||||
build = false
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "A trait for limits and saturations on types inside std::time"
|
||||
repository = "https://codeberg.org/cve/saturating-time"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"time",
|
||||
"system-time",
|
||||
"instant",
|
||||
"saturating",
|
||||
]
|
||||
categories = [
|
||||
"date-and-time",
|
||||
"os",
|
||||
"rust-patterns",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
keywords = ["time", "system-time", "instant", "saturating"]
|
||||
categories = ["date-and-time", "os", "rust-patterns"]
|
||||
repository = "https://gitlab.torproject.org/tpo/core/arti.git"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[features]
|
||||
nightly = []
|
||||
full = ["web-time-compat/full"]
|
||||
|
||||
[lib]
|
||||
name = "saturating_time"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies.web-time-compat]
|
||||
version = "0.3.0"
|
||||
|
||||
Vendored
+14
-9
@@ -32,16 +32,13 @@ However, this crate implements this trait for two structures:
|
||||
|
||||
## Example
|
||||
|
||||
Add the following to your `Cargo.toml`:
|
||||
```toml
|
||||
[dependencies]
|
||||
saturating-time = "0.4.0"
|
||||
```
|
||||
Add `saturating-time` to your `Cargo.toml`.
|
||||
|
||||
If you use Rust nightly, you may want to do:
|
||||
If you use Rust nightly, you may want to additionally configure the following
|
||||
in your `.cargo/config.toml`:
|
||||
```toml
|
||||
[dependencies]
|
||||
saturating-time = { version = "0.4.0", features = ["nightly"] }
|
||||
[build]
|
||||
rustflags = ["--cfg", "saturating_time_nightly"]
|
||||
```
|
||||
|
||||
Now, you can use `saturating-time` in your code:
|
||||
@@ -95,7 +92,15 @@ None yet.
|
||||
[^5]: <https://github.com/rust-lang/rust/pull/151200>
|
||||
[^6]: <https://github.com/rust-lang/rust/issues/151199>
|
||||
|
||||
## History
|
||||
|
||||
Up until (and including) version 0.4.0, `saturating-time` was maintained as an
|
||||
independent project hosted on [Codeberg](https://codeberg.org/cve/saturating-time).
|
||||
|
||||
Starting with version 0.5.0, it has been incorporated into the
|
||||
[Arti](https://arti.torproject.org) and is therefore maintained by
|
||||
*The Tor Project, Inc.*
|
||||
|
||||
## License and Copyright
|
||||
|
||||
This crate is licensed under `MIT OR Apache-2.0`.
|
||||
See the respective `LICENSE-*` files in the repository for more information.
|
||||
|
||||
+18
-2
@@ -73,7 +73,8 @@ impl SaturatingTime for SystemTime {
|
||||
|
||||
impl SaturatingTime for Instant {
|
||||
fn anchor() -> Self {
|
||||
Self::now()
|
||||
use web_time_compat::InstantExt;
|
||||
Self::get()
|
||||
}
|
||||
|
||||
fn max_value() -> Self {
|
||||
@@ -143,7 +144,7 @@ where
|
||||
match next {
|
||||
Some(st) if st != res => {
|
||||
// (2.1) If Some, set res to the returned value and continue.
|
||||
res = st
|
||||
res = st;
|
||||
}
|
||||
// Succeeded without moving. Windows makes this reachable and it
|
||||
// is what hung Tor's certificate validation there: a SystemTime
|
||||
@@ -178,6 +179,21 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// @@ begin test lint list maintained by maint/add_warning @@
|
||||
#![allow(clippy::bool_assert_comparison)]
|
||||
#![allow(clippy::clone_on_copy)]
|
||||
#![allow(clippy::dbg_macro)]
|
||||
#![allow(clippy::mixed_attributes_style)]
|
||||
#![allow(clippy::print_stderr)]
|
||||
#![allow(clippy::print_stdout)]
|
||||
#![allow(clippy::single_char_pattern)]
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::unchecked_time_subtraction)]
|
||||
#![allow(clippy::useless_vec)]
|
||||
#![allow(clippy::needless_pass_by_value)]
|
||||
#![allow(clippy::string_slice)] // See arti#2571
|
||||
//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
|
||||
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
ops::{Add, Sub},
|
||||
|
||||
Vendored
+65
-3
@@ -1,7 +1,54 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
// @@ begin lint list maintained by maint/add_warning @@
|
||||
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
|
||||
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
|
||||
#![warn(missing_docs)]
|
||||
#![warn(noop_method_call)]
|
||||
#![warn(unreachable_pub)]
|
||||
#![warn(clippy::all)]
|
||||
#![deny(clippy::await_holding_lock)]
|
||||
#![deny(clippy::cargo_common_metadata)]
|
||||
#![deny(clippy::cast_lossless)]
|
||||
#![deny(clippy::checked_conversions)]
|
||||
#![allow(clippy::cognitive_complexity)] // See arti#2556
|
||||
#![deny(clippy::debug_assert_with_mut_call)]
|
||||
#![deny(clippy::exhaustive_enums)]
|
||||
#![deny(clippy::exhaustive_structs)]
|
||||
#![deny(clippy::expl_impl_clone_on_copy)]
|
||||
#![deny(clippy::fallible_impl_from)]
|
||||
#![deny(clippy::implicit_clone)]
|
||||
#![deny(clippy::large_stack_arrays)]
|
||||
#![warn(clippy::manual_ok_or)]
|
||||
#![deny(clippy::missing_docs_in_private_items)]
|
||||
#![warn(clippy::needless_borrow)]
|
||||
#![warn(clippy::needless_pass_by_value)]
|
||||
#![warn(clippy::option_option)]
|
||||
#![deny(clippy::print_stderr)]
|
||||
#![deny(clippy::print_stdout)]
|
||||
#![warn(clippy::rc_buffer)]
|
||||
#![deny(clippy::ref_option_ref)]
|
||||
#![warn(clippy::semicolon_if_nothing_returned)]
|
||||
#![warn(clippy::trait_duplication_in_bounds)]
|
||||
#![deny(clippy::unchecked_time_subtraction)]
|
||||
#![deny(clippy::unnecessary_wraps)]
|
||||
#![warn(clippy::unseparated_literal_suffix)]
|
||||
#![deny(clippy::unwrap_used)]
|
||||
#![deny(clippy::mod_module_files)]
|
||||
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
|
||||
#![allow(clippy::uninlined_format_args)]
|
||||
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
|
||||
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
|
||||
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
|
||||
#![allow(clippy::needless_lifetimes)] // See arti#1765
|
||||
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
|
||||
#![allow(clippy::collapsible_if)] // See arti#2342
|
||||
#![deny(clippy::unused_async)]
|
||||
#![deny(clippy::string_slice)] // See arti#2571
|
||||
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
|
||||
#![allow(unexpected_cfgs)]
|
||||
#![forbid(unsafe_code)]
|
||||
#![cfg_attr(
|
||||
feature = "nightly",
|
||||
saturating_time_nightly,
|
||||
feature(time_systemtime_limits, time_saturating_systemtime)
|
||||
)]
|
||||
|
||||
@@ -157,7 +204,7 @@ pub trait SaturatingTime: internal::SaturatingTime {
|
||||
}
|
||||
|
||||
// Use nightly implementation if compiled with the nightly feature.
|
||||
#[cfg(feature = "nightly")]
|
||||
#[cfg(saturating_time_nightly)]
|
||||
impl SaturatingTime for SystemTime {
|
||||
fn max_value() -> Self {
|
||||
Self::MAX
|
||||
@@ -181,7 +228,7 @@ impl SaturatingTime for SystemTime {
|
||||
}
|
||||
|
||||
// Otherwise, use the default one.
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
#[cfg(not(saturating_time_nightly))]
|
||||
impl SaturatingTime for SystemTime {}
|
||||
|
||||
impl SaturatingTime for Instant {
|
||||
@@ -193,6 +240,21 @@ impl SaturatingTime for Instant {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// @@ begin test lint list maintained by maint/add_warning @@
|
||||
#![allow(clippy::bool_assert_comparison)]
|
||||
#![allow(clippy::clone_on_copy)]
|
||||
#![allow(clippy::dbg_macro)]
|
||||
#![allow(clippy::mixed_attributes_style)]
|
||||
#![allow(clippy::print_stderr)]
|
||||
#![allow(clippy::print_stdout)]
|
||||
#![allow(clippy::single_char_pattern)]
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::unchecked_time_subtraction)]
|
||||
#![allow(clippy::useless_vec)]
|
||||
#![allow(clippy::needless_pass_by_value)]
|
||||
#![allow(clippy::string_slice)] // See arti#2571
|
||||
//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
|
||||
|
||||
use super::*;
|
||||
use crate::internal;
|
||||
use std::{
|
||||
|
||||
Reference in New Issue
Block a user