Skip to content

Commit 40e1b17

Browse files
authored
Merge pull request #229 from muzarski/pointer-fixes
safety: pointer related bugfixes
2 parents 33ed7f1 + c1e40d7 commit 40e1b17

File tree

4 files changed

+98
-33
lines changed

4 files changed

+98
-33
lines changed

scylla-rust-wrapper/src/collection.rs

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,26 @@ use crate::value::CassCqlValue;
66
use crate::{argconv::*, value};
77
use std::convert::TryFrom;
88
use std::sync::Arc;
9+
use std::sync::LazyLock;
910

1011
// These constants help us to save an allocation in case user calls `cass_collection_new` (untyped collection).
11-
static UNTYPED_LIST_TYPE: CassDataType = CassDataType::new(CassDataTypeInner::List {
12-
typ: None,
13-
frozen: false,
12+
static UNTYPED_LIST_TYPE: LazyLock<Arc<CassDataType>> = LazyLock::new(|| {
13+
CassDataType::new_arced(CassDataTypeInner::List {
14+
typ: None,
15+
frozen: false,
16+
})
1417
});
15-
static UNTYPED_SET_TYPE: CassDataType = CassDataType::new(CassDataTypeInner::Set {
16-
typ: None,
17-
frozen: false,
18+
static UNTYPED_SET_TYPE: LazyLock<Arc<CassDataType>> = LazyLock::new(|| {
19+
CassDataType::new_arced(CassDataTypeInner::Set {
20+
typ: None,
21+
frozen: false,
22+
})
1823
});
19-
static UNTYPED_MAP_TYPE: CassDataType = CassDataType::new(CassDataTypeInner::Map {
20-
typ: MapDataType::Untyped,
21-
frozen: false,
24+
static UNTYPED_MAP_TYPE: LazyLock<Arc<CassDataType>> = LazyLock::new(|| {
25+
CassDataType::new_arced(CassDataTypeInner::Map {
26+
typ: MapDataType::Untyped,
27+
frozen: false,
28+
})
2229
});
2330

2431
#[derive(Clone)]
@@ -183,9 +190,9 @@ unsafe extern "C" fn cass_collection_data_type(
183190
match &collection_ref.data_type {
184191
Some(dt) => ArcFFI::as_ptr(dt),
185192
None => match collection_ref.collection_type {
186-
CassCollectionType::CASS_COLLECTION_TYPE_LIST => &UNTYPED_LIST_TYPE,
187-
CassCollectionType::CASS_COLLECTION_TYPE_SET => &UNTYPED_SET_TYPE,
188-
CassCollectionType::CASS_COLLECTION_TYPE_MAP => &UNTYPED_MAP_TYPE,
193+
CassCollectionType::CASS_COLLECTION_TYPE_LIST => ArcFFI::as_ptr(&UNTYPED_LIST_TYPE),
194+
CassCollectionType::CASS_COLLECTION_TYPE_SET => ArcFFI::as_ptr(&UNTYPED_SET_TYPE),
195+
CassCollectionType::CASS_COLLECTION_TYPE_MAP => ArcFFI::as_ptr(&UNTYPED_MAP_TYPE),
189196
// CassCollectionType is a C enum. Panic, if it's out of range.
190197
_ => panic!(
191198
"CassCollectionType enum value out of range: {}",
@@ -225,7 +232,10 @@ mod tests {
225232
use crate::{
226233
argconv::ArcFFI,
227234
cass_error::CassError,
228-
cass_types::{CassDataType, CassDataTypeInner, CassValueType, MapDataType},
235+
cass_types::{
236+
cass_data_type_add_sub_type, cass_data_type_free, cass_data_type_new, CassDataType,
237+
CassDataTypeInner, CassValueType, MapDataType,
238+
},
229239
collection::{
230240
cass_collection_append_double, cass_collection_append_float, cass_collection_free,
231241
},
@@ -234,7 +244,8 @@ mod tests {
234244

235245
use super::{
236246
cass_bool_t, cass_collection_append_bool, cass_collection_append_int16,
237-
cass_collection_new, cass_collection_new_from_data_type, CassCollectionType,
247+
cass_collection_data_type, cass_collection_new, cass_collection_new_from_data_type,
248+
CassCollectionType,
238249
};
239250

240251
#[test]
@@ -498,4 +509,24 @@ mod tests {
498509
}
499510
}
500511
}
512+
513+
#[test]
514+
fn regression_empty_collection_data_type_test() {
515+
// This is a regression test that checks whether collections return
516+
// an Arc-based pointer for their type, even if they are empty.
517+
// Previously, they would return the pointer to static data, but not Arc allocated.
518+
unsafe {
519+
let empty_list = cass_collection_new(CassCollectionType::CASS_COLLECTION_TYPE_LIST, 2);
520+
521+
// This would previously return a non Arc-based pointer.
522+
let empty_list_dt = cass_collection_data_type(empty_list);
523+
524+
let empty_set_dt = cass_data_type_new(CassValueType::CASS_VALUE_TYPE_SET);
525+
// This will try to increment the reference count of `empty_list_dt`.
526+
// Previously, this would fail, because `empty_list_dt` did not originate from an Arc allocation.
527+
cass_data_type_add_sub_type(empty_set_dt, empty_list_dt);
528+
529+
cass_data_type_free(empty_set_dt)
530+
}
531+
}
501532
}

scylla-rust-wrapper/src/metadata.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl RefFFI for CassMaterializedViewMeta {}
5353

5454
pub struct CassColumnMeta {
5555
pub name: String,
56-
pub column_type: CassDataType,
56+
pub column_type: Arc<CassDataType>,
5757
pub column_kind: CassColumnType,
5858
}
5959

@@ -68,7 +68,7 @@ pub fn create_table_metadata(table_name: &str, table_metadata: &Table) -> CassTa
6868
.for_each(|(column_name, column_metadata)| {
6969
let cass_column_meta = CassColumnMeta {
7070
name: column_name.clone(),
71-
column_type: get_column_type(&column_metadata.typ),
71+
column_type: Arc::new(get_column_type(&column_metadata.typ)),
7272
column_kind: match column_metadata.kind {
7373
ColumnKind::Regular => CassColumnType::CASS_COLUMN_TYPE_REGULAR,
7474
ColumnKind::Static => CassColumnType::CASS_COLUMN_TYPE_STATIC,
@@ -368,7 +368,7 @@ pub unsafe extern "C" fn cass_column_meta_data_type(
368368
column_meta: *const CassColumnMeta,
369369
) -> *const CassDataType {
370370
let column_meta = RefFFI::as_ref(column_meta);
371-
&column_meta.column_type as *const CassDataType
371+
ArcFFI::as_ptr(&column_meta.column_type)
372372
}
373373

374374
#[no_mangle]
@@ -496,7 +496,14 @@ pub unsafe extern "C" fn cass_materialized_view_meta_base_table(
496496
view_meta: *const CassMaterializedViewMeta,
497497
) -> *const CassTableMeta {
498498
let view_meta = RefFFI::as_ref(view_meta);
499-
view_meta.base_table.as_ptr()
499+
500+
match view_meta.base_table.upgrade() {
501+
Some(arc) => RefFFI::as_ptr(&arc),
502+
None => {
503+
tracing::error!("Failed to upgrade a weak reference to table metadata from materialized view metadata! This is a driver bug!");
504+
std::ptr::null()
505+
}
506+
}
500507
}
501508

502509
#[no_mangle]

scylla-rust-wrapper/src/session.rs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,7 @@ impl CassSessionInner {
7070
}
7171

7272
fn connect(
73-
// This reference is 'static because this is the only was of assuring the borrow checker
74-
// that holding it in our returned future is sound. Ideally, we would prefer to have
75-
// the returned future's lifetime constrained by real lifetime of the session's RwLock,
76-
// but this is impossible to be guaranteed due to C/Rust cross-language barrier.
77-
session_opt: &'static RwLock<Option<CassSessionInner>>,
73+
session_opt: Arc<RwLock<Option<CassSessionInner>>>,
7874
cluster: &CassCluster,
7975
keyspace: Option<String>,
8076
) -> *mut CassFuture {
@@ -94,7 +90,7 @@ impl CassSessionInner {
9490
}
9591

9692
async fn connect_fut(
97-
session_opt: &RwLock<Option<CassSessionInner>>,
93+
session_opt: Arc<RwLock<Option<CassSessionInner>>>,
9894
session_builder_fut: impl Future<Output = SessionBuilder>,
9995
exec_profile_builder_map: HashMap<ExecProfileName, CassExecProfile>,
10096
client_id: uuid::Uuid,
@@ -154,7 +150,7 @@ pub unsafe extern "C" fn cass_session_connect(
154150
session_raw: *mut CassSession,
155151
cluster_raw: *const CassCluster,
156152
) -> *mut CassFuture {
157-
let session_opt = ArcFFI::as_ref(session_raw);
153+
let session_opt = ArcFFI::cloned_from_ptr(session_raw);
158154
let cluster: &CassCluster = BoxFFI::as_ref(cluster_raw);
159155

160156
CassSessionInner::connect(session_opt, cluster, None)
@@ -176,7 +172,7 @@ pub unsafe extern "C" fn cass_session_connect_keyspace_n(
176172
keyspace: *const c_char,
177173
keyspace_length: size_t,
178174
) -> *mut CassFuture {
179-
let session_opt = ArcFFI::as_ref(session_raw);
175+
let session_opt = ArcFFI::cloned_from_ptr(session_raw);
180176
let cluster: &CassCluster = BoxFFI::as_ref(cluster_raw);
181177
let keyspace = ptr_to_cstr_n(keyspace, keyspace_length).map(ToOwned::to_owned);
182178

@@ -188,7 +184,7 @@ pub unsafe extern "C" fn cass_session_execute_batch(
188184
session_raw: *mut CassSession,
189185
batch_raw: *const CassBatch,
190186
) -> *mut CassFuture {
191-
let session_opt = ArcFFI::as_ref(session_raw);
187+
let session_opt = ArcFFI::cloned_from_ptr(session_raw);
192188
let batch_from_raw = BoxFFI::as_ref(batch_raw);
193189
let mut state = batch_from_raw.state.clone();
194190
let request_timeout_ms = batch_from_raw.batch_request_timeout_ms;
@@ -254,7 +250,7 @@ pub unsafe extern "C" fn cass_session_execute(
254250
session_raw: *mut CassSession,
255251
statement_raw: *const CassStatement,
256252
) -> *mut CassFuture {
257-
let session_opt = ArcFFI::as_ref(session_raw);
253+
let session_opt = ArcFFI::cloned_from_ptr(session_raw);
258254

259255
// DO NOT refer to `statement_opt` inside the async block, as I've done just to face a segfault.
260256
let statement_opt = BoxFFI::as_ref(statement_raw);
@@ -389,7 +385,7 @@ pub unsafe extern "C" fn cass_session_prepare_from_existing(
389385
cass_session: *mut CassSession,
390386
statement: *const CassStatement,
391387
) -> *mut CassFuture {
392-
let session = ArcFFI::as_ref(cass_session);
388+
let session = ArcFFI::cloned_from_ptr(cass_session);
393389
let cass_statement = BoxFFI::as_ref(statement);
394390
let statement = cass_statement.statement.clone();
395391

@@ -441,7 +437,7 @@ pub unsafe extern "C" fn cass_session_prepare_n(
441437
// There is a test for this: `NullStringApiArgsTest.Integration_Cassandra_PrepareNullQuery`.
442438
.unwrap_or_default();
443439
let query = Statement::new(query_str.to_string());
444-
let cass_session = ArcFFI::as_ref(cass_session_raw);
440+
let cass_session = ArcFFI::cloned_from_ptr(cass_session_raw);
445441

446442
CassFuture::make_raw(async move {
447443
let session_guard = cass_session.read().await;
@@ -474,7 +470,7 @@ pub unsafe extern "C" fn cass_session_free(session_raw: *mut CassSession) {
474470

475471
#[no_mangle]
476472
pub unsafe extern "C" fn cass_session_close(session: *mut CassSession) -> *mut CassFuture {
477-
let session_opt = ArcFFI::as_ref(session);
473+
let session_opt = ArcFFI::cloned_from_ptr(session);
478474

479475
CassFuture::make_raw(async move {
480476
let mut session_guard = session_opt.write().await;

scylla-rust-wrapper/src/tuple.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ use crate::types::*;
66
use crate::value;
77
use crate::value::CassCqlValue;
88
use std::sync::Arc;
9+
use std::sync::LazyLock;
910

10-
static UNTYPED_TUPLE_TYPE: CassDataType = CassDataType::new(CassDataTypeInner::Tuple(Vec::new()));
11+
static UNTYPED_TUPLE_TYPE: LazyLock<Arc<CassDataType>> =
12+
LazyLock::new(|| CassDataType::new_arced(CassDataTypeInner::Tuple(Vec::new())));
1113

1214
#[derive(Clone)]
1315
pub struct CassTuple {
@@ -92,7 +94,7 @@ unsafe extern "C" fn cass_tuple_free(tuple: *mut CassTuple) {
9294
unsafe extern "C" fn cass_tuple_data_type(tuple: *const CassTuple) -> *const CassDataType {
9395
match &BoxFFI::as_ref(tuple).data_type {
9496
Some(t) => ArcFFI::as_ptr(t),
95-
None => &UNTYPED_TUPLE_TYPE,
97+
None => ArcFFI::as_ptr(&UNTYPED_TUPLE_TYPE),
9698
}
9799
}
98100

@@ -116,3 +118,32 @@ make_binders!(decimal, cass_tuple_set_decimal);
116118
make_binders!(collection, cass_tuple_set_collection);
117119
make_binders!(tuple, cass_tuple_set_tuple);
118120
make_binders!(user_type, cass_tuple_set_user_type);
121+
122+
#[cfg(test)]
123+
mod tests {
124+
use crate::cass_types::{
125+
cass_data_type_add_sub_type, cass_data_type_free, cass_data_type_new, CassValueType,
126+
};
127+
128+
use super::{cass_tuple_data_type, cass_tuple_new};
129+
130+
#[test]
131+
fn regression_empty_tuple_data_type_test() {
132+
// This is a regression test that checks whether tuples return
133+
// an Arc-based pointer for their type, even if they are empty.
134+
// Previously, they would return the pointer to static data, but not Arc allocated.
135+
unsafe {
136+
let empty_tuple = cass_tuple_new(2);
137+
138+
// This would previously return a non Arc-based pointer.
139+
let empty_tuple_dt = cass_tuple_data_type(empty_tuple);
140+
141+
let empty_set_dt = cass_data_type_new(CassValueType::CASS_VALUE_TYPE_SET);
142+
// This will try to increment the reference count of `empty_tuple_dt`.
143+
// Previously, this would fail, because `empty_tuple_dt` did not originate from an Arc allocation.
144+
cass_data_type_add_sub_type(empty_set_dt, empty_tuple_dt);
145+
146+
cass_data_type_free(empty_set_dt)
147+
}
148+
}
149+
}

0 commit comments

Comments
 (0)