Skip to content

Commit 08d556c

Browse files
committed
fix/helper: Fix dealloc_c_utf8_alloced_from_rust
Previous implementation assumed `Vec::shrink_to_fit` would always make Vec's length equal to its capacity. However, there was no such guarantee and undefined behaviour could be triggered. Currently Rust implementation assume this as well. A proper fix would use boxed slices so we don't need to store capacity. We may want to revisit this as the following Rust issue gets more attention: rust-lang/rust#36284
1 parent bcf6936 commit 08d556c

File tree

3 files changed

+42
-14
lines changed

3 files changed

+42
-14
lines changed

src/ffi/directory_details.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,10 @@ impl Drop for DirectoryDetails {
9494
pub struct DirectoryMetadata {
9595
pub name: *mut u8,
9696
pub name_len: usize,
97+
pub name_cap: usize,
9798
pub user_metadata: *mut u8,
9899
pub user_metadata_len: usize,
100+
pub user_metadata_cap: usize,
99101
pub is_private: bool,
100102
pub is_versioned: bool,
101103
pub creation_time_sec: i64,
@@ -112,16 +114,19 @@ impl DirectoryMetadata {
112114
let created_time = dir_metadata.get_created_time().to_timespec();
113115
let modified_time = dir_metadata.get_modified_time().to_timespec();
114116

115-
let (name, name_len) = helper::string_to_c_utf8(dir_metadata.get_name()
117+
let (name, name_len, name_cap) = helper::string_to_c_utf8(dir_metadata.get_name()
116118
.to_string());
117119
let user_metadata = dir_metadata.get_user_metadata().to_base64(config::get_base64_config());
118-
let (user_metadata, user_metadata_len) = helper::string_to_c_utf8(user_metadata);
120+
let (user_metadata, user_metadata_len, user_metadata_cap) =
121+
helper::string_to_c_utf8(user_metadata);
119122

120123
Ok(DirectoryMetadata {
121124
name: name,
122125
name_len: name_len,
126+
name_cap: name_cap,
123127
user_metadata: user_metadata,
124128
user_metadata_len: user_metadata_len,
129+
user_metadata_cap: user_metadata_cap,
125130
is_private: *dir_key.get_access_level() == ::nfs::AccessLevel::Private,
126131
is_versioned: dir_key.is_versioned(),
127132
creation_time_sec: created_time.sec,
@@ -135,9 +140,11 @@ impl DirectoryMetadata {
135140
// a proper impl Drop.
136141
fn deallocate(&mut self) {
137142
unsafe {
138-
let _ = helper::dealloc_c_utf8_alloced_from_rust(self.name, self.name_len);
143+
let _ =
144+
helper::dealloc_c_utf8_alloced_from_rust(self.name, self.name_len, self.name_cap);
139145
let _ = helper::dealloc_c_utf8_alloced_from_rust(self.user_metadata,
140-
self.user_metadata_len);
146+
self.user_metadata_len,
147+
self.user_metadata_cap);
141148
}
142149
}
143150
}

src/ffi/file_details.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ pub struct FileDetails {
3737
pub content: *mut u8,
3838
/// Size of `content`
3939
pub content_len: usize,
40+
/// Capacity of `content`. Only used by the allocator's `dealloc` algorithm.
41+
pub content_cap: usize,
4042
/// Metadata of the file.
4143
pub metadata: *mut FileMetadata,
4244
}
@@ -59,7 +61,7 @@ impl FileDetails {
5961

6062
let content = try!(reader.read(start_position, size));
6163
let content = content.to_base64(config::get_base64_config());
62-
let (content, content_len) = helper::string_to_c_utf8(content);
64+
let (content, content_len, content_cap) = helper::string_to_c_utf8(content);
6365

6466
let file_metadata_ptr = if include_metadata {
6567
Box::into_raw(Box::new(try!(FileMetadata::new(file.get_metadata()))))
@@ -70,6 +72,7 @@ impl FileDetails {
7072
Ok(FileDetails {
7173
content: content,
7274
content_len: content_len,
75+
content_cap: content_cap,
7376
metadata: file_metadata_ptr,
7477
})
7578
}
@@ -78,7 +81,9 @@ impl FileDetails {
7881
// a proper impl Drop.
7982
fn deallocate(self) {
8083
unsafe {
81-
helper::dealloc_c_utf8_alloced_from_rust(self.content, self.content_len);
84+
helper::dealloc_c_utf8_alloced_from_rust(self.content,
85+
self.content_len,
86+
self.content_cap);
8287
}
8388

8489
if !self.metadata.is_null() {
@@ -94,8 +99,10 @@ impl FileDetails {
9499
pub struct FileMetadata {
95100
pub name: *mut u8,
96101
pub name_len: usize,
102+
pub name_cap: usize,
97103
pub user_metadata: *mut u8,
98104
pub user_metadata_len: usize,
105+
pub user_metadata_cap: usize,
99106
pub size: i64,
100107
pub creation_time_sec: i64,
101108
pub creation_time_nsec: i64,
@@ -111,18 +118,22 @@ impl FileMetadata {
111118
let created_time = file_metadata.get_created_time().to_timespec();
112119
let modified_time = file_metadata.get_modified_time().to_timespec();
113120

114-
let (name, name_len) = helper::string_to_c_utf8(file_metadata.get_name().to_string());
121+
let (name, name_len, name_cap) = helper::string_to_c_utf8(file_metadata.get_name()
122+
.to_string());
115123

116124
let user_metadata = file_metadata.get_user_metadata()
117125
.to_base64(config::get_base64_config());
118-
let (user_metadata, user_metadata_len) = helper::string_to_c_utf8(user_metadata);
126+
let (user_metadata, user_metadata_len, user_metadata_cap) =
127+
helper::string_to_c_utf8(user_metadata);
119128

120129
Ok(FileMetadata {
121130
name: name,
122131
name_len: name_len,
132+
name_cap: name_cap,
123133
size: file_metadata.get_size() as i64,
124134
user_metadata: user_metadata,
125135
user_metadata_len: user_metadata_len,
136+
user_metadata_cap: user_metadata_cap,
126137
creation_time_sec: created_time.sec,
127138
creation_time_nsec: created_time.nsec as i64,
128139
modification_time_sec: modified_time.sec,
@@ -135,8 +146,10 @@ impl FileMetadata {
135146
// a proper impl Drop.
136147
pub fn deallocate(&mut self) {
137148
unsafe {
138-
helper::dealloc_c_utf8_alloced_from_rust(self.name, self.name_len);
139-
helper::dealloc_c_utf8_alloced_from_rust(self.user_metadata, self.user_metadata_len);
149+
helper::dealloc_c_utf8_alloced_from_rust(self.name, self.name_len, self.name_cap);
150+
helper::dealloc_c_utf8_alloced_from_rust(self.user_metadata,
151+
self.user_metadata_len,
152+
self.user_metadata_cap);
140153
}
141154
}
142155
}

src/ffi/helper.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,25 @@ pub unsafe fn c_utf8_to_opt_string(ptr: *const u8, len: usize) -> Result<Option<
5353
}
5454
}
5555

56-
pub fn string_to_c_utf8(s: String) -> (*mut u8, usize) {
56+
/// Returns a heap-allocated raw string, usable by C/FFI-boundary.
57+
/// The tuple means (pointer, length_in_bytes, capacity).
58+
/// Use `dealloc_c_utf8_alloced_from_rust` to free the memory.
59+
pub fn string_to_c_utf8(s: String) -> (*mut u8, usize, usize) {
5760
let mut v = s.into_bytes();
5861
v.shrink_to_fit();
5962
let p = v.as_mut_ptr();
6063
let len = v.len();
64+
let cap = v.capacity();
6165
mem::forget(v);
62-
(p, len)
66+
(p, len, cap)
6367
}
6468

65-
pub unsafe fn dealloc_c_utf8_alloced_from_rust(p: *mut u8, len: usize) {
66-
let _ = Vec::from_raw_parts(p, len, len);
69+
/// Dealloc a string allocated with `string_to_c_utf8`.
70+
pub unsafe fn dealloc_c_utf8_alloced_from_rust(p: *mut u8, len: usize,
71+
cap: usize) {
72+
// TODO: refactor implementation to remove the need for `cap`. Related issue:
73+
// <https://github.com/rust-lang/rust/issues/36284>.
74+
let _ = Vec::from_raw_parts(p, len, cap);
6775
}
6876

6977
// TODO: add c_char_ptr_to_str and c_char_ptr_to_opt_str (return &str instead of String)

0 commit comments

Comments
 (0)