Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changes/change-pr-14340.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": "patch:perf"
---

perf: optimize CSP nonce generation
25 changes: 16 additions & 9 deletions crates/tauri/src/manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::{
borrow::Cow,
collections::HashMap,
fmt,
fmt::Write,
sync::{atomic::AtomicBool, Arc, Mutex, MutexGuard},
};

Expand Down Expand Up @@ -127,7 +128,7 @@ fn replace_csp_nonce(
directive: &str,
hashes: Vec<String>,
) {
let mut nonces = Vec::new();
let mut nonces = Vec::with_capacity(asset.matches(token).count());
*asset = replace_with_callback(asset, token, || {
#[cfg(target_pointer_width = "64")]
let mut raw = [0u8; 8];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lucasfernog do you remember why we used usize here at the first place (tracing back to cf54dcf)? It doesn't quite make sense to me

Copy link
Contributor

@vrmiguel vrmiguel Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I don't get the question, but the code is still using a usize there, just that it's using its underlying byte representation instead, right?

In any case, seems like this code can be simplified down to:

let mut raw = [0u8; std::mem::size_of::<usize>()];
getrandom::fill(&mut raw).expect("failed to get random bytes");
let nonce = usize::from_ne_bytes(raw);

Or even:

let mut raw = 0_usize.to_ne_bytes();
getrandom::fill(&mut raw).expect("failed to get random bytes");
let nonce = usize::from_ne_bytes(raw);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant it doesn't make sense to make nonce length depends on target pointer size

Expand All @@ -141,17 +142,23 @@ fn replace_csp_nonce(
nonce.to_string()
});

if !(nonces.is_empty() && hashes.is_empty()) {
let nonce_sources = nonces
.into_iter()
.map(|n| format!("'nonce-{n}'"))
.collect::<Vec<String>>();
if !nonces.is_empty() || !hashes.is_empty() {
let sources = csp.entry(directive.into()).or_default();
let self_source = "'self'".to_string();
if !sources.contains(&self_source) {
let self_source = "'self'";
if !sources.contains(self_source) {
sources.push(self_source);
}
sources.extend(nonce_sources);
#[cfg(target_pointer_width = "64")]
let mut buf = String::with_capacity(28);
#[cfg(target_pointer_width = "32")]
let mut buf = String::with_capacity(20);
#[cfg(target_pointer_width = "16")]
let mut buf = String::with_capacity(14);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these capacities meant to be how many bytes to alloc to store 'nonce-{0..usize::MAX}'? If so, isn't it a bit oversized for 32 and 16 ptr widths? Feels like it should be 18 and 13 respectively, unless I'm missing something

If we want to be a bit fancy-pants (😛) we could use this helper function:

const fn nonce_source_capacity() -> usize {
    // 'nonce-' prefix + digits of usize::max + closing quote
    8 + usize::MAX.ilog10() as usize + 1
}

(You can test that by replacing usize for u64, u32 and u16 respectively)

for nonce in nonces {
buf.clear();
write!(&mut buf, "'nonce-{}'", nonce).unwrap();
sources.push(buf.clone());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reusing buf doesn't seem helpful in this scenario, if we'll clone it anyway. It means that the code will allocate nonces.len()+1 times

for nonce in nonces {
      let mut buf = String::with_capacity(nonce_capacity());
      write!(&mut buf, "'nonce-{}'", nonce).unwrap();
      sources.push(buf);
}

Something like that ^ would allocate n times rather than n+1 times

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly doubt these optimizations will actually make any differences, format! Itself is quite optimized to estimate the buffer size already

sources.extend(hashes);
}
}
Expand Down
Loading