Skip to content
Open
416 changes: 187 additions & 229 deletions src/exe_format/elf.rs

Large diffs are not rendered by default.

84 changes: 32 additions & 52 deletions src/parsers/json5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use bun_core::StackCheck;
// `is_identifier_start/_part` landed in `bun_core::lexer`; route through there.
use bun_alloc::{ArenaVec as BumpVec, ArenaVecExt as _};
use bun_ast::{E, Expr, G};
use bun_ast::{Loc, Log, Source};
use bun_ast::{Loc, Log, Source, usize2loc};
use bun_core::lexer as identifier;
use bun_core::strings;

Expand Down Expand Up @@ -92,6 +92,7 @@ pub enum ParseError {
ExpectedClosingBracket,
InvalidIdentifier,
TrailingData,
DocumentTooLarge,
StackOverflow,
}

Expand Down Expand Up @@ -123,6 +124,7 @@ pub enum Error {
ExpectedClosingBracket { pos: usize },
InvalidIdentifier { pos: usize },
TrailingData { pos: usize },
DocumentTooLarge,
}

#[derive(Copy, Clone, PartialEq, Eq, strum::IntoStaticStr, Debug)]
Expand Down Expand Up @@ -165,9 +167,8 @@ impl Error {
| Error::ExpectedClosingBrace { pos }
| Error::ExpectedClosingBracket { pos }
| Error::InvalidIdentifier { pos }
| Error::TrailingData { pos } => Loc {
start: i32::try_from(pos).expect("int cast"),
},
| Error::TrailingData { pos } => usize2loc(pos),
Error::DocumentTooLarge => Loc { start: 0 },
};
let msg: &'static [u8] = match *self {
Error::Oom | Error::StackOverflow => unreachable!(),
Expand All @@ -191,6 +192,7 @@ impl Error {
Error::ExpectedClosingBracket { .. } => b"Expected ']'",
Error::InvalidIdentifier { .. } => b"Invalid identifier start character",
Error::TrailingData { .. } => b"Unexpected token after JSON5 value",
Error::DocumentTooLarge => b"JSON5 document is too large to parse (2 GiB maximum)",
};
log.add_error(Some(source), loc, msg);
Ok(())
Expand Down Expand Up @@ -222,6 +224,7 @@ impl<'a> JSON5Parser<'a> {
match err {
ParseError::OutOfMemory => Error::Oom,
ParseError::StackOverflow => Error::StackOverflow,
ParseError::DocumentTooLarge => Error::DocumentTooLarge,
// Scanner errors use scan position
ParseError::UnexpectedCharacter => Error::UnexpectedCharacter { pos: scan_pos },
ParseError::UnterminatedString => Error::UnterminatedString { pos: scan_pos },
Expand Down Expand Up @@ -288,13 +291,16 @@ impl<'a> JSON5Parser<'a> {
0
}

/// `parse_root` has rejected any document whose positions do not fit a `Loc`.
fn start_token(&mut self) {
self.token.loc = usize2loc(self.pos);
}

fn scan(&mut self) -> Result<(), ParseError> {
self.token.data = 'next: loop {
match self.peek() {
0 => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next TokenData::Eof;
}
// Whitespace — skip without setting loc
Expand All @@ -304,73 +310,53 @@ impl<'a> JSON5Parser<'a> {
}
// Structural
b'{' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::LeftBrace;
}
b'}' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::RightBrace;
}
b'[' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::LeftBracket;
}
b']' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::RightBracket;
}
b':' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::Colon;
}
b',' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::Comma;
}
b'+' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::Number(self.scan_signed_value(false)?);
}
b'-' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
self.pos += 1;
break 'next TokenData::Number(self.scan_signed_value(true)?);
}
// Strings
b'"' | b'\'' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next TokenData::String(self.scan_string()?);
}
// Numbers
b'0'..=b'9' | b'.' => {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next TokenData::Number(self.scan_number()?);
}
// Comments — skip without setting loc
Expand All @@ -393,27 +379,21 @@ impl<'a> JSON5Parser<'a> {
}
c => {
if c == b't' {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next if self.scan_keyword(b"true") {
TokenData::Boolean(true)
} else {
TokenData::Identifier(self.scan_identifier()?)
};
} else if c == b'f' {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next if self.scan_keyword(b"false") {
TokenData::Boolean(false)
} else {
TokenData::Identifier(self.scan_identifier()?)
};
} else if c == b'n' {
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next if self.scan_keyword(b"null") {
TokenData::Null
} else {
Expand All @@ -425,9 +405,7 @@ impl<'a> JSON5Parser<'a> {
|| c == b'$'
|| c == b'\\'
{
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
break 'next TokenData::Identifier(self.scan_identifier()?);
} else if c >= 0x80 {
// Multi-byte: check whitespace first, then identifier
Expand All @@ -436,9 +414,7 @@ impl<'a> JSON5Parser<'a> {
self.pos += usize::from(mb);
continue 'next;
}
self.token.loc = Loc {
start: i32::try_from(self.pos).expect("int cast"),
};
self.start_token();
let Some(cp) = self.read_codepoint() else {
return Err(ParseError::UnexpectedCharacter);
};
Expand Down Expand Up @@ -505,6 +481,10 @@ impl<'a> JSON5Parser<'a> {
// ── Parser ──

fn parse_root(&mut self) -> Result<Expr, ParseError> {
// Positions are `i32` `Loc`s, including the EOF token's at `source.len()`.
if self.source.len() > i32::MAX as usize {
return Err(ParseError::DocumentTooLarge);
}
self.scan()?;
let result = self.parse_value()?;
if !matches!(self.token.data, TokenData::Eof) {
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/image/Image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1945,7 +1945,7 @@ impl PipelineTask {
fn apply_pipeline(&self, d: &mut codecs::Decoded) -> Result<(), codecs::Error> {
let p = &self.pipeline;
if p.rotate != 0 {
let next = codecs::rotate(&d.rgba, d.width, d.height, u32::from(p.rotate))?;
let next = codecs::rotate(&d.rgba, d.width, d.height, p.rotate)?;
// Assignment drops
// the old `Vec<u8>`/owned buffer.
d.rgba = next.rgba;
Expand Down Expand Up @@ -2071,7 +2071,7 @@ fn apply_orientation(
if t.rotate != 0 {
// Swap pixel slots only — `next` carries no ICC profile, and the
// one on `d` (set by decode) must survive EXIF auto-orient.
let next = codecs::rotate(&d.rgba, d.width, d.height, u32::from(t.rotate))?;
let next = codecs::rotate(&d.rgba, d.width, d.height, t.rotate)?;
d.rgba = next.rgba;
d.width = next.width;
d.height = next.height;
Expand Down
80 changes: 38 additions & 42 deletions src/runtime/image/codecs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,15 @@ pub(crate) fn guard(w: u32, h: u32, max_pixels: u64) -> Result<(), Error> {
Ok(())
}

/// A decoder-reported dimension; anything not strictly positive is a corrupt header.
#[inline]
fn positive_dimension(v: c_int) -> Result<u32, Error> {
match u32::try_from(v) {
Ok(d) if d != 0 => Ok(d),
_ => Err(Error::DecodeFailed),
}
}

pub(crate) struct Probe {
pub format: Format,
pub width: u32,
Expand Down Expand Up @@ -367,25 +376,20 @@ pub(crate) fn probe(bytes: &[u8], max_pixels: u64) -> Result<Probe, Error> {
let rw = unsafe { jpeg::tj3Get(handle.as_ptr(), jpeg::TJPARAM_JPEGWIDTH) };
// SAFETY: same handle invariant as above.
let rh = unsafe { jpeg::tj3Get(handle.as_ptr(), jpeg::TJPARAM_JPEGHEIGHT) };
if rw <= 0 || rh <= 0 {
return Err(Error::DecodeFailed);
}
w = u32::try_from(rw).expect("int cast");
h = u32::try_from(rh).expect("int cast");
w = positive_dimension(rw)?;
h = positive_dimension(rh)?;
}
Format::Webp => {
let mut cw: c_int = 0;
let mut ch: c_int = 0;
// SAFETY: (ptr,len) from a valid live slice; cw/ch are valid `*mut c_int` out-params.
if unsafe { webp::WebPGetInfo(bytes.as_ptr(), bytes.len(), &raw mut cw, &raw mut ch) }
== 0
|| cw <= 0
|| ch <= 0
{
return Err(Error::DecodeFailed);
}
w = u32::try_from(cw).expect("int cast");
h = u32::try_from(ch).expect("int cast");
w = positive_dimension(cw)?;
h = positive_dimension(ch)?;
}
Format::Bmp => {
let ih = bmp::parse_header(bytes)?;
Expand Down Expand Up @@ -655,6 +659,12 @@ pub(crate) fn modulate(rgba: &mut [u8], brightness: f32, saturation: f32) {
unsafe { bun_image_modulate_rgba8(rgba.as_mut_ptr(), rgba.len(), brightness, saturation) }
}

/// Backstop for the kernels' `i32` dimensions; decoders and `do_resize` stay far below.
#[inline]
fn kernel_dimension(v: u32) -> Result<i32, Error> {
i32::try_from(v).map_err(|_| Error::TooManyPixels)
}

pub(crate) fn resize(
src: &[u8],
sw: u32,
Expand All @@ -673,31 +683,25 @@ pub(crate) fn resize(
Err(e) => return Err(e),
}
}
let (src_w, src_h) = (kernel_dimension(sw)?, kernel_dimension(sh)?);
let (dst_w, dst_h) = (kernel_dimension(dw)?, kernel_dimension(dh)?);
// ONE allocation for output + the kernel's scratch arena (intermediate
// dst_w×src_h×4 row buffer + spans/weights tables). Zero mallocs in the
// C++; mimalloc here is faster than libc, and the over-allocation rounds
// into the same size class as the row buffer alone.
let out_sz: usize = (dw as usize) * (dh as usize) * 4;
// SAFETY: pure FFI query; all args are by-value ints, no pointers.
let scratch_sz = unsafe {
bun_image_resize_scratch_size(
i32::try_from(sw).expect("int cast"),
i32::try_from(sh).expect("int cast"),
i32::try_from(dw).expect("int cast"),
i32::try_from(dh).expect("int cast"),
f as i32,
)
};
let scratch_sz = unsafe { bun_image_resize_scratch_size(src_w, src_h, dst_w, dst_h, f as i32) };
let mut block: Vec<u8> = vec![0u8; out_sz + scratch_sz];
// SAFETY: block has out_sz + scratch_sz bytes; dst at [0..out_sz), scratch at [out_sz..).
let rc = unsafe {
bun_image_resize_rgba8(
src.as_ptr(),
i32::try_from(sw).expect("int cast"),
i32::try_from(sh).expect("int cast"),
src_w,
src_h,
block.as_mut_ptr(),
i32::try_from(dw).expect("int cast"),
i32::try_from(dh).expect("int cast"),
dst_w,
dst_h,
f as i32,
block.as_mut_ptr().add(out_sz),
)
Expand All @@ -713,15 +717,21 @@ pub(crate) fn resize(
Ok(block)
}

pub(crate) fn rotate(src: &[u8], w: u32, h: u32, degrees: u32) -> Result<Decoded, Error> {
/// `degrees` is 90, 180 or 270 (callers validate it).
pub(crate) fn rotate(src: &[u8], w: u32, h: u32, degrees: u16) -> Result<Decoded, Error> {
let (dw, dh): (u32, u32) = if degrees == 90 || degrees == 270 {
(h, w)
} else {
(w, h)
};
#[cfg(target_os = "macos")]
if use_system() {
match system_backend::BackendError::split(system_backend::rotate(src, w, h, degrees / 90)) {
match system_backend::BackendError::split(system_backend::rotate(
src,
w,
h,
u32::from(degrees / 90),
)) {
Ok(Some(out)) => {
return Ok(Decoded {
rgba: out,
Expand All @@ -734,17 +744,10 @@ pub(crate) fn rotate(src: &[u8], w: u32, h: u32, degrees: u32) -> Result<Decoded
Err(e) => return Err(e),
}
}
let (kw, kh) = (kernel_dimension(w)?, kernel_dimension(h)?);
let mut out: Vec<u8> = vec![0u8; (dw as usize) * (dh as usize) * 4];
// SAFETY: src has w*h*4 bytes; out has dw*dh*4 bytes; degrees is multiple of 90.
unsafe {
bun_image_rotate_rgba8(
src.as_ptr(),
i32::try_from(w).expect("int cast"),
i32::try_from(h).expect("int cast"),
out.as_mut_ptr(),
i32::try_from(degrees).expect("int cast"),
)
};
unsafe { bun_image_rotate_rgba8(src.as_ptr(), kw, kh, out.as_mut_ptr(), i32::from(degrees)) };
Ok(Decoded {
rgba: out,
width: dw,
Expand All @@ -762,16 +765,9 @@ pub(crate) fn flip(src: &[u8], w: u32, h: u32, horizontal: bool) -> Result<Vec<u
Err(e) => return Err(e),
}
}
let (kw, kh) = (kernel_dimension(w)?, kernel_dimension(h)?);
let mut out: Vec<u8> = vec![0u8; (w as usize) * (h as usize) * 4];
// SAFETY: src and out both have w*h*4 bytes.
unsafe {
bun_image_flip_rgba8(
src.as_ptr(),
i32::try_from(w).expect("int cast"),
i32::try_from(h).expect("int cast"),
out.as_mut_ptr(),
horizontal as i32,
)
};
unsafe { bun_image_flip_rgba8(src.as_ptr(), kw, kh, out.as_mut_ptr(), horizontal as i32) };
Ok(out)
}
Loading
Loading