Skip to content
8 changes: 8 additions & 0 deletions src/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ let joined = resolve_path::join_string_buf::<platform::Auto>(&mut buf, &[a, b]);
let rel = resolve_path::relative(from, to);
```

The threadlocal-buffer functions (`join`, `join_abs_string`, `normalize_string`,
`relative`, ...) accept input of any length. The `join*_buf`, `normalize*_buf`
and `relative*_buf` functions panic when the result does not fit the buffer;
when the input comes from the user (a package.json field, a command operand, an
archive entry), use the `*_checked` variant (`join_string_buf_checked`,
`join_z_buf_checked`, `join_abs_string_buf_checked`, `normalize_buf_checked`,
`relative_buf_z_checked`, ...) and report its `None` as `ENAMETOOLONG`.

Use the path-buffer pool to avoid 64 KB stack allocations on Windows
(`PathBuffer` is `[u8; PATH_MAX_BYTES]`, ~64 KB on Windows):

Expand Down
13 changes: 8 additions & 5 deletions src/install/TarballStream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -806,18 +806,21 @@ impl TarballStream {
let rest: &[OSPathChar] = tokenize_rest_after_first(&pathname[..]);

let mut norm_buf = OSPathBuffer::uninit();
if rest.len() >= norm_buf.len() {
// The last unit of `norm_buf` is reserved for the NUL.
let capacity = norm_buf.len() - 1;
let Some(norm_len) = resolve_path::normalize_buf_t::<OSPathChar, platform::Auto>(
rest,
&mut norm_buf[..capacity],
)
.map(|normalized| normalized.len()) else {
bun_core::warn!(
"Skipping entry with a path longer than the maximum path length: {}\n",
bun_core::fmt::fmt_os_path(rest, Default::default()),
);
self.phase = Phase::WantData;
self.out_fd = None;
return Ok(());
}
let normalized =
resolve_path::normalize_buf_t::<OSPathChar, platform::Auto>(rest, &mut norm_buf[..]);
let norm_len = normalized.len();
};
norm_buf[norm_len] = 0;
// SAFETY: norm_buf[norm_len] == 0 written above.
let path: OSPathZMut =
Expand Down
41 changes: 25 additions & 16 deletions src/libarchive/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1016,12 +1016,14 @@ pub fn is_symlink_target_safe(
written += link_target_bytes.len();

let mut norm_buf = bun_paths::path_buffer_pool::get();
let resolved = bun_paths::resolve_path::normalize_string_generic_t::<u8, true, false>(
let Some(resolved) = bun_paths::resolve_path::normalize_string_generic_t::<u8, true, false>(
&join_buf[..written],
&mut norm_buf[..],
b'/',
|c| c == b'/',
);
) else {
return false;
};

!(strings::eql(resolved, b"..") || strings::has_prefix_comptime(resolved, b"../"))
}
Expand Down Expand Up @@ -1304,14 +1306,19 @@ impl Archiver {

// pathname = sliceTo(remaining[..len :0], 0)
let pathname = slice_to_nul(remaining);
if pathname.is_empty() || pathname.len() >= normalized_buf.len() {
if pathname.is_empty() {
continue 'loop_;
}
let normalized = bun_paths::resolve_path::normalize_buf_t::<
u8,
bun_paths::platform::Auto,
>(pathname, &mut normalized_buf[..]);
let normalized_len = normalized.len();
// An entry too long to be a path cannot overwrite anything.
let Some(normalized_len) =
bun_paths::resolve_path::normalize_buf_t::<u8, bun_paths::platform::Auto>(
pathname,
&mut normalized_buf[..],
)
.map(|normalized| normalized.len())
else {
continue 'loop_;
};
let pathname: &[u8] = &normalized_buf[..normalized_len];
if pathname.is_empty() || pathname == b"." {
continue 'loop_;
Expand Down Expand Up @@ -1498,21 +1505,23 @@ impl Archiver {
// at its original `.len()`; therefore `remaining[remaining.len()] == 0`.
let pathname: &[OSPathChar] = remaining;

if pathname.len() >= normalized_buf.len() {
// The last unit of `normalized_buf` is reserved for the NUL.
let capacity = normalized_buf.len() - 1;
let Some(normalized_len) =
bun_paths::resolve_path::normalize_buf_t::<
OSPathChar,
bun_paths::platform::Auto,
>(pathname, &mut normalized_buf[..capacity])
.map(|normalized| normalized.len())
else {
if options.log {
bun_core::warn!(
"Skipping entry with a path longer than the maximum path length: {}\n",
Comment thread
claude[bot] marked this conversation as resolved.
Resolved
bun_core::fmt::fmt_os_path(pathname, Default::default()),
);
}
continue;
}

let normalized = bun_paths::resolve_path::normalize_buf_t::<
OSPathChar,
bun_paths::platform::Auto,
>(pathname, &mut normalized_buf[..]);
let normalized_len = normalized.len();
};
normalized_buf[normalized_len] = 0;
// SAFETY: we just wrote a NUL at normalized_buf[normalized_len]
let path: &mut [OSPathChar] = &mut normalized_buf[..normalized_len];
Expand Down
Loading
Loading