From 701e57197a17496573ae9890dced43bad73c176c Mon Sep 17 00:00:00 2001 From: Josh Megnauth Date: Tue, 29 Oct 2024 12:26:46 +0000 Subject: [PATCH] fix(netdb): Out of bounds in gethostbyaddr and less transmutes --- src/header/netdb/host.rs | 2 +- src/header/netdb/lookup.rs | 20 +++++++++---------- src/header/netdb/mod.rs | 41 +++++++++++++++++++++++++------------- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/header/netdb/host.rs b/src/header/netdb/host.rs index 4edb9e69a4..576f8d0593 100644 --- a/src/header/netdb/host.rs +++ b/src/header/netdb/host.rs @@ -85,7 +85,7 @@ pub unsafe extern "C" fn gethostent() -> *mut hostent { inet_aton(addr_cstr, addr.as_mut_ptr()); let addr = addr.assume_init(); - _HOST_ADDR_LIST = mem::transmute::(addr.s_addr); + _HOST_ADDR_LIST = addr.s_addr.to_ne_bytes(); HOST_ADDR_LIST = [_HOST_ADDR_LIST.as_mut_ptr() as *mut c_char, ptr::null_mut()]; HOST_ADDR = Some(addr); diff --git a/src/header/netdb/lookup.rs b/src/header/netdb/lookup.rs index 1379a149e5..e2cfd60696 100644 --- a/src/header/netdb/lookup.rs +++ b/src/header/netdb/lookup.rs @@ -47,7 +47,7 @@ pub fn lookup_host(host: &str) -> Result { for (i, octet) in dns_vec.iter().enumerate() { dns_arr[i] = *octet; } - let dns_addr = unsafe { mem::transmute::<[u8; 4], u32>(dns_arr) }; + let dns_addr = u32::from_ne_bytes(dns_arr); let mut timespec = timespec::default(); unsafe { @@ -112,14 +112,12 @@ pub fn lookup_host(host: &str) -> Result { if answer.a_type == 0x0001 && answer.a_class == 0x0001 && answer.data.len() == 4 { let addr = in_addr { - s_addr: unsafe { - mem::transmute::<[u8; 4], u32>([ - answer.data[0], - answer.data[1], - answer.data[2], - answer.data[3], - ]) - }, + s_addr: u32::from_ne_bytes([ + answer.data[0], + answer.data[1], + answer.data[2], + answer.data[3], + ]), }; addrs.push(addr); } @@ -148,7 +146,7 @@ pub fn lookup_addr(addr: in_addr) -> Result>, c_int> { dns_arr[i] = *octet; } - let mut addr_vec: Vec = unsafe { mem::transmute::(addr.s_addr).to_vec() }; + let mut addr_vec: Vec = addr.s_addr.to_ne_bytes().to_vec(); addr_vec.reverse(); let mut name: Vec = vec![]; for octet in addr_vec { @@ -187,7 +185,7 @@ pub fn lookup_addr(addr: in_addr) -> Result>, c_int> { sin_family: AF_INET as u16, sin_port: htons(53), sin_addr: in_addr { - s_addr: unsafe { mem::transmute::<[u8; 4], u32>(dns_arr) }, + s_addr: u32::from_ne_bytes(dns_arr), }, ..Default::default() }; diff --git a/src/header/netdb/mod.rs b/src/header/netdb/mod.rs index 508bd176f4..d5ee94a52f 100644 --- a/src/header/netdb/mod.rs +++ b/src/header/netdb/mod.rs @@ -229,11 +229,10 @@ pub unsafe extern "C" fn gethostbyaddr( host_aliases.push(ptr::null_mut()); HOST_ALIASES = Some(_host_aliases); - match lookup_addr(addr) { - Ok(s) => { - _HOST_ADDR_LIST = mem::transmute::(addr.s_addr); + match lookup_addr(addr).map(|host_names| host_names.into_iter().next()) { + Ok(Some(host_name)) => { + _HOST_ADDR_LIST = addr.s_addr.to_ne_bytes(); HOST_ADDR_LIST = [_HOST_ADDR_LIST.as_mut_ptr() as *mut c_char, ptr::null_mut()]; - let host_name = s[0].to_vec(); HOST_NAME = Some(host_name); HOST_ENTRY = hostent { h_name: HOST_NAME.as_mut().unwrap().as_mut_ptr() as *mut c_char, @@ -244,6 +243,13 @@ pub unsafe extern "C" fn gethostbyaddr( }; &mut HOST_ENTRY } + // `glibc` sets errno if an address doesn't have a host name + // `musl` uses the address as the host name in said case + Ok(None) => { + // TODO: Set h_errno instead to match spec + platform::ERRNO.set(EIO); + ptr::null_mut() + } Err(e) => { platform::ERRNO.set(e); ptr::null_mut() @@ -253,9 +259,18 @@ pub unsafe extern "C" fn gethostbyaddr( #[no_mangle] pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut hostent { - // check if some idiot gave us an address instead of a name - let name_cstr = CStr::from_ptr(name); - let mut octets = str::from_utf8_unchecked(name_cstr.to_bytes()).split('.'); + let name_cstr = + CStr::from_nullable_ptr(name).expect("gethostbyname() called with a NULL pointer"); + let Ok(name_str) = str::from_utf8(name_cstr.to_bytes()) else { + // TODO Set h_errno instead of errno (spec) + platform::ERRNO.set(EINVAL); + return ptr::null_mut(); + }; + + // Addresses and hostnames are both valid, so we'll check addresses first + // The standard doesn't define what to do when called with addresses + // Some implementations just skip resolution and copy the address to h_name + let mut octets = name_str.split('.'); let mut s_addr = [0u8; 4]; let mut is_addr = true; for item in &mut s_addr { @@ -263,6 +278,7 @@ pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut hostent { *item = n; } else { is_addr = false; + break; } } if octets.next() != None { @@ -270,9 +286,8 @@ pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut hostent { } if is_addr { - let addr = in_addr { - s_addr: mem::transmute::<[u8; 4], u32>(s_addr), - }; + let s_addr = u32::from_ne_bytes(s_addr); + let addr = in_addr { s_addr }; return gethostbyaddr(&addr as *const _ as *const c_void, 4, AF_INET); } @@ -303,9 +318,7 @@ pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut hostent { } } - let name_cstr = CStr::from_ptr(name); - - let mut host = match lookup_host(str::from_utf8_unchecked(name_cstr.to_bytes())) { + let mut host = match lookup_host(name_str) { Ok(lookuphost) => lookuphost, Err(e) => { platform::ERRNO.set(e); @@ -322,7 +335,7 @@ pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut hostent { let host_name: Vec = name_cstr.to_bytes().to_vec(); HOST_NAME = Some(host_name); - _HOST_ADDR_LIST = mem::transmute::(host_addr.s_addr); + _HOST_ADDR_LIST = host_addr.s_addr.to_ne_bytes(); HOST_ADDR_LIST = [_HOST_ADDR_LIST.as_mut_ptr() as *mut c_char, ptr::null_mut()]; HOST_ADDR = Some(host_addr);