Skip to content

Commit 60ccaee

Browse files
committed
Merge branch 'document-and-lint-all-usages-of-unsafe-des-1459'
2 parents c06b29d + 148e43d commit 60ccaee

File tree

50 files changed

+270
-156
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+270
-156
lines changed

Cargo.lock

+3-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ single_use_lifetimes = "warn"
7474

7575
[workspace.lints.clippy]
7676
unused_async = "deny"
77+
undocumented_unsafe_blocks = "warn"
7778

7879
[workspace.dependencies]
7980
hickory-proto = "0.24.3"

desktop/packages/nseventforwarder/nseventforwarder-rs/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! Forward [NSEvent]s from macOS to node.
22
#![cfg(target_os = "macos")]
3-
#![warn(clippy::undocumented_unsafe_blocks)]
43

54
use std::ptr::NonNull;
65
use std::sync::{mpsc, Arc, Mutex};

mullvad-api/src/ffi/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#![cfg(not(target_os = "android"))]
2+
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
3+
24
use std::{
35
ffi::{CStr, CString},
46
net::SocketAddr,

mullvad-daemon/src/exception_logging/unix.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//! Install signal handlers to catch critical program faults and log them. See [`enable`].
2-
#![warn(clippy::undocumented_unsafe_blocks)]
32
43
use libc::siginfo_t;
54
use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal};

mullvad-daemon/src/exception_logging/win.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::undocumented_unsafe_blocks)]
2+
13
use mullvad_paths::log_dir;
24
use std::{
35
borrow::Cow,

mullvad-daemon/src/macos_launch_daemon.rs

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
//! must be checked so that the user can be directed to approve the launch
66
//! daemon in the system settings.
77
8+
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
9+
810
use libc::c_longlong;
911
use objc2::{class, msg_send, runtime::AnyObject, Encode, Encoding, RefEncode};
1012
use std::ffi::CStr;

mullvad-daemon/src/main.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,7 @@ async fn create_daemon(log_dir: Option<PathBuf>) -> Result<Daemon, String> {
229229

230230
#[cfg(unix)]
231231
fn running_as_admin() -> bool {
232-
let uid = unsafe { libc::getuid() };
233-
uid == 0
232+
nix::unistd::Uid::current().is_root()
234233
}
235234

236235
#[cfg(windows)]

mullvad-daemon/src/migrations/mod.rs

+32-12
Original file line numberDiff line numberDiff line change
@@ -227,15 +227,22 @@ pub(crate) fn migrate_device(
227227

228228
#[cfg(windows)]
229229
mod windows {
230-
use std::{ffi::OsStr, io, os::windows::ffi::OsStrExt, path::Path, ptr};
230+
use std::{
231+
ffi::OsStr,
232+
io,
233+
os::windows::ffi::OsStrExt,
234+
path::Path,
235+
ptr::{self, NonNull},
236+
};
231237
use talpid_types::ErrorExt;
232238
use tokio::fs;
233239
use windows_sys::Win32::{
234-
Foundation::{LocalFree, ERROR_SUCCESS, HLOCAL, PSID},
240+
Foundation::{LocalFree, ERROR_SUCCESS, PSID},
235241
Security::{
236242
Authorization::{GetNamedSecurityInfoW, SE_FILE_OBJECT, SE_OBJECT_TYPE},
237243
IsWellKnownSid, WinBuiltinAdministratorsSid, WinLocalSystemSid,
238-
OWNER_SECURITY_INFORMATION, SECURITY_DESCRIPTOR, SID, WELL_KNOWN_SID_TYPE,
244+
OWNER_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, SECURITY_DESCRIPTOR, SID,
245+
WELL_KNOWN_SID_TYPE,
239246
},
240247
};
241248

@@ -355,8 +362,8 @@ mod windows {
355362
}
356363

357364
struct SecurityInformation {
358-
security_descriptor: *mut SECURITY_DESCRIPTOR,
359-
owner: PSID,
365+
security_descriptor: NonNull<SECURITY_DESCRIPTOR>,
366+
owner: Option<NonNull<SID>>,
360367
}
361368

362369
impl SecurityInformation {
@@ -375,9 +382,12 @@ mod windows {
375382
let mut u16_path: Vec<u16> = object_name.as_ref().encode_wide().collect();
376383
u16_path.push(0u16);
377384

378-
let mut security_descriptor = ptr::null_mut();
379-
let mut owner = ptr::null_mut();
385+
let mut security_descriptor: PSECURITY_DESCRIPTOR = ptr::null_mut();
386+
let mut owner: PSID = ptr::null_mut();
380387

388+
// SAFETY:
389+
// - u16_path is a null-terminated UTF-16 string
390+
// - The *mut pointers are allowed to be null
381391
let status = unsafe {
382392
GetNamedSecurityInfoW(
383393
u16_path.as_ptr(),
@@ -395,25 +405,35 @@ mod windows {
395405
return Err(std::io::Error::from_raw_os_error(status as i32));
396406
}
397407

408+
let Some(security_descriptor) = NonNull::new(security_descriptor) else {
409+
return Err(std::io::Error::other("GetNamedSecurityInfoW returned null"));
410+
};
411+
398412
Ok(SecurityInformation {
399-
security_descriptor: security_descriptor as *mut _,
400-
owner,
413+
security_descriptor: security_descriptor.cast::<SECURITY_DESCRIPTOR>(),
414+
owner: NonNull::new(owner.cast::<SID>()),
401415
})
402416
}
403417

404418
pub fn owner(&self) -> Option<&SID> {
405-
unsafe { (self.owner as *const SID).as_ref() }
419+
// SAFETY: GetNamedSecurityInfoW promises that this pointer was valid,
420+
// and it should stay valid until we deallocate self.security_descriptor.
421+
self.owner.map(|ptr| unsafe { ptr.as_ref() })
406422
}
407423
}
408424

409425
impl Drop for SecurityInformation {
410426
fn drop(&mut self) {
411-
unsafe { LocalFree(self.security_descriptor as HLOCAL) };
427+
// SAFETY: GetNamedSecurityInfoW promises that this pointer was valid,
428+
// and we do not deallocate it before this point. Since we have &mut self,
429+
// we know that no one else has a reference to security_descriptor.
430+
unsafe { LocalFree(self.security_descriptor.as_ptr() as PSECURITY_DESCRIPTOR) };
412431
}
413432
}
414433

415434
fn is_well_known_sid(sid: &SID, well_known_sid_type: WELL_KNOWN_SID_TYPE) -> bool {
416-
unsafe { IsWellKnownSid(sid as *const SID as *mut _, well_known_sid_type) == 1 }
435+
// SAFETY: this function doesn't take ownership of sid, and is trivially safe to call.
436+
unsafe { IsWellKnownSid(sid as *const SID as PSID, well_known_sid_type) == 1 }
417437
}
418438
}
419439

mullvad-daemon/src/system_service.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
2+
13
use crate::cli;
24
use mullvad_daemon::{runtime::new_multi_thread, DaemonShutdownHandle};
35
use std::{

mullvad-ios/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#![cfg(target_os = "ios")]
2+
#![allow(clippy::undocumented_unsafe_blocks)]
3+
24
mod api_client;
35
mod encrypted_dns_proxy;
46
mod ephemeral_peer_proxy;

mullvad-leak-checker/src/lib.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,14 @@ impl fmt::Debug for Interface {
4747
match self {
4848
Self::Name(arg0) => f.debug_tuple("Name").field(arg0).finish(),
4949

50-
// SAFETY: u64 is valid for all bit patterns, so reading the union as a u64 is safe.
5150
#[cfg(target_os = "windows")]
52-
Self::Luid(arg0) => f.debug_tuple("Luid").field(&unsafe { arg0.Value }).finish(),
51+
Self::Luid(arg0) => f
52+
.debug_tuple("Luid")
53+
.field(
54+
// SAFETY: u64 is valid for all bit patterns, so reading the union as a u64 is safe.
55+
&unsafe { arg0.Value },
56+
)
57+
.finish(),
5358

5459
#[cfg(target_os = "macos")]
5560
Self::Index(arg0) => f.debug_tuple("Luid").field(arg0).finish(),

mullvad-paths/src/windows.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
2+
13
use crate::{Error, Result};
24
use once_cell::sync::OnceCell;
35
use std::{
@@ -362,6 +364,7 @@ fn adjust_token_privilege(
362364
privilege: &WideCStr,
363365
enable: bool,
364366
) -> std::io::Result<()> {
367+
// SAFETY: LUID is a C struct and can safely be zeroed.
365368
let mut privilege_luid: LUID = unsafe { mem::zeroed() };
366369

367370
if unsafe { LookupPrivilegeValueW(ptr::null(), privilege.as_ptr(), &mut privilege_luid) } == 0 {

talpid-core/src/dns/macos.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
2+
13
use parking_lot::Mutex;
24
use std::{
35
collections::{BTreeSet, HashMap},

talpid-core/src/dns/windows/dnsapi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ impl DnsApi {
6262
std::thread::spawn(move || {
6363
let begin = Instant::now();
6464

65+
// SAFETY: this function is trivially safe to call
6566
let result = if unsafe { (DnsFlushResolverCache)() } != 0 {
6667
let elapsed = begin.elapsed();
6768
if elapsed >= FLUSH_TIMEOUT {

talpid-core/src/dns/windows/iphlpapi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! <https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-setinterfacednssettings>,
33
//! it requires at least Windows 10, build 19041. For that reason, use run-time linking and fall
44
//! back on other methods if it is not available.
5+
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
56

67
use crate::dns::{DnsMonitorT, ResolvedDnsConfig};
78
use once_cell::sync::OnceCell;

talpid-core/src/dns/windows/netsh.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
2+
13
use crate::dns::{DnsMonitorT, ResolvedDnsConfig};
24
use std::{
35
ffi::OsString,

talpid-core/src/dns/windows/tcpip.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,12 @@ fn flush_dns_cache() -> Result<(), Error> {
164164
/// Obtain a string representation for a GUID object.
165165
fn string_from_guid(guid: &GUID) -> String {
166166
let mut buffer = [0u16; 40];
167-
let length = unsafe { StringFromGUID2(guid, &mut buffer[0] as *mut _, buffer.len() as i32 - 1) }
168-
as usize;
167+
168+
let length =
169+
// SAFETY: `guid` and `buffer` are valid references.
170+
// StringFromGUID2 won't write past the end of the provided length.
171+
unsafe { StringFromGUID2(guid, buffer.as_mut_ptr(), buffer.len() as i32 - 1) } as usize;
172+
169173
// cannot fail because `buffer` is large enough
170174
assert!(length > 0);
171175
let length = length - 1;

talpid-core/src/firewall/windows/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
2+
13
use crate::{dns::ResolvedDnsConfig, tunnel::TunnelMetadata};
24

35
use std::{ffi::CStr, io, net::IpAddr, ptr, sync::LazyLock};

talpid-core/src/linux/mod.rs

+9-20
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,16 @@
1-
use std::{
2-
ffi::{self, CString},
3-
io,
4-
};
1+
use nix::{errno::Errno, net::if_::if_nametoindex};
52

63
/// Converts an interface name into the corresponding index.
74
pub fn iface_index(name: &str) -> Result<libc::c_uint, IfaceIndexLookupError> {
8-
let c_name = CString::new(name)
9-
.map_err(|e| IfaceIndexLookupError::InvalidInterfaceName(name.to_owned(), e))?;
10-
let index = unsafe { libc::if_nametoindex(c_name.as_ptr()) };
11-
if index == 0 {
12-
Err(IfaceIndexLookupError::InterfaceLookupError(
13-
name.to_owned(),
14-
io::Error::last_os_error(),
15-
))
16-
} else {
17-
Ok(index)
18-
}
5+
if_nametoindex(name).map_err(|error| IfaceIndexLookupError {
6+
interface_name: name.to_owned(),
7+
error,
8+
})
199
}
2010

2111
#[derive(Debug, thiserror::Error)]
22-
pub enum IfaceIndexLookupError {
23-
#[error("Invalid network interface name: {0}")]
24-
InvalidInterfaceName(String, #[source] ffi::NulError),
25-
#[error("Failed to get index for interface {0}")]
26-
InterfaceLookupError(String, #[source] io::Error),
12+
#[error("Failed to get index for interface {interface_name}: {error}")]
13+
pub struct IfaceIndexLookupError {
14+
pub interface_name: String,
15+
pub error: Errno,
2716
}

talpid-core/src/offline/windows.rs

-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ pub struct BroadcastListener {
2424
_notify_tx: Arc<UnboundedSender<Connectivity>>,
2525
}
2626

27-
unsafe impl Send for BroadcastListener {}
28-
2927
impl BroadcastListener {
3028
pub async fn start(
3129
notify_tx: UnboundedSender<Connectivity>,

talpid-core/src/split_tunnel/macos/bpf.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -132,16 +132,17 @@ impl Bpf {
132132
return Err(Error::InterfaceNameTooLong);
133133
}
134134

135+
// SAFETY: `name_bytes` cannot exceed the size of `ifr_name`
135136
unsafe {
136-
// SAFETY: `name_bytes` cannot exceed the size of `ifr_name`
137137
std::ptr::copy_nonoverlapping(
138138
name_bytes.as_ptr(),
139139
&mut ifr.ifr_name as *mut _ as *mut _,
140140
name_bytes.len(),
141-
);
142-
// SAFETY: The fd is valid for the lifetime of `self`, and `ifr` has a valid interface
143-
ioctl!(self.file.as_raw_fd(), BIOCSETIF, &ifr)
144-
}
141+
)
142+
};
143+
144+
// SAFETY: The fd is valid for the lifetime of `self`, and `ifr` has a valid interface
145+
unsafe { ioctl!(self.file.as_raw_fd(), BIOCSETIF, &ifr) }
145146
}
146147

147148
/// Enable or disable immediate mode (BIOCIMMEDIATE)

talpid-core/src/split_tunnel/macos/tun.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use pnet_packet::{
2020
MutablePacket, Packet,
2121
};
2222
use std::{
23-
ffi::{c_uint, CStr},
23+
ffi::c_uint,
2424
io::{self, IoSlice, Write},
2525
net::{IpAddr, Ipv4Addr, Ipv6Addr},
2626
};
@@ -887,8 +887,14 @@ impl PacketCodec for PktapCodec {
887887
return None;
888888
}
889889

890-
let iface = unsafe { CStr::from_ptr(header.pth_ifname.as_ptr() as *const _) };
891-
if iface.to_bytes() != self.interface.as_bytes() {
890+
// cast the array from [i8] to [u8] to enable comparison with String::as_bytes
891+
let iface = header.pth_ifname.map(|b| b as u8);
892+
// get the interface name by splitting on the first null byte (if any)
893+
let iface = iface
894+
.split(|&b| b == 0)
895+
.next()
896+
.expect("split will yield at least one element");
897+
if iface != self.interface.as_bytes() {
892898
return None;
893899
}
894900

talpid-core/src/split_tunnel/windows/driver.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
2+
13
use super::windows::{
24
get_device_path, get_process_creation_time, get_process_device_path, open_process,
35
ProcessAccess,

talpid-core/src/split_tunnel/windows/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
2+
13
mod driver;
24
mod path_monitor;
35
mod service;

talpid-core/src/window.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! Utilities for working with windows on Windows.
2+
#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare.
23

34
use std::{os::windows::io::AsRawHandle, ptr, sync::Arc, thread};
45
use tokio::sync::broadcast;

talpid-net/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ libc = "0.2"
1515
talpid-types = { path = "../talpid-types" }
1616
socket2 = { workspace = true, features = ["all"] }
1717
log = { workspace = true }
18+
thiserror = { workspace = true }
19+
nix = { version = "0.29", features = ["net"] }

0 commit comments

Comments
 (0)