Skip to content
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

Warn on undocumented usage of unsafe #7639

Merged
merged 34 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0b844e2
Warn on undocumented `unsafe` blocks
hulthe Feb 10, 2025
376458c
Remove redundant warn(undocumented_unsafe_blocks) attrs
hulthe Feb 13, 2025
b908dc0
Replace libc::getuid with nix
hulthe Feb 10, 2025
9de617d
Replace libc::if_nametoindex with nix
hulthe Feb 10, 2025
78e4e44
Replace libc with nix in talpid-time::unix
hulthe Feb 10, 2025
d145fa8
Move some safety comments to clippy recognizes them
hulthe Feb 10, 2025
2bbc865
Add safety comments to talpid_net::unix
hulthe Feb 10, 2025
0b13ec3
Move iface_index helper to talpid-net
hulthe Feb 10, 2025
3ac5e02
Fix unaligned pointer read
hulthe Feb 11, 2025
4ec109b
Add safety comments to talpid_platform_metadata::windows
hulthe Feb 11, 2025
f7e0f10
Fix safety comment position in mullvad-leak-checker
hulthe Feb 11, 2025
281018b
Allow undocumented_unsafe_blocks in mullvad_paths::windows
hulthe Feb 11, 2025
fbf3222
Fix position of safety comment in talpid_routing::windows
hulthe Feb 11, 2025
f4a54e5
Add safety comment in talpid_openvpn
hulthe Feb 11, 2025
a0a2e1f
Add safety comments in talpid_routing::windows::route_manager
hulthe Feb 11, 2025
403391f
Add safety comments in talpid_windows::io
hulthe Feb 11, 2025
f972cb9
Add some safety comments in talpid_windows::net
hulthe Feb 11, 2025
7268ceb
Add safety comment to talpid_wireguard::wireguard_go
hulthe Feb 13, 2025
1f61549
Add safety comment to talpid_tunnel_config_client::socket
hulthe Feb 13, 2025
980b0a0
Fix safety comments in talpid_core::split_tunnel::macos::bpf
hulthe Feb 13, 2025
c7b8c7f
Remove unsafe block in talpid_core::split_tunnel::macos::tun
hulthe Feb 13, 2025
405a774
Fix safety comment position in talpid-routing::unix::macos::data
hulthe Feb 13, 2025
0574ffd
Fix math in talpid-routing::unix::macos::data (?)
hulthe Feb 13, 2025
1e88c29
Fix bad pointer deref in talpid-routing::unix::macos::data
hulthe Feb 13, 2025
417d1b5
Allow undocumented_unsafe_blocks in some modules
hulthe Feb 11, 2025
2e0038f
Update test/Cargo.lock
hulthe Feb 13, 2025
ae8756c
Add safety comment in talpid_routing::windows::route_manager
hulthe Feb 13, 2025
009cb72
Suppress unsafe warning when cloning JavaVM
hulthe Feb 13, 2025
ba26efb
Allow undocumented_unsafe_blocks in even more modules
hulthe Feb 13, 2025
0cce552
Refactor unsafe slightly in mullvad_daemon::migrations
hulthe Feb 13, 2025
b899e4b
Fix improper pointer provenance
hulthe Feb 13, 2025
16b44cd
Remove unused unsafe impl Send
hulthe Feb 13, 2025
927d805
Add safety comment to talpid_core::dns::windows::dnsapi
hulthe Feb 13, 2025
148e43d
Remove unnecessary impl Sync
hulthe Feb 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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};
Expand Down
2 changes: 2 additions & 0 deletions mullvad-api/src/ffi/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
1 change: 0 additions & 1 deletion mullvad-daemon/src/exception_logging/unix.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down
2 changes: 2 additions & 0 deletions mullvad-daemon/src/exception_logging/win.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::undocumented_unsafe_blocks)]

use mullvad_paths::log_dir;
use std::{
borrow::Cow,
Expand Down
2 changes: 2 additions & 0 deletions mullvad-daemon/src/macos_launch_daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 1 addition & 2 deletions mullvad-daemon/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
44 changes: 32 additions & 12 deletions mullvad-daemon/src/migrations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
};

Expand Down Expand Up @@ -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 {
Expand All @@ -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(),
Expand All @@ -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 }
}
}

Expand Down
2 changes: 2 additions & 0 deletions mullvad-daemon/src/system_service.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down
2 changes: 2 additions & 0 deletions mullvad-ios/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#![cfg(target_os = "ios")]
#![allow(clippy::undocumented_unsafe_blocks)]

mod api_client;
mod encrypted_dns_proxy;
mod ephemeral_peer_proxy;
Expand Down
9 changes: 7 additions & 2 deletions mullvad-leak-checker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
3 changes: 3 additions & 0 deletions mullvad-paths/src/windows.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions talpid-core/src/dns/macos.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.

use parking_lot::Mutex;
use std::{
collections::{BTreeSet, HashMap},
Expand Down
1 change: 1 addition & 0 deletions talpid-core/src/dns/windows/dnsapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions talpid-core/src/dns/windows/iphlpapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions talpid-core/src/dns/windows/netsh.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.

use crate::dns::{DnsMonitorT, ResolvedDnsConfig};
use std::{
ffi::OsString,
Expand Down
8 changes: 6 additions & 2 deletions talpid-core/src/dns/windows/tcpip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions talpid-core/src/firewall/windows/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down
29 changes: 9 additions & 20 deletions talpid-core/src/linux/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
}
2 changes: 0 additions & 2 deletions talpid-core/src/offline/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down
11 changes: 6 additions & 5 deletions talpid-core/src/split_tunnel/macos/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 9 additions & 3 deletions talpid-core/src/split_tunnel/macos/tun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand Down Expand Up @@ -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;
}

Expand Down
2 changes: 2 additions & 0 deletions talpid-core/src/split_tunnel/windows/driver.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
2 changes: 2 additions & 0 deletions talpid-core/src/split_tunnel/windows/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.

mod driver;
mod path_monitor;
mod service;
Expand Down
1 change: 1 addition & 0 deletions talpid-core/src/window.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
2 changes: 2 additions & 0 deletions talpid-net/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Loading
Loading