Skip to content
Merged
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
2 changes: 1 addition & 1 deletion crates/compiler-core/src/bytecode/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1240,7 +1240,7 @@ impl<T: OpArgType> Arg<T> {
}

#[inline(always)]
pub fn try_get(self, arg: OpArg) -> Option<T> {
pub fn try_get(self, arg: OpArg) -> Result<T, MarshalError> {
T::from_op_arg(arg.0)
}

Expand Down
45 changes: 24 additions & 21 deletions crates/compiler-core/src/bytecode/oparg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ use bitflags::bitflags;

use core::{fmt, num::NonZeroU8};

use crate::bytecode::{CodeUnit, instruction::Instruction};
use crate::{
bytecode::{CodeUnit, instruction::Instruction},
marshal::MarshalError,
};

pub trait OpArgType: Copy {
fn from_op_arg(x: u32) -> Option<Self>;
fn from_op_arg(x: u32) -> Result<Self, MarshalError>;

fn to_op_arg(self) -> u32;
}
Expand Down Expand Up @@ -158,15 +161,15 @@ impl fmt::Display for ConvertValueOparg {

impl OpArgType for ConvertValueOparg {
#[inline]
fn from_op_arg(x: u32) -> Option<Self> {
Some(match x {
fn from_op_arg(x: u32) -> Result<Self, MarshalError> {
Ok(match x {
// Ruff `ConversionFlag::None` is `-1i8`,
// when its converted to `u8` its value is `u8::MAX`
0 | 255 => Self::None,
1 => Self::Str,
2 => Self::Repr,
3 => Self::Ascii,
_ => return None,
_ => return Err(MarshalError::InvalidBytecode),
})
}

Expand All @@ -188,8 +191,8 @@ pub enum ResumeType {

impl OpArgType for u32 {
#[inline(always)]
fn from_op_arg(x: u32) -> Option<Self> {
Some(x)
fn from_op_arg(x: u32) -> Result<Self, MarshalError> {
Ok(x)
}

#[inline(always)]
Expand All @@ -200,8 +203,8 @@ impl OpArgType for u32 {

impl OpArgType for bool {
#[inline(always)]
fn from_op_arg(x: u32) -> Option<Self> {
Some(x != 0)
fn from_op_arg(x: u32) -> Result<Self, MarshalError> {
Ok(x != 0)
}

#[inline(always)]
Expand All @@ -217,10 +220,10 @@ macro_rules! op_arg_enum_impl {
self as u32
}

fn from_op_arg(x: u32) -> Option<Self> {
Some(match u8::try_from(x).ok()? {
fn from_op_arg(x: u32) -> Result<Self, MarshalError> {
Ok(match u8::try_from(x).map_err(|_| MarshalError::InvalidBytecode)? {
$($value => Self::$var,)*
_ => return None,
_ => return Err(MarshalError::InvalidBytecode),
})
}
}
Expand Down Expand Up @@ -248,8 +251,8 @@ pub struct Label(pub u32);

impl OpArgType for Label {
#[inline(always)]
fn from_op_arg(x: u32) -> Option<Self> {
Some(Self(x))
fn from_op_arg(x: u32) -> Result<Self, MarshalError> {
Ok(Self(x))
}

#[inline(always)]
Expand Down Expand Up @@ -341,8 +344,8 @@ bitflags! {

impl OpArgType for MakeFunctionFlags {
#[inline(always)]
fn from_op_arg(x: u32) -> Option<Self> {
Self::from_bits(x as u8)
fn from_op_arg(x: u32) -> Result<Self, MarshalError> {
Self::from_bits(x as u8).ok_or(MarshalError::InvalidBytecode)
}

#[inline(always)]
Expand Down Expand Up @@ -601,11 +604,11 @@ pub enum BuildSliceArgCount {

impl OpArgType for BuildSliceArgCount {
#[inline(always)]
fn from_op_arg(x: u32) -> Option<Self> {
Some(match x {
fn from_op_arg(x: u32) -> Result<Self, MarshalError> {
Ok(match x {
2 => Self::Two,
3 => Self::Three,
_ => return None,
_ => return Err(MarshalError::InvalidBytecode),
})
}

Expand Down Expand Up @@ -636,9 +639,9 @@ pub struct UnpackExArgs {

impl OpArgType for UnpackExArgs {
#[inline(always)]
fn from_op_arg(x: u32) -> Option<Self> {
fn from_op_arg(x: u32) -> Result<Self, MarshalError> {
let [before, after, ..] = x.to_le_bytes();
Some(Self { before, after })
Ok(Self { before, after })
}
Comment on lines 640 to 645
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Validate upper bytes for UnpackExArgs to avoid accepting invalid bytecode.
Right now values with non-zero high bytes are silently truncated, which defeats the new error reporting.

🛠️ Suggested fix
 fn from_op_arg(x: u32) -> Result<Self, MarshalError> {
-    let [before, after, ..] = x.to_le_bytes();
-    Ok(Self { before, after })
+    let [before, after, hi1, hi2] = x.to_le_bytes();
+    if hi1 != 0 || hi2 != 0 {
+        return Err(MarshalError::InvalidBytecode);
+    }
+    Ok(Self { before, after })
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
impl OpArgType for UnpackExArgs {
#[inline(always)]
fn from_op_arg(x: u32) -> Option<Self> {
fn from_op_arg(x: u32) -> Result<Self, MarshalError> {
let [before, after, ..] = x.to_le_bytes();
Some(Self { before, after })
Ok(Self { before, after })
}
impl OpArgType for UnpackExArgs {
#[inline(always)]
fn from_op_arg(x: u32) -> Result<Self, MarshalError> {
let [before, after, hi1, hi2] = x.to_le_bytes();
if hi1 != 0 || hi2 != 0 {
return Err(MarshalError::InvalidBytecode);
}
Ok(Self { before, after })
}
}
🤖 Prompt for AI Agents
In `@crates/compiler-core/src/bytecode/oparg.rs` around lines 640 - 645,
from_op_arg for UnpackExArgs currently truncates high bytes via to_le_bytes;
instead validate that the upper two bytes of x are zero before constructing
UnpackExArgs to avoid accepting invalid bytecode: check (x >> 16) == 0 (or
inspect bytes[2..]) and return an appropriate MarshalError if non-zero,
otherwise proceed to extract before and after and return Ok(Self{before,
after}); reference UnpackExArgs, OpArgType::from_op_arg, and MarshalError to
locate where to add the check and error return.


#[inline(always)]
Expand Down