summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJoakim Hulthe <joakim.hulthe@mullvad.net>2025-02-25 14:10:59 +0100
committerJoakim Hulthe <joakim.hulthe@mullvad.net>2025-02-25 14:10:59 +0100
commit60ccaee30c558ec05dbe36224cdb66464fab4272 (patch)
tree1d9f0950f9a3209f5ad4d928f4d342a9da7c2586
parentc06b29d60703bcfcfbbb48deef3c526631f847b7 (diff)
parent148e43dc21254caab5edcabfad6bbda957208e0e (diff)
downloadmullvadvpn-60ccaee30c558ec05dbe36224cdb66464fab4272.tar.xz
mullvadvpn-60ccaee30c558ec05dbe36224cdb66464fab4272.zip
Merge branch 'document-and-lint-all-usages-of-unsafe-des-1459'
-rw-r--r--Cargo.lock4
-rw-r--r--Cargo.toml1
-rw-r--r--desktop/packages/nseventforwarder/nseventforwarder-rs/lib.rs1
-rw-r--r--mullvad-api/src/ffi/mod.rs2
-rw-r--r--mullvad-daemon/src/exception_logging/unix.rs1
-rw-r--r--mullvad-daemon/src/exception_logging/win.rs2
-rw-r--r--mullvad-daemon/src/macos_launch_daemon.rs2
-rw-r--r--mullvad-daemon/src/main.rs3
-rw-r--r--mullvad-daemon/src/migrations/mod.rs44
-rw-r--r--mullvad-daemon/src/system_service.rs2
-rw-r--r--mullvad-ios/src/lib.rs2
-rw-r--r--mullvad-leak-checker/src/lib.rs9
-rw-r--r--mullvad-paths/src/windows.rs3
-rw-r--r--talpid-core/src/dns/macos.rs2
-rw-r--r--talpid-core/src/dns/windows/dnsapi.rs1
-rw-r--r--talpid-core/src/dns/windows/iphlpapi.rs1
-rw-r--r--talpid-core/src/dns/windows/netsh.rs2
-rw-r--r--talpid-core/src/dns/windows/tcpip.rs8
-rw-r--r--talpid-core/src/firewall/windows/mod.rs2
-rw-r--r--talpid-core/src/linux/mod.rs29
-rw-r--r--talpid-core/src/offline/windows.rs2
-rw-r--r--talpid-core/src/split_tunnel/macos/bpf.rs11
-rw-r--r--talpid-core/src/split_tunnel/macos/tun.rs12
-rw-r--r--talpid-core/src/split_tunnel/windows/driver.rs2
-rw-r--r--talpid-core/src/split_tunnel/windows/mod.rs2
-rw-r--r--talpid-core/src/window.rs1
-rw-r--r--talpid-net/Cargo.toml2
-rw-r--r--talpid-net/src/unix.rs20
-rw-r--r--talpid-openvpn/src/lib.rs1
-rw-r--r--talpid-openvpn/src/wintun.rs9
-rw-r--r--talpid-platform-metadata/src/windows.rs49
-rw-r--r--talpid-routing/src/unix/macos/data.rs17
-rw-r--r--talpid-routing/src/unix/macos/interface.rs2
-rw-r--r--talpid-routing/src/windows/get_best_default_route.rs8
-rw-r--r--talpid-routing/src/windows/route_manager.rs3
-rw-r--r--talpid-time/Cargo.toml2
-rw-r--r--talpid-time/src/unix.rs31
-rw-r--r--talpid-tunnel-config-client/src/socket.rs18
-rw-r--r--talpid-tunnel/src/tun_provider/android/mod.rs5
-rw-r--r--talpid-windows/src/io.rs6
-rw-r--r--talpid-windows/src/net.rs8
-rw-r--r--talpid-windows/src/process.rs2
-rw-r--r--talpid-windows/src/sync.rs2
-rw-r--r--talpid-wireguard/src/wireguard_go/mod.rs4
-rw-r--r--talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs28
-rw-r--r--talpid-wireguard/src/wireguard_kernel/parsers.rs43
-rw-r--r--talpid-wireguard/src/wireguard_kernel/wg_message.rs6
-rw-r--r--talpid-wireguard/src/wireguard_nt/mod.rs2
-rw-r--r--test/Cargo.lock2
-rw-r--r--windows-installer/src/main.rs1
50 files changed, 268 insertions, 154 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 4afca015ea..aa087cf126 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4615,8 +4615,10 @@ version = "0.0.0"
dependencies = [
"libc",
"log",
+ "nix 0.29.0",
"socket2",
"talpid-types",
+ "thiserror 2.0.9",
]
[[package]]
@@ -4705,7 +4707,7 @@ dependencies = [
name = "talpid-time"
version = "0.0.0"
dependencies = [
- "libc",
+ "nix 0.29.0",
"tokio",
]
diff --git a/Cargo.toml b/Cargo.toml
index 0931b8a534..603912f350 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -74,6 +74,7 @@ single_use_lifetimes = "warn"
[workspace.lints.clippy]
unused_async = "deny"
+undocumented_unsafe_blocks = "warn"
[workspace.dependencies]
hickory-proto = "0.24.3"
diff --git a/desktop/packages/nseventforwarder/nseventforwarder-rs/lib.rs b/desktop/packages/nseventforwarder/nseventforwarder-rs/lib.rs
index 09c7271e5a..152019828e 100644
--- a/desktop/packages/nseventforwarder/nseventforwarder-rs/lib.rs
+++ b/desktop/packages/nseventforwarder/nseventforwarder-rs/lib.rs
@@ -1,6 +1,5 @@
//! Forward [NSEvent]s from macOS to node.
#![cfg(target_os = "macos")]
-#![warn(clippy::undocumented_unsafe_blocks)]
use std::ptr::NonNull;
use std::sync::{mpsc, Arc, Mutex};
diff --git a/mullvad-api/src/ffi/mod.rs b/mullvad-api/src/ffi/mod.rs
index 5eae180f01..3e0324d29b 100644
--- a/mullvad-api/src/ffi/mod.rs
+++ b/mullvad-api/src/ffi/mod.rs
@@ -1,4 +1,6 @@
#![cfg(not(target_os = "android"))]
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use std::{
ffi::{CStr, CString},
net::SocketAddr,
diff --git a/mullvad-daemon/src/exception_logging/unix.rs b/mullvad-daemon/src/exception_logging/unix.rs
index b956db8319..99bc0d8ec7 100644
--- a/mullvad-daemon/src/exception_logging/unix.rs
+++ b/mullvad-daemon/src/exception_logging/unix.rs
@@ -1,5 +1,4 @@
//! Install signal handlers to catch critical program faults and log them. See [`enable`].
-#![warn(clippy::undocumented_unsafe_blocks)]
use libc::siginfo_t;
use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal};
diff --git a/mullvad-daemon/src/exception_logging/win.rs b/mullvad-daemon/src/exception_logging/win.rs
index 3fa50aa506..2c11f61578 100644
--- a/mullvad-daemon/src/exception_logging/win.rs
+++ b/mullvad-daemon/src/exception_logging/win.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)]
+
use mullvad_paths::log_dir;
use std::{
borrow::Cow,
diff --git a/mullvad-daemon/src/macos_launch_daemon.rs b/mullvad-daemon/src/macos_launch_daemon.rs
index 0c9c89080c..4b7a6c5efa 100644
--- a/mullvad-daemon/src/macos_launch_daemon.rs
+++ b/mullvad-daemon/src/macos_launch_daemon.rs
@@ -5,6 +5,8 @@
//! must be checked so that the user can be directed to approve the launch
//! daemon in the system settings.
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use libc::c_longlong;
use objc2::{class, msg_send, runtime::AnyObject, Encode, Encoding, RefEncode};
use std::ffi::CStr;
diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs
index c2146752c7..58e542364f 100644
--- a/mullvad-daemon/src/main.rs
+++ b/mullvad-daemon/src/main.rs
@@ -229,8 +229,7 @@ async fn create_daemon(log_dir: Option<PathBuf>) -> Result<Daemon, String> {
#[cfg(unix)]
fn running_as_admin() -> bool {
- let uid = unsafe { libc::getuid() };
- uid == 0
+ nix::unistd::Uid::current().is_root()
}
#[cfg(windows)]
diff --git a/mullvad-daemon/src/migrations/mod.rs b/mullvad-daemon/src/migrations/mod.rs
index 635c2f6ebe..eaf3350e0c 100644
--- a/mullvad-daemon/src/migrations/mod.rs
+++ b/mullvad-daemon/src/migrations/mod.rs
@@ -227,15 +227,22 @@ pub(crate) fn migrate_device(
#[cfg(windows)]
mod windows {
- use std::{ffi::OsStr, io, os::windows::ffi::OsStrExt, path::Path, ptr};
+ use std::{
+ ffi::OsStr,
+ io,
+ os::windows::ffi::OsStrExt,
+ path::Path,
+ ptr::{self, NonNull},
+ };
use talpid_types::ErrorExt;
use tokio::fs;
use windows_sys::Win32::{
- Foundation::{LocalFree, ERROR_SUCCESS, HLOCAL, PSID},
+ Foundation::{LocalFree, ERROR_SUCCESS, PSID},
Security::{
Authorization::{GetNamedSecurityInfoW, SE_FILE_OBJECT, SE_OBJECT_TYPE},
IsWellKnownSid, WinBuiltinAdministratorsSid, WinLocalSystemSid,
- OWNER_SECURITY_INFORMATION, SECURITY_DESCRIPTOR, SID, WELL_KNOWN_SID_TYPE,
+ OWNER_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, SECURITY_DESCRIPTOR, SID,
+ WELL_KNOWN_SID_TYPE,
},
};
@@ -355,8 +362,8 @@ mod windows {
}
struct SecurityInformation {
- security_descriptor: *mut SECURITY_DESCRIPTOR,
- owner: PSID,
+ security_descriptor: NonNull<SECURITY_DESCRIPTOR>,
+ owner: Option<NonNull<SID>>,
}
impl SecurityInformation {
@@ -375,9 +382,12 @@ mod windows {
let mut u16_path: Vec<u16> = object_name.as_ref().encode_wide().collect();
u16_path.push(0u16);
- let mut security_descriptor = ptr::null_mut();
- let mut owner = ptr::null_mut();
+ let mut security_descriptor: PSECURITY_DESCRIPTOR = ptr::null_mut();
+ let mut owner: PSID = ptr::null_mut();
+ // SAFETY:
+ // - u16_path is a null-terminated UTF-16 string
+ // - The *mut pointers are allowed to be null
let status = unsafe {
GetNamedSecurityInfoW(
u16_path.as_ptr(),
@@ -395,25 +405,35 @@ mod windows {
return Err(std::io::Error::from_raw_os_error(status as i32));
}
+ let Some(security_descriptor) = NonNull::new(security_descriptor) else {
+ return Err(std::io::Error::other("GetNamedSecurityInfoW returned null"));
+ };
+
Ok(SecurityInformation {
- security_descriptor: security_descriptor as *mut _,
- owner,
+ security_descriptor: security_descriptor.cast::<SECURITY_DESCRIPTOR>(),
+ owner: NonNull::new(owner.cast::<SID>()),
})
}
pub fn owner(&self) -> Option<&SID> {
- unsafe { (self.owner as *const SID).as_ref() }
+ // SAFETY: GetNamedSecurityInfoW promises that this pointer was valid,
+ // and it should stay valid until we deallocate self.security_descriptor.
+ self.owner.map(|ptr| unsafe { ptr.as_ref() })
}
}
impl Drop for SecurityInformation {
fn drop(&mut self) {
- unsafe { LocalFree(self.security_descriptor as HLOCAL) };
+ // SAFETY: GetNamedSecurityInfoW promises that this pointer was valid,
+ // and we do not deallocate it before this point. Since we have &mut self,
+ // we know that no one else has a reference to security_descriptor.
+ unsafe { LocalFree(self.security_descriptor.as_ptr() as PSECURITY_DESCRIPTOR) };
}
}
fn is_well_known_sid(sid: &SID, well_known_sid_type: WELL_KNOWN_SID_TYPE) -> bool {
- unsafe { IsWellKnownSid(sid as *const SID as *mut _, well_known_sid_type) == 1 }
+ // SAFETY: this function doesn't take ownership of sid, and is trivially safe to call.
+ unsafe { IsWellKnownSid(sid as *const SID as PSID, well_known_sid_type) == 1 }
}
}
diff --git a/mullvad-daemon/src/system_service.rs b/mullvad-daemon/src/system_service.rs
index ccec49486b..6b1f521474 100644
--- a/mullvad-daemon/src/system_service.rs
+++ b/mullvad-daemon/src/system_service.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use crate::cli;
use mullvad_daemon::{runtime::new_multi_thread, DaemonShutdownHandle};
use std::{
diff --git a/mullvad-ios/src/lib.rs b/mullvad-ios/src/lib.rs
index fa23672e29..2fea39f9c7 100644
--- a/mullvad-ios/src/lib.rs
+++ b/mullvad-ios/src/lib.rs
@@ -1,4 +1,6 @@
#![cfg(target_os = "ios")]
+#![allow(clippy::undocumented_unsafe_blocks)]
+
mod api_client;
mod encrypted_dns_proxy;
mod ephemeral_peer_proxy;
diff --git a/mullvad-leak-checker/src/lib.rs b/mullvad-leak-checker/src/lib.rs
index eb80211518..3a0bf43e22 100644
--- a/mullvad-leak-checker/src/lib.rs
+++ b/mullvad-leak-checker/src/lib.rs
@@ -47,9 +47,14 @@ impl fmt::Debug for Interface {
match self {
Self::Name(arg0) => f.debug_tuple("Name").field(arg0).finish(),
- // SAFETY: u64 is valid for all bit patterns, so reading the union as a u64 is safe.
#[cfg(target_os = "windows")]
- Self::Luid(arg0) => f.debug_tuple("Luid").field(&unsafe { arg0.Value }).finish(),
+ Self::Luid(arg0) => f
+ .debug_tuple("Luid")
+ .field(
+ // SAFETY: u64 is valid for all bit patterns, so reading the union as a u64 is safe.
+ &unsafe { arg0.Value },
+ )
+ .finish(),
#[cfg(target_os = "macos")]
Self::Index(arg0) => f.debug_tuple("Luid").field(arg0).finish(),
diff --git a/mullvad-paths/src/windows.rs b/mullvad-paths/src/windows.rs
index 2898e76ba8..fa652f31f7 100644
--- a/mullvad-paths/src/windows.rs
+++ b/mullvad-paths/src/windows.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use crate::{Error, Result};
use once_cell::sync::OnceCell;
use std::{
@@ -362,6 +364,7 @@ fn adjust_token_privilege(
privilege: &WideCStr,
enable: bool,
) -> std::io::Result<()> {
+ // SAFETY: LUID is a C struct and can safely be zeroed.
let mut privilege_luid: LUID = unsafe { mem::zeroed() };
if unsafe { LookupPrivilegeValueW(ptr::null(), privilege.as_ptr(), &mut privilege_luid) } == 0 {
diff --git a/talpid-core/src/dns/macos.rs b/talpid-core/src/dns/macos.rs
index aa40c0c4bc..09d9536836 100644
--- a/talpid-core/src/dns/macos.rs
+++ b/talpid-core/src/dns/macos.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use parking_lot::Mutex;
use std::{
collections::{BTreeSet, HashMap},
diff --git a/talpid-core/src/dns/windows/dnsapi.rs b/talpid-core/src/dns/windows/dnsapi.rs
index 03d9d44abf..b33dbee592 100644
--- a/talpid-core/src/dns/windows/dnsapi.rs
+++ b/talpid-core/src/dns/windows/dnsapi.rs
@@ -62,6 +62,7 @@ impl DnsApi {
std::thread::spawn(move || {
let begin = Instant::now();
+ // SAFETY: this function is trivially safe to call
let result = if unsafe { (DnsFlushResolverCache)() } != 0 {
let elapsed = begin.elapsed();
if elapsed >= FLUSH_TIMEOUT {
diff --git a/talpid-core/src/dns/windows/iphlpapi.rs b/talpid-core/src/dns/windows/iphlpapi.rs
index eeb4b3560c..a30543b4a1 100644
--- a/talpid-core/src/dns/windows/iphlpapi.rs
+++ b/talpid-core/src/dns/windows/iphlpapi.rs
@@ -2,6 +2,7 @@
//! <https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-setinterfacednssettings>,
//! it requires at least Windows 10, build 19041. For that reason, use run-time linking and fall
//! back on other methods if it is not available.
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
use crate::dns::{DnsMonitorT, ResolvedDnsConfig};
use once_cell::sync::OnceCell;
diff --git a/talpid-core/src/dns/windows/netsh.rs b/talpid-core/src/dns/windows/netsh.rs
index b731310056..cab3171d48 100644
--- a/talpid-core/src/dns/windows/netsh.rs
+++ b/talpid-core/src/dns/windows/netsh.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use crate::dns::{DnsMonitorT, ResolvedDnsConfig};
use std::{
ffi::OsString,
diff --git a/talpid-core/src/dns/windows/tcpip.rs b/talpid-core/src/dns/windows/tcpip.rs
index 70bb4660d6..3e0a8ea6e1 100644
--- a/talpid-core/src/dns/windows/tcpip.rs
+++ b/talpid-core/src/dns/windows/tcpip.rs
@@ -164,8 +164,12 @@ fn flush_dns_cache() -> Result<(), Error> {
/// Obtain a string representation for a GUID object.
fn string_from_guid(guid: &GUID) -> String {
let mut buffer = [0u16; 40];
- let length = unsafe { StringFromGUID2(guid, &mut buffer[0] as *mut _, buffer.len() as i32 - 1) }
- as usize;
+
+ let length =
+ // SAFETY: `guid` and `buffer` are valid references.
+ // StringFromGUID2 won't write past the end of the provided length.
+ unsafe { StringFromGUID2(guid, buffer.as_mut_ptr(), buffer.len() as i32 - 1) } as usize;
+
// cannot fail because `buffer` is large enough
assert!(length > 0);
let length = length - 1;
diff --git a/talpid-core/src/firewall/windows/mod.rs b/talpid-core/src/firewall/windows/mod.rs
index 8dadd7db5c..3ef49938dd 100644
--- a/talpid-core/src/firewall/windows/mod.rs
+++ b/talpid-core/src/firewall/windows/mod.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use crate::{dns::ResolvedDnsConfig, tunnel::TunnelMetadata};
use std::{ffi::CStr, io, net::IpAddr, ptr, sync::LazyLock};
diff --git a/talpid-core/src/linux/mod.rs b/talpid-core/src/linux/mod.rs
index 4b5321e4f0..225534ea12 100644
--- a/talpid-core/src/linux/mod.rs
+++ b/talpid-core/src/linux/mod.rs
@@ -1,27 +1,16 @@
-use std::{
- ffi::{self, CString},
- io,
-};
+use nix::{errno::Errno, net::if_::if_nametoindex};
/// Converts an interface name into the corresponding index.
pub fn iface_index(name: &str) -> Result<libc::c_uint, IfaceIndexLookupError> {
- let c_name = CString::new(name)
- .map_err(|e| IfaceIndexLookupError::InvalidInterfaceName(name.to_owned(), e))?;
- let index = unsafe { libc::if_nametoindex(c_name.as_ptr()) };
- if index == 0 {
- Err(IfaceIndexLookupError::InterfaceLookupError(
- name.to_owned(),
- io::Error::last_os_error(),
- ))
- } else {
- Ok(index)
- }
+ if_nametoindex(name).map_err(|error| IfaceIndexLookupError {
+ interface_name: name.to_owned(),
+ error,
+ })
}
#[derive(Debug, thiserror::Error)]
-pub enum IfaceIndexLookupError {
- #[error("Invalid network interface name: {0}")]
- InvalidInterfaceName(String, #[source] ffi::NulError),
- #[error("Failed to get index for interface {0}")]
- InterfaceLookupError(String, #[source] io::Error),
+#[error("Failed to get index for interface {interface_name}: {error}")]
+pub struct IfaceIndexLookupError {
+ pub interface_name: String,
+ pub error: Errno,
}
diff --git a/talpid-core/src/offline/windows.rs b/talpid-core/src/offline/windows.rs
index 5e09763cd0..56616cba51 100644
--- a/talpid-core/src/offline/windows.rs
+++ b/talpid-core/src/offline/windows.rs
@@ -24,8 +24,6 @@ pub struct BroadcastListener {
_notify_tx: Arc<UnboundedSender<Connectivity>>,
}
-unsafe impl Send for BroadcastListener {}
-
impl BroadcastListener {
pub async fn start(
notify_tx: UnboundedSender<Connectivity>,
diff --git a/talpid-core/src/split_tunnel/macos/bpf.rs b/talpid-core/src/split_tunnel/macos/bpf.rs
index 566cc1f56b..53d3d36fbe 100644
--- a/talpid-core/src/split_tunnel/macos/bpf.rs
+++ b/talpid-core/src/split_tunnel/macos/bpf.rs
@@ -132,16 +132,17 @@ impl Bpf {
return Err(Error::InterfaceNameTooLong);
}
+ // SAFETY: `name_bytes` cannot exceed the size of `ifr_name`
unsafe {
- // SAFETY: `name_bytes` cannot exceed the size of `ifr_name`
std::ptr::copy_nonoverlapping(
name_bytes.as_ptr(),
&mut ifr.ifr_name as *mut _ as *mut _,
name_bytes.len(),
- );
- // SAFETY: The fd is valid for the lifetime of `self`, and `ifr` has a valid interface
- ioctl!(self.file.as_raw_fd(), BIOCSETIF, &ifr)
- }
+ )
+ };
+
+ // SAFETY: The fd is valid for the lifetime of `self`, and `ifr` has a valid interface
+ unsafe { ioctl!(self.file.as_raw_fd(), BIOCSETIF, &ifr) }
}
/// Enable or disable immediate mode (BIOCIMMEDIATE)
diff --git a/talpid-core/src/split_tunnel/macos/tun.rs b/talpid-core/src/split_tunnel/macos/tun.rs
index 90581c736c..2533e8a360 100644
--- a/talpid-core/src/split_tunnel/macos/tun.rs
+++ b/talpid-core/src/split_tunnel/macos/tun.rs
@@ -20,7 +20,7 @@ use pnet_packet::{
MutablePacket, Packet,
};
use std::{
- ffi::{c_uint, CStr},
+ ffi::c_uint,
io::{self, IoSlice, Write},
net::{IpAddr, Ipv4Addr, Ipv6Addr},
};
@@ -887,8 +887,14 @@ impl PacketCodec for PktapCodec {
return None;
}
- let iface = unsafe { CStr::from_ptr(header.pth_ifname.as_ptr() as *const _) };
- if iface.to_bytes() != self.interface.as_bytes() {
+ // cast the array from [i8] to [u8] to enable comparison with String::as_bytes
+ let iface = header.pth_ifname.map(|b| b as u8);
+ // get the interface name by splitting on the first null byte (if any)
+ let iface = iface
+ .split(|&b| b == 0)
+ .next()
+ .expect("split will yield at least one element");
+ if iface != self.interface.as_bytes() {
return None;
}
diff --git a/talpid-core/src/split_tunnel/windows/driver.rs b/talpid-core/src/split_tunnel/windows/driver.rs
index e3026b0d95..9625e41406 100644
--- a/talpid-core/src/split_tunnel/windows/driver.rs
+++ b/talpid-core/src/split_tunnel/windows/driver.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use super::windows::{
get_device_path, get_process_creation_time, get_process_device_path, open_process,
ProcessAccess,
diff --git a/talpid-core/src/split_tunnel/windows/mod.rs b/talpid-core/src/split_tunnel/windows/mod.rs
index 1c446377fc..79c0d09a5c 100644
--- a/talpid-core/src/split_tunnel/windows/mod.rs
+++ b/talpid-core/src/split_tunnel/windows/mod.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
mod driver;
mod path_monitor;
mod service;
diff --git a/talpid-core/src/window.rs b/talpid-core/src/window.rs
index 30df21f2da..33e83614b3 100644
--- a/talpid-core/src/window.rs
+++ b/talpid-core/src/window.rs
@@ -1,4 +1,5 @@
//! Utilities for working with windows on Windows.
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
use std::{os::windows::io::AsRawHandle, ptr, sync::Arc, thread};
use tokio::sync::broadcast;
diff --git a/talpid-net/Cargo.toml b/talpid-net/Cargo.toml
index 861e1765cc..485eaa37d0 100644
--- a/talpid-net/Cargo.toml
+++ b/talpid-net/Cargo.toml
@@ -15,3 +15,5 @@ libc = "0.2"
talpid-types = { path = "../talpid-types" }
socket2 = { workspace = true, features = ["all"] }
log = { workspace = true }
+thiserror = { workspace = true }
+nix = { version = "0.29", features = ["net"] }
diff --git a/talpid-net/src/unix.rs b/talpid-net/src/unix.rs
index ef2bfdeb27..48d65c45f0 100644
--- a/talpid-net/src/unix.rs
+++ b/talpid-net/src/unix.rs
@@ -1,10 +1,26 @@
#![cfg(any(target_os = "linux", target_os = "macos"))]
-use std::{io, os::fd::AsRawFd};
+use std::{ffi::c_uint, io, os::fd::AsRawFd};
+use nix::{errno::Errno, net::if_::if_nametoindex};
use socket2::Domain;
use talpid_types::ErrorExt;
+/// Converts an interface name into the corresponding index.
+pub fn iface_index(name: &str) -> Result<c_uint, IfaceIndexLookupError> {
+ if_nametoindex(name).map_err(|error| IfaceIndexLookupError {
+ interface_name: name.to_owned(),
+ error,
+ })
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("Failed to get index for interface {interface_name}: {error}")]
+pub struct IfaceIndexLookupError {
+ pub interface_name: String,
+ pub error: Errno,
+}
+
#[cfg(target_os = "macos")]
const SIOCSIFMTU: u64 = 0x80206934;
#[cfg(target_os = "macos")]
@@ -21,6 +37,7 @@ pub fn set_mtu(interface_name: &str, mtu: u16) -> Result<(), io::Error> {
Some(socket2::Protocol::TCP),
)?;
+ // SAFETY: ifreq is a C struct, these can safely be zeroed.
let mut ifr: libc::ifreq = unsafe { std::mem::zeroed() };
if interface_name.len() >= ifr.ifr_name.len() {
return Err(io::Error::new(
@@ -55,6 +72,7 @@ pub fn get_mtu(interface_name: &str) -> Result<u16, io::Error> {
Some(socket2::Protocol::TCP),
)?;
+ // SAFETY: ifreq is a C struct, these can safely be zeroed.
let mut ifr: libc::ifreq = unsafe { std::mem::zeroed() };
if interface_name.len() >= ifr.ifr_name.len() {
return Err(io::Error::new(
diff --git a/talpid-openvpn/src/lib.rs b/talpid-openvpn/src/lib.rs
index d01290e6f3..399bc3fa36 100644
--- a/talpid-openvpn/src/lib.rs
+++ b/talpid-openvpn/src/lib.rs
@@ -200,6 +200,7 @@ impl std::fmt::Debug for dyn WintunContext {
write!(
f,
"WintunContext {{ luid: {}, ipv6: {} }}",
+ // SAFETY: It's always safe to interpet a LUID as an u64
unsafe { self.luid().Value },
self.ipv6()
)
diff --git a/talpid-openvpn/src/wintun.rs b/talpid-openvpn/src/wintun.rs
index e3e7938828..ef5ebd5482 100644
--- a/talpid-openvpn/src/wintun.rs
+++ b/talpid-openvpn/src/wintun.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use once_cell::sync::OnceCell;
use std::{ffi::CStr, fmt, io, mem, os::windows::io::RawHandle, path::Path, ptr};
use talpid_types::{win32_err, ErrorExt};
@@ -347,8 +349,11 @@ fn find_adapter_registry_key(find_guid: &str, permissions: REG_SAM_FLAGS) -> io:
/// Obtain a string representation for a GUID object.
fn string_from_guid(guid: &GUID) -> String {
let mut buffer = [0u16; 40];
- let length = unsafe { StringFromGUID2(guid, &mut buffer[0] as *mut _, buffer.len() as i32 - 1) }
- as usize;
+
+ // SAFETY: `guid` and `buffer` are valid references.
+ let length =
+ unsafe { StringFromGUID2(guid, buffer.as_mut_ptr(), buffer.len() as i32 - 1) } as usize;
+
// cannot fail because `buffer` is large enough
assert!(length > 0);
let length = length - 1;
diff --git a/talpid-platform-metadata/src/windows.rs b/talpid-platform-metadata/src/windows.rs
index 1df2cb0f12..99e2f18b3c 100644
--- a/talpid-platform-metadata/src/windows.rs
+++ b/talpid-platform-metadata/src/windows.rs
@@ -4,10 +4,13 @@ use std::{
mem::{self, MaybeUninit},
os::windows::ffi::OsStrExt,
};
-use windows_sys::Win32::System::{
- LibraryLoader::{GetModuleHandleW, GetProcAddress},
- SystemInformation::OSVERSIONINFOEXW,
- SystemServices::VER_NT_WORKSTATION,
+use windows_sys::Win32::{
+ Foundation::{NTSTATUS, STATUS_SUCCESS},
+ System::{
+ LibraryLoader::{GetModuleHandleW, GetProcAddress},
+ SystemInformation::OSVERSIONINFOEXW,
+ SystemServices::VER_NT_WORKSTATION,
+ },
};
#[allow(non_camel_case_types)]
@@ -49,27 +52,43 @@ impl WindowsVersion {
.chain(iter::once(0u16))
.collect();
+ // SAFETY: module_name is a valid UTF-16/WTF-16 null-terminated string.
let ntdll = unsafe { GetModuleHandleW(module_name.as_ptr()) };
if ntdll == 0 {
return Err(io::Error::last_os_error());
}
+ // SAFETY: ntdll is a valid pointer, RtlGetVersion is a valid null-terminated ANSI string.
let function_address = unsafe { GetProcAddress(ntdll, b"RtlGetVersion\0" as *const u8) }
.ok_or_else(io::Error::last_os_error)?;
- let rtl_get_version: extern "stdcall" fn(*mut RTL_OSVERSIONINFOEXW) =
- unsafe { *(&function_address as *const _ as *const _) };
+ // SAFETY: We're correcting this function pointer to the ACTUAL type of RtlGetVersion.
+ // https://learn.microsoft.com/en-us/windows/win32/devnotes/rtlgetversion
+ let rtl_get_version = unsafe {
+ mem::transmute::<
+ unsafe extern "system" fn() -> isize,
+ unsafe extern "stdcall" fn(*mut RTL_OSVERSIONINFOEXW) -> NTSTATUS,
+ >(function_address)
+ };
- let mut version_info: MaybeUninit<RTL_OSVERSIONINFOEXW> = mem::MaybeUninit::zeroed();
- unsafe {
- (*version_info.as_mut_ptr()).dwOSVersionInfoSize =
- mem::size_of_val(&version_info) as u32;
- rtl_get_version(version_info.as_mut_ptr());
+ let mut version_info: RTL_OSVERSIONINFOEXW =
+ // SAFETY: RTL_OSVERSIONINFOEXW is a C struct and can safely be zeroed.
+ unsafe { MaybeUninit::zeroed().assume_init() };
- Ok(WindowsVersion {
- inner: version_info.assume_init(),
- })
- }
+ version_info.dwOSVersionInfoSize = mem::size_of_val(&version_info) as u32;
+
+ // SAFETY:
+ // - &mut version_info is a valid pointer.
+ // - rtl_get_version was provided by GetProcAddress and should be valid.
+ let status = unsafe { rtl_get_version(&mut version_info) };
+ debug_assert_eq!(
+ status, STATUS_SUCCESS,
+ "RtlGetVersion always returns success"
+ );
+
+ Ok(WindowsVersion {
+ inner: version_info,
+ })
}
pub fn windows_version_string(&self) -> String {
diff --git a/talpid-routing/src/unix/macos/data.rs b/talpid-routing/src/unix/macos/data.rs
index 5ac83d0885..66644d6ce3 100644
--- a/talpid-routing/src/unix/macos/data.rs
+++ b/talpid-routing/src/unix/macos/data.rs
@@ -647,7 +647,12 @@ impl Interface {
actual_size: buffer.len(),
});
}
- let header: libc::if_msghdr = unsafe { std::ptr::read(buffer.as_ptr() as *const _) };
+ let header = buffer.as_ptr().cast::<libc::if_msghdr>();
+
+ // SAFETY:
+ // - `buffer` points to initialized memory of the correct size.
+ // - if_msghdr is a C struct, and valid for any bit pattern
+ let header: libc::if_msghdr = unsafe { header.read_unaligned() };
// let payload = buffer[INTERFACE_MESSAGE_HEADER_SIZE..header.ifm_msglen.into()].to_vec();
Ok(Self { header })
}
@@ -856,17 +861,17 @@ impl RouteSocketAddress {
// The "serialized" socket addresses must be padded to be aligned to 4 bytes, with
// the smallest size being 4 bytes.
- let buffer_size = len + len % 4;
+ let buffer_size = len.next_multiple_of(4);
let mut buffer = vec![0u8; buffer_size];
+ // SAFETY: copying contents of addr into buffer is safe, as long as addr.len()
+ // returns a correct size for the socket address pointer.
unsafe {
- // SAFETY: copying conents of addr into buffer is safe, as long as addr.len()
- // returns a correct size for the socket address pointer.
std::ptr::copy_nonoverlapping(
addr.as_ptr() as *const _,
buffer.as_mut_ptr(),
len,
- );
- }
+ )
+ };
buffer
}
}
diff --git a/talpid-routing/src/unix/macos/interface.rs b/talpid-routing/src/unix/macos/interface.rs
index 43e30d3549..7de3f84cc5 100644
--- a/talpid-routing/src/unix/macos/interface.rs
+++ b/talpid-routing/src/unix/macos/interface.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
use ipnetwork::IpNetwork;
use nix::{
diff --git a/talpid-routing/src/windows/get_best_default_route.rs b/talpid-routing/src/windows/get_best_default_route.rs
index 21dc6edd7b..798bc37285 100644
--- a/talpid-routing/src/windows/get_best_default_route.rs
+++ b/talpid-routing/src/windows/get_best_default_route.rs
@@ -153,10 +153,12 @@ struct AnnotatedRoute<'a> {
}
fn annotate_route(route: &MIB_IPFORWARD_ROW2) -> Option<AnnotatedRoute<'_>> {
- // SAFETY: `si_family` is valid in both `Ipv4` and `Ipv6` so we can safely access `si_family`.
let iface = get_ip_interface_entry(
- AddressFamily::try_from_af_family(unsafe { route.DestinationPrefix.Prefix.si_family })
- .ok()?,
+ AddressFamily::try_from_af_family(
+ // SAFETY: `si_family` is valid in both `Ipv4` and `Ipv6` so we can safely access `si_family`.
+ unsafe { route.DestinationPrefix.Prefix.si_family },
+ )
+ .ok()?,
&route.InterfaceLuid,
)
.ok()?;
diff --git a/talpid-routing/src/windows/route_manager.rs b/talpid-routing/src/windows/route_manager.rs
index 86ccff21fa..5cfc4b72e7 100644
--- a/talpid-routing/src/windows/route_manager.rs
+++ b/talpid-routing/src/windows/route_manager.rs
@@ -600,6 +600,7 @@ fn interface_luid_from_gateway(gateway: &SOCKADDR_INET) -> Result<NET_LUID_LH> {
unsafe fn get_first_gateway_address_reference(
adapter: &IP_ADAPTER_ADDRESSES_LH,
) -> &IP_ADAPTER_GATEWAY_ADDRESS_LH {
+ // SAFETY: See function docs
unsafe { &*adapter.FirstGatewayAddress }
}
@@ -611,6 +612,7 @@ fn adapter_interface_enabled(
// SAFETY: All fields in the Anonymous2 union are at represented by a u32 so dereferencing
// them is safe
AF_INET => Ok(0 != unsafe { adapter.Anonymous2.Flags } & IP_ADAPTER_IPV4_ENABLED),
+ // SAFETY: Same as above.
AF_INET6 => Ok(0 != unsafe { adapter.Anonymous2.Flags } & IP_ADAPTER_IPV6_ENABLED),
_ => Err(Error::InvalidSiFamily),
}
@@ -662,6 +664,7 @@ fn equal_address(lhs: &SOCKADDR_INET, rhs: &SOCKET_ADDRESS) -> Result<bool> {
return Ok(false);
}
+ // SAFETY: The si_family field is always valid
match unsafe { lhs.si_family } {
AF_INET => {
let typed_rhs = rhs.lpSockaddr as *mut SOCKADDR_IN;
diff --git a/talpid-time/Cargo.toml b/talpid-time/Cargo.toml
index de5c13ec60..48742abbf3 100644
--- a/talpid-time/Cargo.toml
+++ b/talpid-time/Cargo.toml
@@ -19,4 +19,4 @@ test = []
tokio = { workspace = true, features = ["time"] }
[target.'cfg(unix)'.dependencies]
-libc = "0.2"
+nix = { version = "0.29", features = ["time"] }
diff --git a/talpid-time/src/unix.rs b/talpid-time/src/unix.rs
index 5b247b2f5e..a2ea70b0af 100644
--- a/talpid-time/src/unix.rs
+++ b/talpid-time/src/unix.rs
@@ -1,17 +1,20 @@
-use libc::{c_long, clock_gettime, clockid_t, timespec};
-use std::{mem::MaybeUninit, time::Duration};
+use nix::{
+ sys::time::TimeSpec,
+ time::{clock_gettime, ClockId},
+};
+use std::{ffi::c_long, time::Duration};
const NSEC_PER_SEC: c_long = 1_000_000_000;
#[cfg(any(target_os = "macos", target_os = "ios"))]
-const CLOCK_ID: clockid_t = libc::CLOCK_MONOTONIC;
+const CLOCK_ID: ClockId = ClockId::CLOCK_MONOTONIC;
#[cfg(any(target_os = "linux", target_os = "android"))]
-const CLOCK_ID: clockid_t = libc::CLOCK_BOOTTIME;
+const CLOCK_ID: ClockId = ClockId::CLOCK_BOOTTIME;
#[derive(Clone, Copy)]
pub struct Instant {
- t: timespec,
+ t: TimeSpec,
}
impl Instant {
@@ -24,15 +27,15 @@ impl Instant {
// * `tv_sec >= 0`
// * `tv_nsec < NSEC_PER_SEC`
- let (tv_sec, tv_nsec) = if self.t.tv_nsec < earlier.t.tv_nsec {
+ let (tv_sec, tv_nsec) = if self.t.tv_nsec() < earlier.t.tv_nsec() {
(
- self.t.tv_sec - earlier.t.tv_sec - 1,
- NSEC_PER_SEC - earlier.t.tv_nsec + self.t.tv_nsec,
+ self.t.tv_sec() - earlier.t.tv_sec() - 1,
+ NSEC_PER_SEC - earlier.t.tv_nsec() + self.t.tv_nsec(),
)
} else {
(
- self.t.tv_sec - earlier.t.tv_sec,
- self.t.tv_nsec - earlier.t.tv_nsec,
+ self.t.tv_sec() - earlier.t.tv_sec(),
+ self.t.tv_nsec() - earlier.t.tv_nsec(),
)
};
@@ -49,10 +52,6 @@ impl Instant {
}
}
-fn now() -> timespec {
- let mut t = MaybeUninit::zeroed();
- unsafe {
- clock_gettime(CLOCK_ID, t.as_mut_ptr());
- t.assume_init()
- }
+fn now() -> TimeSpec {
+ clock_gettime(CLOCK_ID).expect("Clock ID is valid")
}
diff --git a/talpid-tunnel-config-client/src/socket.rs b/talpid-tunnel-config-client/src/socket.rs
index f7e48a6f8f..c7709ea9ba 100644
--- a/talpid-tunnel-config-client/src/socket.rs
+++ b/talpid-tunnel-config-client/src/socket.rs
@@ -16,7 +16,8 @@ mod sys {
use super::*;
pub use libc::{setsockopt, socklen_t, IPPROTO_TCP, TCP_MAXSEG};
- pub use std::os::fd::{AsRawFd, RawFd};
+ use std::ffi::c_int;
+ pub use std::os::fd::AsRawFd;
/// MTU to set on the tunnel config client socket. We want a low value to prevent fragmentation.
/// Especially on Android, we've found that the real MTU is often lower than the default MTU, and
@@ -33,7 +34,7 @@ mod sys {
impl TcpSocket {
pub fn new() -> io::Result<Self> {
let socket = StdTcpSocket::new_v4()?;
- try_set_tcp_sock_mtu(socket.as_raw_fd());
+ try_set_tcp_sock_mtu(&socket);
Ok(Self { socket })
}
@@ -42,13 +43,16 @@ mod sys {
}
}
- fn try_set_tcp_sock_mtu(sock: RawFd) {
- let mss = desired_mss();
+ fn try_set_tcp_sock_mtu(sock: &impl AsRawFd) {
+ let mss = c_int::from(desired_mss());
+
log::debug!("Tunnel config TCP socket MSS: {mss}");
+ // TODO: replace with nix when TcpMaxSeg is added for macos
+ // SAFETY: `mss` is a valid pointer to a c_int.
let result = unsafe {
setsockopt(
- sock,
+ sock.as_raw_fd(),
IPPROTO_TCP,
TCP_MAXSEG,
&mss as *const _ as _,
@@ -63,11 +67,11 @@ mod sys {
}
}
- const fn desired_mss() -> u32 {
+ const fn desired_mss() -> u16 {
const IPV4_HEADER_SIZE: u16 = 20;
const MAX_TCP_HEADER_SIZE: u16 = 60;
let mtu = CONFIG_CLIENT_MTU.saturating_sub(IPV4_HEADER_SIZE);
- mtu.saturating_sub(MAX_TCP_HEADER_SIZE) as u32
+ mtu.saturating_sub(MAX_TCP_HEADER_SIZE)
}
}
diff --git a/talpid-tunnel/src/tun_provider/android/mod.rs b/talpid-tunnel/src/tun_provider/android/mod.rs
index d4adc6ba36..7b4b1ae3c3 100644
--- a/talpid-tunnel/src/tun_provider/android/mod.rs
+++ b/talpid-tunnel/src/tun_provider/android/mod.rs
@@ -102,6 +102,8 @@ impl AndroidTunProvider {
pub fn open_tun(&mut self) -> Result<VpnServiceTun, Error> {
let config = VpnServiceConfig::new(self.config.clone());
+ // i have no idea why this is/isn't safe.
+ #[allow(clippy::undocumented_unsafe_blocks)]
let jvm = unsafe { JavaVM::from_raw(self.jvm.get_java_vm_pointer()) }
.map_err(Error::CloneJavaVm)?;
@@ -125,6 +127,9 @@ impl AndroidTunProvider {
/// Returns an open tunnel with the current configuration
pub fn open_tun_forced(&mut self) -> Result<VpnServiceTun, Error> {
let config = VpnServiceConfig::new(self.config.clone());
+
+ // i have no idea why this is/isn't safe.
+ #[allow(clippy::undocumented_unsafe_blocks)]
let jvm = unsafe { JavaVM::from_raw(self.jvm.get_java_vm_pointer()) }
.map_err(Error::CloneJavaVm)?;
diff --git a/talpid-windows/src/io.rs b/talpid-windows/src/io.rs
index 1bcffb30d3..8a8966f620 100644
--- a/talpid-windows/src/io.rs
+++ b/talpid-windows/src/io.rs
@@ -4,18 +4,22 @@ use windows_sys::Win32::System::IO::OVERLAPPED;
use crate::sync::Event;
/// Abstraction over `OVERLAPPED`.
+///
+/// - https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-overlapped
+/// - https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventw
pub struct Overlapped {
overlapped: OVERLAPPED,
event: Option<Event>,
}
+// SAFETY: Both OVERLAPPED and Event is used for async I/O, so this *should* be safe.
unsafe impl Send for Overlapped {}
-unsafe impl Sync for Overlapped {}
impl Overlapped {
/// Creates an `OVERLAPPED` object with `hEvent` set.
pub fn new(event: Option<Event>) -> io::Result<Self> {
let mut overlapped = Overlapped {
+ // SAFETY: OVERLAPPED is a C struct and can safely be zeroed.
overlapped: unsafe { mem::zeroed() },
event: None,
};
diff --git a/talpid-windows/src/net.rs b/talpid-windows/src/net.rs
index c744114a3a..33c79e1a9d 100644
--- a/talpid-windows/src/net.rs
+++ b/talpid-windows/src/net.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use socket2::SockAddr;
use std::{
ffi::{OsStr, OsString},
@@ -391,9 +393,7 @@ pub fn luid_from_alias<T: AsRef<OsStr>>(alias: T) -> io::Result<NET_LUID_LH> {
/// Returns the alias of an interface given its LUID.
pub fn alias_from_luid(luid: &NET_LUID_LH) -> io::Result<OsString> {
let mut buffer = [0u16; IF_MAX_STRING_SIZE as usize + 1];
- win32_err!(unsafe {
- ConvertInterfaceLuidToAlias(luid, &mut buffer[0] as *mut _, buffer.len())
- })?;
+ win32_err!(unsafe { ConvertInterfaceLuidToAlias(luid, buffer.as_mut_ptr(), buffer.len()) })?;
let nul = buffer.iter().position(|&c| c == 0u16).unwrap();
Ok(OsString::from_wide(&buffer[0..nul]))
}
@@ -426,6 +426,7 @@ pub fn ipaddr_from_in6addr(addr: IN6_ADDR) -> Ipv6Addr {
/// Converts a `SocketAddr` to `SOCKADDR_INET`
pub fn inet_sockaddr_from_socketaddr(addr: SocketAddr) -> SOCKADDR_INET {
+ // SAFETY: SOCKADDR_INET is a union of C structs, these can be safely zeroed.
let mut sockaddr: SOCKADDR_INET = unsafe { mem::zeroed() };
match addr {
// SAFETY: `*const sockaddr` may be treated as `*const sockaddr_in` since we know it's a v4
@@ -444,6 +445,7 @@ pub fn inet_sockaddr_from_socketaddr(addr: SocketAddr) -> SOCKADDR_INET {
/// Converts a `SOCKADDR_INET` to `SocketAddr`. Returns an error if the address family is invalid.
pub fn try_socketaddr_from_inet_sockaddr(addr: SOCKADDR_INET) -> Result<SocketAddr> {
+ // SAFETY: si_family is always valid
let family = unsafe { addr.si_family };
unsafe {
let mut storage: sockaddr_storage = mem::zeroed();
diff --git a/talpid-windows/src/process.rs b/talpid-windows/src/process.rs
index ecddbe09f2..fcf58c9654 100644
--- a/talpid-windows/src/process.rs
+++ b/talpid-windows/src/process.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare
+
use std::{
ffi::{c_char, CStr},
io, mem,
diff --git a/talpid-windows/src/sync.rs b/talpid-windows/src/sync.rs
index 7b4ed59be3..79a86d6941 100644
--- a/talpid-windows/src/sync.rs
+++ b/talpid-windows/src/sync.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use std::{io, ptr};
use windows_sys::Win32::{
Foundation::{CloseHandle, DuplicateHandle, BOOL, DUPLICATE_SAME_ACCESS, HANDLE},
diff --git a/talpid-wireguard/src/wireguard_go/mod.rs b/talpid-wireguard/src/wireguard_go/mod.rs
index 73b22a452f..6e082870f4 100644
--- a/talpid-wireguard/src/wireguard_go/mod.rs
+++ b/talpid-wireguard/src/wireguard_go/mod.rs
@@ -761,12 +761,16 @@ mod logging {
use std::ffi::c_char;
// Callback that receives messages from WireGuard
+ //
+ // # Safety
+ // - `msg` must be a valid pointer to a null-terminated UTF-8 string.
pub unsafe extern "system" fn wg_go_logging_callback(
level: WgLogLevel,
msg: *const c_char,
context: u64,
) {
let managed_msg = if !msg.is_null() {
+ // SAFETY: caller promises that the pointer is valid.
unsafe { std::ffi::CStr::from_ptr(msg).to_string_lossy().to_string() }
} else {
"Logging message from WireGuard is NULL".to_string()
diff --git a/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs b/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs
index ba3bca14be..46a394a59d 100644
--- a/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs
+++ b/talpid-wireguard/src/wireguard_kernel/nm_tunnel.rs
@@ -13,6 +13,7 @@ use talpid_dbus::{
WireguardTunnel,
},
};
+use talpid_net::unix::iface_index;
use talpid_tunnel_config_client::DaitaSettings;
#[derive(thiserror::Error, Debug)]
@@ -209,30 +210,3 @@ fn convert_config_to_dbus(config: &Config) -> DeviceConfig {
settings
}
-
-/// Converts an interface name into the corresponding index.
-#[cfg(target_os = "linux")]
-fn iface_index(name: &str) -> std::result::Result<libc::c_uint, IfaceIndexLookupError> {
- let c_name = std::ffi::CString::new(name)
- .map_err(|e| IfaceIndexLookupError::InvalidInterfaceName(name.to_owned(), e))?;
- let index = unsafe { libc::if_nametoindex(c_name.as_ptr()) };
- if index == 0 {
- Err(IfaceIndexLookupError::InterfaceLookupError(
- name.to_owned(),
- std::io::Error::last_os_error(),
- ))
- } else {
- Ok(index)
- }
-}
-
-/// Failure to lookup an interfaces index by its name.
-#[derive(Debug, thiserror::Error)]
-pub enum IfaceIndexLookupError {
- /// The interface name is invalid - contains null bytes or is too long.
- #[error("Invalid network interface name: {0}")]
- InvalidInterfaceName(String, #[source] std::ffi::NulError),
- /// Interface wasn't found by its name.
- #[error("Failed to get index for interface {0}")]
- InterfaceLookupError(String, #[source] std::io::Error),
-}
diff --git a/talpid-wireguard/src/wireguard_kernel/parsers.rs b/talpid-wireguard/src/wireguard_kernel/parsers.rs
index 86ecd025b5..a8c16d7d68 100644
--- a/talpid-wireguard/src/wireguard_kernel/parsers.rs
+++ b/talpid-wireguard/src/wireguard_kernel/parsers.rs
@@ -2,7 +2,7 @@ use byteorder::{ByteOrder, NativeEndian};
use nix::sys::{socket::InetAddr, time::TimeSpec};
use std::{
ffi::{CStr, CString},
- mem,
+ mem::{self, transmute},
net::IpAddr,
};
@@ -36,30 +36,41 @@ pub fn parse_wg_key(buffer: &[u8]) -> Result<[u8; 32], DecodeError> {
}
pub fn parse_inet_sockaddr(buffer: &[u8]) -> Result<InetAddr, DecodeError> {
- if buffer.len() != mem::size_of::<libc::sockaddr_in6>()
- && buffer.len() != mem::size_of::<libc::sockaddr_in>()
- {
- return Err(format!(
+ let wrong_len = || {
+ format!(
"Unexpected length for sockaddr_in: {}, expected {} or {}",
buffer.len(),
mem::size_of::<libc::sockaddr_in6>(),
mem::size_of::<libc::sockaddr_in>()
)
- .into());
- }
- let ptr = buffer.as_ptr();
+ };
+
const AF_INET: u16 = libc::AF_INET as u16;
const AF_INET6: u16 = libc::AF_INET6 as u16;
+ if buffer.len() < size_of::<u16>() {
+ return Err(wrong_len().into());
+ }
+
match NativeEndian::read_u16(buffer) {
- AF_INET => unsafe {
- let sockaddr: *const libc::sockaddr_in = ptr as *const _;
- Ok(InetAddr::V4(*sockaddr))
- },
- AF_INET6 => unsafe {
- let sockaddr: *const libc::sockaddr_in6 = ptr as *const _;
- Ok(InetAddr::V6(*sockaddr))
- },
+ AF_INET => {
+ let buffer: &[u8; size_of::<libc::sockaddr_in>()] =
+ buffer.try_into().map_err(|_| wrong_len())?;
+
+ // SAFETY: sockaddr_in has a defined repr(C) layout and is valid for all bit patterns
+ let sockaddr: libc::sockaddr_in = unsafe { transmute(*buffer) };
+
+ Ok(InetAddr::V4(sockaddr))
+ }
+ AF_INET6 => {
+ let buffer: &[u8; size_of::<libc::sockaddr_in6>()] =
+ buffer.try_into().map_err(|_| wrong_len())?;
+
+ // SAFETY: sockaddr_in6 has a defined repr(C) layout and is valid for all bit patterns
+ let sockaddr: libc::sockaddr_in6 = unsafe { transmute(*buffer) };
+
+ Ok(InetAddr::V6(sockaddr))
+ }
unexpected_addr_family => {
Err(format!("Unexpected address family: {unexpected_addr_family}").into())
}
diff --git a/talpid-wireguard/src/wireguard_kernel/wg_message.rs b/talpid-wireguard/src/wireguard_kernel/wg_message.rs
index 14ab31ee76..9c299b4a04 100644
--- a/talpid-wireguard/src/wireguard_kernel/wg_message.rs
+++ b/talpid-wireguard/src/wireguard_kernel/wg_message.rs
@@ -386,14 +386,14 @@ impl Nla for PeerNla {
Flags(value) | ProtocolVersion(value) => NativeEndian::write_u32(buffer, *value),
Endpoint(endpoint) => match &endpoint {
InetAddr::V4(sockaddr_in) => {
- // SAFETY: `sockaddr_in` has no padding bytes
buffer
+ // SAFETY: `sockaddr_in` has no padding bytes
.write_all(unsafe { struct_as_slice(sockaddr_in) })
.expect("Buffer too small for sockaddr_in");
}
InetAddr::V6(sockaddr_in6) => {
- // SAFETY: `sockaddr_in` has no padding bytes
buffer
+ // SAFETY: `sockaddr_in` has no padding bytes
.write_all(unsafe { struct_as_slice(sockaddr_in6) })
.expect("Buffer too small for sockaddr_in6");
}
@@ -403,8 +403,8 @@ impl Nla for PeerNla {
}
LastHandshakeTime(last_handshake) => {
let timespec: &libc::timespec = last_handshake.as_ref();
- // SAFETY: `timespec` has no padding bytes
buffer
+ // SAFETY: `timespec` has no padding bytes
.write_all(unsafe { struct_as_slice(timespec) })
.expect("Buffer too small for timespec");
}
diff --git a/talpid-wireguard/src/wireguard_nt/mod.rs b/talpid-wireguard/src/wireguard_nt/mod.rs
index 4451f36281..ff5b082490 100644
--- a/talpid-wireguard/src/wireguard_nt/mod.rs
+++ b/talpid-wireguard/src/wireguard_nt/mod.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
+
use super::{
config::Config,
logging,
diff --git a/test/Cargo.lock b/test/Cargo.lock
index 897e6c9645..e25c4d94d7 100644
--- a/test/Cargo.lock
+++ b/test/Cargo.lock
@@ -3478,7 +3478,7 @@ dependencies = [
name = "talpid-time"
version = "0.0.0"
dependencies = [
- "libc",
+ "nix 0.29.0",
"tokio",
]
diff --git a/windows-installer/src/main.rs b/windows-installer/src/main.rs
index 64e70c282d..62f80ee024 100644
--- a/windows-installer/src/main.rs
+++ b/windows-installer/src/main.rs
@@ -1,5 +1,4 @@
#![windows_subsystem = "windows"]
-#![warn(clippy::undocumented_unsafe_blocks)]
#[cfg(target_os = "windows")]
#[path = "windows.rs"]