Skip to content

Commit a4889f5

Browse files
fix: replace String::from_utf16_lossy with String::from_utf16 (#568)
1 parent 2b1cf81 commit a4889f5

File tree

16 files changed

+200
-131
lines changed

16 files changed

+200
-131
lines changed

crates/winscard/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,13 @@ impl From<core::str::Utf8Error> for Error {
184184
}
185185
}
186186

187+
#[cfg(feature = "std")]
188+
impl From<std::string::FromUtf16Error> for Error {
189+
fn from(value: std::string::FromUtf16Error) -> Self {
190+
Error::new(ErrorKind::InvalidParameter, value.to_string())
191+
}
192+
}
193+
187194
#[cfg(feature = "std")]
188195
impl From<std::ffi::NulError> for Error {
189196
fn from(value: std::ffi::NulError) -> Self {

ffi/src/sspi/credentials_attributes.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,10 @@ pub unsafe fn extract_kdc_proxy_settings(p_buffer: NonNull<c_void>) -> Result<Kd
108108
// SAFETY:
109109
// - `proxy_server_ptr` is guaranteed to be non-null due to the prior check.
110110
// - `proxy_server_length` is valid.
111-
let proxy_server = String::from_utf16_lossy(unsafe {
111+
let proxy_server = String::from_utf16(unsafe {
112112
from_raw_parts(proxy_server_ptr, *proxy_server_length as usize / size_of::<SecWChar>())
113-
});
113+
})
114+
.map_err(Error::from)?;
114115

115116
let client_tls_cred = if *client_tls_cred_offset != 0 && *client_tls_cred_length != 0 {
116117
// SAFETY:
@@ -129,7 +130,10 @@ pub unsafe fn extract_kdc_proxy_settings(p_buffer: NonNull<c_void>) -> Result<Kd
129130
// - `client_tls_cred_ptr` is guaranteed to be non-null due to the prior check.
130131
// - `client_tls_cred_length` is valid.
131132
let client_tls_cred_data = unsafe { from_raw_parts(client_tls_cred_ptr, *client_tls_cred_length as usize) };
132-
Some(String::from_utf16_lossy(client_tls_cred_data))
133+
134+
let client_tls_cred = String::from_utf16(client_tls_cred_data).map_err(Error::from)?;
135+
136+
Some(client_tls_cred)
133137
} else {
134138
None
135139
};

ffi/src/sspi/scard_cert.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub fn smart_card_info(username: &[u8], pkcs11_module: &Path) -> Result<SystemSm
5353
let pkcs11 = Pkcs11::new(pkcs11_module)?;
5454
pkcs11.initialize(CInitializeArgs::OsThreads)?;
5555

56-
let username = utf16_bytes_to_utf8_string(username);
56+
let username = utf16_bytes_to_utf8_string(username)?;
5757

5858
for slot in pkcs11.get_slots_with_token()? {
5959
let session = pkcs11.open_ro_session(slot)?;

ffi/src/sspi/sec_handle.rs

Lines changed: 63 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -463,11 +463,13 @@ pub unsafe extern "system" fn AcquireCredentialsHandleW(
463463
check_null!(p_auth_data);
464464
check_null!(ph_credential);
465465

466-
// SAFETY:
467-
// - `psz_package` is guaranteed to be non-null due to the prior check.
468-
// - The memory region `psz_package` contains a valid null-terminator at the end of string.
469-
// - The memory region `psz_package` points to is valid for reads of bytes up to and including null-terminator.
470-
let security_package_name = unsafe { c_w_str_to_string(psz_package) };
466+
let security_package_name = try_execute!(
467+
// SAFETY:
468+
// - `psz_package` is guaranteed to be non-null due to the prior check.
469+
// - The memory region `psz_package` contains a valid null-terminator at the end of string.
470+
// - The memory region `psz_package` points to is valid for reads of bytes up to and including null-terminator.
471+
unsafe { c_w_str_to_string(psz_package) }.map_err(Error::from)
472+
);
471473
try_execute!(verify_security_package(&security_package_name));
472474

473475
debug!(?security_package_name);
@@ -737,11 +739,13 @@ pub unsafe extern "system" fn InitializeSecurityContextW(
737739
let service_principal = if p_target_name.is_null() {
738740
String::new()
739741
} else {
740-
// SAFETY:
741-
// - `p_target_name` is guaranteed to be non-null due to the prior check.
742-
// - The memory region `p_target_name` contains a valid null-terminator at the end of string.
743-
// - The memory region `p_target_name` points to is valid for reads of bytes up to and including null-terminator.
744-
unsafe { c_w_str_to_string(p_target_name) }
742+
try_execute!(
743+
// SAFETY:
744+
// - `p_target_name` is guaranteed to be non-null due to the prior check.
745+
// - The memory region `p_target_name` contains a valid null-terminator at the end of string.
746+
// - The memory region `p_target_name` points to is valid for reads of bytes up to and including null-terminator.
747+
unsafe { c_w_str_to_string(p_target_name) }.map_err(Error::from)
748+
)
745749
};
746750
debug!(?service_principal, "Target name (SPN)");
747751

@@ -1334,11 +1338,13 @@ pub unsafe extern "system" fn SetCredentialsAttributesW(
13341338
};
13351339

13361340
if ul_attribute == SECPKG_CRED_ATTR_NAMES {
1337-
// SAFETY:
1338-
// - `p_buffer` is guaranteed to be non-null due to the prior check.
1339-
// - The memory region `p_buffer` contains a valid null-terminator at the end of string.
1340-
// - The memory region `p_buffer` points to is valid for reads of bytes up to and including null-terminator.
1341-
let workstation = unsafe { c_w_str_to_string(p_buffer as *const _) };
1341+
let workstation = try_execute!(
1342+
// SAFETY:
1343+
// - `p_buffer` is guaranteed to be non-null due to the prior check.
1344+
// - The memory region `p_buffer` contains a valid null-terminator at the end of string.
1345+
// - The memory region `p_buffer` points to is valid for reads of bytes up to and including null-terminator.
1346+
unsafe { c_w_str_to_string(p_buffer as *const _) }.map_err(Error::from)
1347+
);
13421348

13431349
credentials_handle.attributes.workstation = Some(workstation);
13441350

@@ -1355,11 +1361,13 @@ pub unsafe extern "system" fn SetCredentialsAttributesW(
13551361
0
13561362
} else if ul_attribute == SECPKG_CRED_ATTR_KDC_URL {
13571363
let cred_attr = p_buffer.cast::<SecPkgCredentialsKdcUrlW>();
1358-
// SAFETY:
1359-
// - `p_buffer` is guaranteed to be non-null due to the prior check.
1360-
// - The memory region `p_buffer` contains a valid null-terminator at the end of string.
1361-
// - The memory region `p_buffer` points to is valid for reads of bytes up to and including null-terminator.
1362-
let kdc_url = unsafe { c_w_str_to_string((*cred_attr).kdc_url.cast_const()) };
1364+
let kdc_url = try_execute!(
1365+
// SAFETY:
1366+
// - `p_buffer` is guaranteed to be non-null due to the prior check.
1367+
// - The memory region `p_buffer` contains a valid null-terminator at the end of string.
1368+
// - The memory region `p_buffer` points to is valid for reads of bytes up to and including null-terminator.
1369+
unsafe { c_w_str_to_string((*cred_attr).kdc_url.cast_const()) }.map_err(Error::from)
1370+
);
13631371
credentials_handle.attributes.kdc_url = Some(kdc_url);
13641372

13651373
0
@@ -1545,32 +1553,42 @@ pub unsafe extern "system" fn ChangeAccountPasswordW(
15451553
check_null!(psz_new_password);
15461554
check_null!(p_output);
15471555

1548-
// SAFETY:
1549-
// - `psz_package_name` is guaranteed to be non-null due to the prior check.
1550-
// - The memory region `psz_package_name` contains a valid null-terminator at the end of string.
1551-
// - The memory region `psz_package_name` points to is valid for reads of bytes up to and including null-terminator.
1552-
let mut security_package_name = unsafe { c_w_str_to_string(psz_package_name) };
1556+
let mut security_package_name = try_execute!(
1557+
// SAFETY:
1558+
// - `psz_package_name` is guaranteed to be non-null due to the prior check.
1559+
// - The memory region `psz_package_name` contains a valid null-terminator at the end of string.
1560+
// - The memory region `psz_package_name` points to is valid for reads of bytes up to and including null-terminator.
1561+
unsafe { c_w_str_to_string(psz_package_name) }.map_err(Error::from)
1562+
);
15531563

1554-
// SAFETY:
1555-
// - `psz_domain_name` is guaranteed to be non-null due to the prior check.
1556-
// - The memory region `psz_domain_name` contains a valid null-terminator at the end of string.
1557-
// - The memory region `psz_domain_name` points to is valid for reads of bytes up to and including null-terminator.
1558-
let mut domain = unsafe { c_w_str_to_string(psz_domain_name) };
1559-
// SAFETY:
1560-
// - `psz_account_name` is guaranteed to be non-null due to the prior check.
1561-
// - The memory region `psz_account_name` contains a valid null-terminator at the end of string.
1562-
// - The memory region `psz_account_name` points to is valid for reads of bytes up to and including null-terminator.
1563-
let mut username = unsafe { c_w_str_to_string(psz_account_name) };
1564-
// SAFETY:
1565-
// - `psz_old_password` is guaranteed to be non-null due to the prior check.
1566-
// - The memory region `psz_old_password` contains a valid null-terminator at the end of string.
1567-
// - The memory region `psz_old_password` points to is valid for reads of bytes up to and including null-terminator.
1568-
let mut password = Secret::new(unsafe { c_w_str_to_string(psz_old_password) });
1569-
// SAFETY:
1570-
// - `psz_new_password` is guaranteed to be non-null due to the prior check.
1571-
// - The memory region `psz_new_password` contains a valid null-terminator at the end of string.
1572-
// - The memory region `psz_new_password` points to is valid for reads of bytes up to and including null-terminator.
1573-
let mut new_password = Secret::new(unsafe { c_w_str_to_string(psz_new_password) });
1564+
let mut domain = try_execute!(
1565+
// SAFETY:
1566+
// - `psz_domain_name` is guaranteed to be non-null due to the prior check.
1567+
// - The memory region `psz_domain_name` contains a valid null-terminator at the end of string.
1568+
// - The memory region `psz_domain_name` points to is valid for reads of bytes up to and including null-terminator.
1569+
unsafe { c_w_str_to_string(psz_domain_name) }.map_err(Error::from)
1570+
);
1571+
let mut username = try_execute!(
1572+
// SAFETY:
1573+
// - `psz_account_name` is guaranteed to be non-null due to the prior check.
1574+
// - The memory region `psz_account_name` contains a valid null-terminator at the end of string.
1575+
// - The memory region `psz_account_name` points to is valid for reads of bytes up to and including null-terminator.
1576+
unsafe { c_w_str_to_string(psz_account_name) }.map_err(Error::from)
1577+
);
1578+
let mut password = Secret::new(try_execute!(
1579+
// SAFETY:
1580+
// - `psz_old_password` is guaranteed to be non-null due to the prior check.
1581+
// - The memory region `psz_old_password` contains a valid null-terminator at the end of string.
1582+
// - The memory region `psz_old_password` points to is valid for reads of bytes up to and including null-terminator.
1583+
unsafe { c_w_str_to_string(psz_old_password) }.map_err(Error::from)
1584+
));
1585+
let mut new_password = Secret::new(try_execute!(
1586+
// SAFETY:
1587+
// - `psz_new_password` is guaranteed to be non-null due to the prior check.
1588+
// - The memory region `psz_new_password` contains a valid null-terminator at the end of string.
1589+
// - The memory region `psz_new_password` points to is valid for reads of bytes up to and including null-terminator.
1590+
unsafe { c_w_str_to_string(psz_new_password) }.map_err(Error::from)
1591+
));
15741592

15751593
// SAFETY:
15761594
// * `security_package_name' is a `String`.

ffi/src/sspi/sec_pkg_info.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::ffi::CStr;
22
use std::mem::size_of;
33
use std::ptr::copy_nonoverlapping;
44

5-
use sspi::{enumerate_security_packages, str_to_w_buff, PackageInfo, KERBEROS_VERSION};
5+
use sspi::{enumerate_security_packages, str_to_w_buff, Error, PackageInfo, KERBEROS_VERSION};
66
#[cfg(windows)]
77
use symbol_rename_macro::rename_symbol;
88

@@ -448,11 +448,13 @@ pub unsafe extern "system" fn QuerySecurityPackageInfoW(
448448
check_null!(p_package_name);
449449
check_null!(pp_package_info);
450450

451-
// SAFETY:
452-
// - `p_package_name` is guaranteed to be non-null due to the prior check.
453-
// - The memory region `p_package_name` contains a valid null-terminator at the end of string.
454-
// - The memory region `p_package_name` points to is valid for reads of bytes up to and including null-terminator.
455-
let pkg_name = unsafe { c_w_str_to_string(p_package_name) };
451+
let pkg_name = try_execute!(
452+
// SAFETY:
453+
// - `p_package_name` is guaranteed to be non-null due to the prior check.
454+
// - The memory region `p_package_name` contains a valid null-terminator at the end of string.
455+
// - The memory region `p_package_name` points to is valid for reads of bytes up to and including null-terminator.
456+
unsafe { c_w_str_to_string(p_package_name) }.map_err(Error::from)
457+
);
456458

457459
let pkg_info: RawSecPkgInfoW = try_execute!(enumerate_security_packages())
458460
.into_iter()

ffi/src/sspi/sec_winnt_auth_identity.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -478,13 +478,18 @@ pub unsafe fn auth_data_to_identity_buffers_w(
478478
let auth_data = unsafe { auth_data.as_ref() }.expect("auth_data pointer should not be null");
479479

480480
if !auth_data.package_list.is_null() && auth_data.package_list_length > 0 {
481-
// SAFETY: `package_list` is not null due to a prior check.
482-
*package_list = Some(String::from_utf16_lossy(unsafe {
483-
from_raw_parts(
484-
auth_data.package_list,
485-
usize::try_from(auth_data.package_list_length).unwrap(),
481+
*package_list = Some(
482+
String::from_utf16(
483+
// SAFETY: `package_list` is not null due to a prior check.
484+
unsafe {
485+
from_raw_parts(
486+
auth_data.package_list,
487+
usize::try_from(auth_data.package_list_length).unwrap(),
488+
)
489+
},
486490
)
487-
}));
491+
.map_err(Error::from)?,
492+
);
488493
}
489494

490495
(
@@ -1263,18 +1268,21 @@ mod tests {
12631268

12641269
assert_eq!(
12651270
"user",
1266-
String::from_utf16_lossy(from_raw_parts((*identity).user, (*identity).user_length as usize))
1271+
String::from_utf16(from_raw_parts((*identity).user, (*identity).user_length as usize))
1272+
.expect("user is a correct utf-16 string")
12671273
);
12681274
assert_eq!(
12691275
"pass",
1270-
String::from_utf16_lossy(from_raw_parts(
1276+
String::from_utf16(from_raw_parts(
12711277
(*identity).password,
12721278
(*identity).password_length as usize
12731279
))
1280+
.expect("password is a correct utf-16 string")
12741281
);
12751282
assert_eq!(
12761283
"domain",
1277-
String::from_utf16_lossy(from_raw_parts((*identity).domain, (*identity).domain_length as usize))
1284+
String::from_utf16(from_raw_parts((*identity).domain, (*identity).domain_length as usize))
1285+
.expect("domain is a correct utf-16 string")
12781286
);
12791287

12801288
let status = SspiFreeAuthIdentity(identity as *mut _);

ffi/src/utils.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::slice::from_raw_parts;
2+
use std::string::FromUtf16Error;
23

34
use libc::c_char;
45

@@ -13,7 +14,7 @@ pub(crate) fn into_raw_ptr<T>(value: T) -> *mut T {
1314
/// Behavior is undefined is any of the following conditions are violated:
1415
///
1516
/// * `s` must be a [valid], null-terminated C string.
16-
pub(crate) unsafe fn c_w_str_to_string(s: *const u16) -> String {
17+
pub(crate) unsafe fn c_w_str_to_string(s: *const u16) -> Result<String, FromUtf16Error> {
1718
let mut len = 0;
1819

1920
// SAFETY: `s` is a valid, null-terminated C string.
@@ -22,7 +23,7 @@ pub(crate) unsafe fn c_w_str_to_string(s: *const u16) -> String {
2223
}
2324

2425
// SAFETY: `s` is a valid, null-terminated C string.
25-
String::from_utf16_lossy(unsafe { from_raw_parts(s, len) })
26+
String::from_utf16(unsafe { from_raw_parts(s, len) })
2627
}
2728

2829
/// The returned length includes the null terminator char.

ffi/src/winscard/scard.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,13 @@ pub unsafe extern "system" fn SCardConnectW(
155155
check_null!(ph_card);
156156
check_null!(pdw_active_protocol);
157157

158-
// SAFETY:
159-
// - `sz_reader` is guaranteed to be non-null due to the prior check.
160-
// - The memory region `sz_reader` contains a valid null-terminator at the end of string.
161-
// - The memory region `sz_reader` points to is valid for reads of bytes up to and including null-terminator.
162-
let reader_name = unsafe { c_w_str_to_string(sz_reader) };
158+
let reader_name = try_execute!(
159+
// SAFETY:
160+
// - `sz_reader` is guaranteed to be non-null due to the prior check.
161+
// - The memory region `sz_reader` contains a valid null-terminator at the end of string.
162+
// - The memory region `sz_reader` points to is valid for reads of bytes up to and including null-terminator.
163+
unsafe { c_w_str_to_string(sz_reader) }.map_err(Error::from)
164+
);
163165

164166
try_execute!(
165167
// SAFETY:

0 commit comments

Comments
 (0)