New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix potential use after free in MacOS / iOS impl #54
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,61 @@ | ||
use core_foundation_sys::base::{CFRelease, CFTypeRef}; | ||
use core_foundation_sys::string::{kCFStringEncodingUTF8, CFStringGetCStringPtr}; | ||
use core_foundation_sys::base::{Boolean, CFRange, CFRelease, CFTypeRef}; | ||
use core_foundation_sys::string::{ | ||
kCFStringEncodingUTF8, CFStringGetBytes, CFStringGetCStringPtr, CFStringGetLength, | ||
}; | ||
use core_foundation_sys::timezone::{CFTimeZoneCopySystem, CFTimeZoneGetName}; | ||
|
||
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> { | ||
unsafe { | ||
Dropping::new(CFTimeZoneCopySystem()) | ||
.and_then(|tz| Dropping::new(CFTimeZoneGetName(tz.0))) | ||
.and_then(|name| { | ||
let name = CFStringGetCStringPtr(name.0, kCFStringEncodingUTF8); | ||
if name.is_null() { | ||
None | ||
} else { | ||
Some(name) | ||
} | ||
}) | ||
.and_then(|name| std::ffi::CStr::from_ptr(name).to_str().ok()) | ||
.map(|name| name.to_owned()) | ||
.ok_or(crate::GetTimezoneError::OsError) | ||
unsafe { get_timezone().ok_or(crate::GetTimezoneError::OsError) } | ||
} | ||
|
||
#[inline] | ||
unsafe fn get_timezone() -> Option<String> { | ||
// The longest name in the IANA time zone database is 25 ASCII characters long. | ||
const MAX_LEN: usize = 32; | ||
|
||
// Get system time zone, and borrow its name. | ||
let tz = Dropping::new(CFTimeZoneCopySystem())?; | ||
let name = CFTimeZoneGetName(tz.0); | ||
if name.is_null() { | ||
return None; | ||
} | ||
|
||
// If the name is encoded in UTF-8, copy it directly. | ||
let cstr = CFStringGetCStringPtr(name, kCFStringEncodingUTF8); | ||
if !cstr.is_null() { | ||
let cstr = std::ffi::CStr::from_ptr(cstr); | ||
if let Ok(name) = cstr.to_str() { | ||
return Some(name.to_owned()); | ||
} | ||
} | ||
|
||
// Otherwise convert the name to UTF-8. | ||
let mut buf = [0; MAX_LEN]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I love that we're avoiding the heap allocation here but would like to make sure our assumptions are valid. What do you think about adding an assert that
I think the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh I see now, the check below for |
||
let mut buf_bytes = 0; | ||
let range = CFRange { | ||
location: 0, | ||
length: CFStringGetLength(name), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: this length is "character length" not byte length. https://developer.apple.com/documentation/corefoundation/1542853-cfstringgetlength
|
||
}; | ||
if CFStringGetBytes( | ||
name, | ||
range, | ||
kCFStringEncodingUTF8, | ||
b'\0', | ||
false as Boolean, | ||
buf.as_mut_ptr(), | ||
buf.len() as isize, | ||
&mut buf_bytes, | ||
) != range.length | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This return value is the number of encoding-aware characters, not the length of the byte string. the length stored in the out parameter https://developer.apple.com/documentation/corefoundation/1543006-cfstringgetbytes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I check that all characters in the original encoding were consumed, not more or less. But I use core-foundation even asserts that the length matches: https://docs.rs/core-foundation/0.9.3/src/core_foundation/string.rs.html#82 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh I see. https://developer.apple.com/documentation/corefoundation/1543006-cfstringgetbytes
|
||
{ | ||
// Could not convert the name. | ||
None | ||
} else if !(1..MAX_LEN as isize).contains(&buf_bytes) { | ||
// The name should not be empty, or excessively long. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we want the excessively long case to be a panic? |
||
None | ||
} else { | ||
// Convert the name to a `String`. | ||
let name = core::str::from_utf8(&buf[..buf_bytes as usize]).ok()?; | ||
Some(name.to_owned()) | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for adding these comments outlining the phases of this function. 🎉