Skip to content

Commit 8b7ccac

Browse files
committed
Fix panic in GroupInfo::members
I recently observed undefined behavior in one of the projects I'm working on because [slice::from_raw_parts] is called without a null-check in `GroupInfo::members`. This undefined behavior was present when iterating over the resulting slice and it would just terminate prematurely when trying to chain multiple iterators. The function is pretty strict about what kind of pointers it accepts: > data must be non-null and aligned even for zero-length slices. This undefined behavior has become a panic in debug builds in [Rust 1.78.0]: > For example, slice::from_raw_parts requires an aligned non-null pointer. > The following use of a purposely-misaligned pointer has undefined behavior, > and while if you were unlucky it may have appeared to "work" in the past, > the debug assertion can now catch it: Cause is found in [rdkafka.c]. I see there are more uses of `slice::from_raw_parts` so I replaced all of them except a call to `Vec::from_raw_parts` which seems fine. I'd appreciate feedback! [slice::from_raw_parts]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html [Rust 1.78.0]: https://blog.rust-lang.org/2024/05/02/Rust-1.78.0.html#asserting-unsafe-preconditions [rdkafka.c]: https://github.com/confluentinc/librdkafka/blob/95a542c87c61d2c45b445f91c73dd5442eb04f3c/src/rdkafka.c#L4668-L4670
1 parent e69c2aa commit 8b7ccac

7 files changed

+46
-56
lines changed

src/config.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
//! [librdkafka-config]: https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md
2424
2525
use std::collections::HashMap;
26-
use std::ffi::{CStr, CString};
26+
use std::ffi::CString;
2727
use std::iter::FromIterator;
2828
use std::os::raw::c_char;
2929
use std::ptr;

src/groups.rs

+11-22
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22
33
use std::ffi::CStr;
44
use std::fmt;
5-
use std::slice;
65

76
use rdkafka_sys as rdsys;
87
use rdkafka_sys::types::*;
98

10-
use crate::util::{KafkaDrop, NativePtr};
9+
use crate::util::{self, KafkaDrop, NativePtr};
1110

1211
/// Group member information container.
1312
pub struct GroupMemberInfo(RDKafkaGroupMemberInfo);
@@ -43,28 +42,20 @@ impl GroupMemberInfo {
4342
/// Return the metadata of the member.
4443
pub fn metadata(&self) -> Option<&[u8]> {
4544
unsafe {
46-
if self.0.member_metadata.is_null() {
47-
None
48-
} else {
49-
Some(slice::from_raw_parts::<u8>(
50-
self.0.member_metadata as *const u8,
51-
self.0.member_metadata_size as usize,
52-
))
53-
}
45+
util::ptr_to_opt_slice(
46+
self.0.member_metadata as *const u8,
47+
self.0.member_metadata_size as usize,
48+
)
5449
}
5550
}
5651

5752
/// Return the partition assignment of the member.
5853
pub fn assignment(&self) -> Option<&[u8]> {
5954
unsafe {
60-
if self.0.member_assignment.is_null() {
61-
None
62-
} else {
63-
Some(slice::from_raw_parts::<u8>(
64-
self.0.member_assignment as *const u8,
65-
self.0.member_assignment_size as usize,
66-
))
67-
}
55+
util::ptr_to_opt_slice(
56+
self.0.member_assignment as *const u8,
57+
self.0.member_assignment_size as usize,
58+
)
6859
}
6960
}
7061
}
@@ -85,7 +76,7 @@ impl GroupInfo {
8576
/// Returns the members of the group.
8677
pub fn members(&self) -> &[GroupMemberInfo] {
8778
unsafe {
88-
slice::from_raw_parts(
79+
util::ptr_to_slice(
8980
self.0.members as *const GroupMemberInfo,
9081
self.0.member_cnt as usize,
9182
)
@@ -149,8 +140,6 @@ impl GroupList {
149140

150141
/// Returns all the groups in the list.
151142
pub fn groups(&self) -> &[GroupInfo] {
152-
unsafe {
153-
slice::from_raw_parts(self.0.groups as *const GroupInfo, self.0.group_cnt as usize)
154-
}
143+
unsafe { util::ptr_to_slice(self.0.groups as *const GroupInfo, self.0.group_cnt as usize) }
155144
}
156145
}

src/message.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ impl Headers for BorrowedHeaders {
283283
Some(Header {
284284
key: CStr::from_ptr(name_ptr).to_str().unwrap(),
285285
value: (!value_ptr.is_null())
286-
.then(|| util::ptr_to_slice(value_ptr, value_size)),
286+
.then(|| util::ptr_to_slice(value_ptr as *const u8, value_size)),
287287
})
288288
}
289289
}
@@ -425,20 +425,20 @@ impl<'a> Message for BorrowedMessage<'a> {
425425
type Headers = BorrowedHeaders;
426426

427427
fn key(&self) -> Option<&[u8]> {
428-
unsafe { util::ptr_to_opt_slice((*self.ptr).key, (*self.ptr).key_len) }
428+
unsafe { util::ptr_to_opt_slice(self.ptr.key as *const u8, self.ptr.key_len) }
429429
}
430430

431431
fn payload(&self) -> Option<&[u8]> {
432-
unsafe { util::ptr_to_opt_slice((*self.ptr).payload, (*self.ptr).len) }
432+
unsafe { util::ptr_to_opt_slice(self.ptr.payload as *const u8, self.ptr.len) }
433433
}
434434

435435
unsafe fn payload_mut(&mut self) -> Option<&mut [u8]> {
436-
util::ptr_to_opt_mut_slice((*self.ptr).payload, (*self.ptr).len)
436+
util::ptr_to_opt_mut_slice(self.ptr.payload as *mut u8, self.ptr.len)
437437
}
438438

439439
fn topic(&self) -> &str {
440440
unsafe {
441-
CStr::from_ptr(rdsys::rd_kafka_topic_name((*self.ptr).rkt))
441+
CStr::from_ptr(rdsys::rd_kafka_topic_name(self.ptr.rkt))
442442
.to_str()
443443
.expect("Topic name is not valid UTF-8")
444444
}

src/metadata.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
//! Cluster metadata.
22
33
use std::ffi::CStr;
4-
use std::slice;
54

65
use rdkafka_sys as rdsys;
76
use rdkafka_sys::types::*;
87

98
use crate::error::IsError;
10-
use crate::util::{KafkaDrop, NativePtr};
9+
use crate::util::{self, KafkaDrop, NativePtr};
1110

1211
/// Broker metadata information.
1312
pub struct MetadataBroker(RDKafkaMetadataBroker);
@@ -60,12 +59,12 @@ impl MetadataPartition {
6059

6160
/// Returns the broker IDs of the replicas.
6261
pub fn replicas(&self) -> &[i32] {
63-
unsafe { slice::from_raw_parts(self.0.replicas, self.0.replica_cnt as usize) }
62+
unsafe { util::ptr_to_slice(self.0.replicas, self.0.replica_cnt as usize) }
6463
}
6564

6665
/// Returns the broker IDs of the in-sync replicas.
6766
pub fn isr(&self) -> &[i32] {
68-
unsafe { slice::from_raw_parts(self.0.isrs, self.0.isr_cnt as usize) }
67+
unsafe { util::ptr_to_slice(self.0.isrs, self.0.isr_cnt as usize) }
6968
}
7069
}
7170

@@ -85,7 +84,7 @@ impl MetadataTopic {
8584
/// Returns the partition metadata information for all the partitions.
8685
pub fn partitions(&self) -> &[MetadataPartition] {
8786
unsafe {
88-
slice::from_raw_parts(
87+
ptr_to_slice(
8988
self.0.partitions as *const MetadataPartition,
9089
self.0.partition_cnt as usize,
9190
)
@@ -141,7 +140,7 @@ impl Metadata {
141140
/// Returns the metadata information for all the brokers in the cluster.
142141
pub fn brokers(&self) -> &[MetadataBroker] {
143142
unsafe {
144-
slice::from_raw_parts(
143+
util::ptr_to_slice(
145144
self.0.brokers as *const MetadataBroker,
146145
self.0.broker_cnt as usize,
147146
)
@@ -151,7 +150,7 @@ impl Metadata {
151150
/// Returns the metadata information for all the topics in the cluster.
152151
pub fn topics(&self) -> &[MetadataTopic] {
153152
unsafe {
154-
slice::from_raw_parts(
153+
util::ptr_to_slice(
155154
self.0.topics as *const MetadataTopic,
156155
self.0.topic_cnt as usize,
157156
)

src/producer/base_producer.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ use std::marker::PhantomData;
4646
use std::mem;
4747
use std::os::raw::c_void;
4848
use std::ptr;
49-
use std::slice;
5049
use std::str;
5150
use std::sync::atomic::{AtomicBool, Ordering};
5251
use std::sync::Arc;
@@ -67,7 +66,7 @@ use crate::producer::{
6766
DefaultProducerContext, Partitioner, Producer, ProducerContext, PurgeConfig,
6867
};
6968
use crate::topic_partition_list::TopicPartitionList;
70-
use crate::util::{IntoOpaque, NativePtr, Timeout};
69+
use crate::util::{self, IntoOpaque, NativePtr, Timeout};
7170

7271
pub use crate::message::DeliveryResult;
7372

@@ -217,11 +216,7 @@ unsafe extern "C" fn partitioner_cb<Part: Partitioner, C: ProducerContext<Part>>
217216

218217
let is_partition_available = |p: i32| rdsys::rd_kafka_topic_partition_available(topic, p) == 1;
219218

220-
let key = if keydata.is_null() {
221-
None
222-
} else {
223-
Some(slice::from_raw_parts(keydata as *const u8, keylen))
224-
};
219+
let key = util::ptr_to_opt_slice(keydata, keylen);
225220

226221
let producer_context = &mut *(rkt_opaque as *mut C);
227222

src/topic_partition_list.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use std::collections::HashMap;
66
use std::ffi::{CStr, CString};
77
use std::fmt;
8-
use std::slice;
98
use std::str;
109

1110
use libc::c_void;
@@ -139,7 +138,8 @@ impl<'a> TopicPartitionListElem<'a> {
139138

140139
/// Returns the optional metadata associated with the entry.
141140
pub fn metadata(&self) -> &str {
142-
let bytes = unsafe { util::ptr_to_slice(self.ptr.metadata, self.ptr.metadata_size) };
141+
let bytes =
142+
unsafe { util::ptr_to_slice(self.ptr.metadata as *const u8, self.ptr.metadata_size) };
143143
str::from_utf8(bytes).expect("Metadata is not UTF-8")
144144
}
145145

@@ -317,7 +317,7 @@ impl TopicPartitionList {
317317

318318
/// Sets all partitions in the list to the specified offset.
319319
pub fn set_all_offsets(&mut self, offset: Offset) -> Result<(), KafkaError> {
320-
let slice = unsafe { slice::from_raw_parts_mut((*self.ptr).elems, self.count()) };
320+
let slice = unsafe { util::ptr_to_mut_slice(self.ptr.elems, self.count()) };
321321
for elem_ptr in slice {
322322
let mut elem = TopicPartitionListElem::from_ptr(self, &mut *elem_ptr);
323323
elem.set_offset(offset)?;
@@ -327,7 +327,7 @@ impl TopicPartitionList {
327327

328328
/// Returns all the elements of the list.
329329
pub fn elements(&self) -> Vec<TopicPartitionListElem<'_>> {
330-
let slice = unsafe { slice::from_raw_parts_mut((*self.ptr).elems, self.count()) };
330+
let slice = unsafe { util::ptr_to_mut_slice(self.ptr.elems, self.count()) };
331331
let mut vec = Vec::with_capacity(slice.len());
332332
for elem_ptr in slice {
333333
vec.push(TopicPartitionListElem::from_ptr(self, &mut *elem_ptr));
@@ -337,7 +337,7 @@ impl TopicPartitionList {
337337

338338
/// Returns all the elements of the list that belong to the specified topic.
339339
pub fn elements_for_topic<'a>(&'a self, topic: &str) -> Vec<TopicPartitionListElem<'a>> {
340-
let slice = unsafe { slice::from_raw_parts_mut((*self.ptr).elems, self.count()) };
340+
let slice = unsafe { util::ptr_to_mut_slice(self.ptr.elems, self.count()) };
341341
let mut vec = Vec::with_capacity(slice.len());
342342
for elem_ptr in slice {
343343
let tp = TopicPartitionListElem::from_ptr(self, &mut *elem_ptr);

src/util.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -105,32 +105,39 @@ pub fn current_time_millis() -> i64 {
105105

106106
/// Converts a pointer to an array to an optional slice. If the pointer is null,
107107
/// returns `None`.
108-
pub(crate) unsafe fn ptr_to_opt_slice<'a, T>(ptr: *const c_void, size: usize) -> Option<&'a [T]> {
108+
pub(crate) unsafe fn ptr_to_opt_slice<'a, T>(ptr: *const T, size: usize) -> Option<&'a [T]> {
109109
if ptr.is_null() {
110110
None
111111
} else {
112-
Some(slice::from_raw_parts::<T>(ptr as *const T, size))
112+
Some(slice::from_raw_parts(ptr, size))
113113
}
114114
}
115115

116-
pub(crate) unsafe fn ptr_to_opt_mut_slice<'a, T>(
117-
ptr: *const c_void,
118-
size: usize,
119-
) -> Option<&'a mut [T]> {
116+
pub(crate) unsafe fn ptr_to_opt_mut_slice<'a, T>(ptr: *mut T, size: usize) -> Option<&'a mut [T]> {
120117
if ptr.is_null() {
121118
None
122119
} else {
123-
Some(slice::from_raw_parts_mut::<T>(ptr as *mut T, size))
120+
Some(slice::from_raw_parts_mut(ptr, size))
124121
}
125122
}
126123

127124
/// Converts a pointer to an array to a slice. If the pointer is null or the
128125
/// size is zero, returns a zero-length slice..
129-
pub(crate) unsafe fn ptr_to_slice<'a, T>(ptr: *const c_void, size: usize) -> &'a [T] {
126+
pub(crate) unsafe fn ptr_to_slice<'a, T>(ptr: *const T, size: usize) -> &'a [T] {
130127
if ptr.is_null() || size == 0 {
131128
&[][..]
132129
} else {
133-
slice::from_raw_parts::<T>(ptr as *const T, size)
130+
slice::from_raw_parts(ptr, size)
131+
}
132+
}
133+
134+
/// Converts a pointer to an array to a mutable slice. If the pointer is null
135+
/// or the size is zero, returns a zero-length slice.
136+
pub(crate) unsafe fn ptr_to_mut_slice<'a, T>(ptr: *mut T, size: usize) -> &'a mut [T] {
137+
if ptr.is_null() || size == 0 {
138+
&mut [][..]
139+
} else {
140+
slice::from_raw_parts_mut(ptr, size)
134141
}
135142
}
136143

0 commit comments

Comments
 (0)