sed -n '435,450p' crates/compiler-core/src/bytecode.rs
Repository: RustPython/RustPython
Length of output: 668
🏁 Script executed:
rg "\.len\(\)" crates/compiler-core/src/bytecode.rs | head -10
Repository: RustPython/RustPython
Length of output: 489
🏁 Script executed:
sed -n '515,570p' crates/compiler-core/src/bytecode.rs
Repository: RustPython/RustPython
Length of output: 2142
Safe readers allow potential UB on invalid index.
read_op, read_arg, and read_adaptive_counter are public safe functions that dereference raw pointers derived from wrapping_add(index) without bounds validation. Out-of-range input will cause undefined behavior instead of panicking. This contradicts the pattern already established in the same file for read_cache_u16, which includes an assertion.
Add bounds checks to all three functions:
Proposed fix
pub fn read_op(&self, index: usize) -> Instruction {
let units = unsafe { &*self.0.get() };
+ assert!(index < units.len(), "read_op: index out of bounds");
let ptr = units.as_ptr().wrapping_add(index) as *const AtomicU8;
let byte = unsafe { &*ptr }.load(Ordering::Acquire);
// SAFETY: Only valid Instruction values are stored via replace_op/compare_exchange_op.
unsafe { mem::transmute::<u8, Instruction>(byte) }
}
pub fn read_arg(&self, index: usize) -> OpArgByte {
let units = unsafe { &*self.0.get() };
+ assert!(index < units.len(), "read_arg: index out of bounds");
let ptr = units.as_ptr().wrapping_add(index) as *const u8;
let arg_ptr = unsafe { ptr.add(1) } as *const AtomicU8;
OpArgByte::from(unsafe { &*arg_ptr }.load(Ordering::Relaxed))
}
pub fn read_adaptive_counter(&self, index: usize) -> u8 {
let units = unsafe { &*self.0.get() };
+ assert!(index < units.len(), "read_adaptive_counter: index out of bounds");
let ptr = units.as_ptr().wrapping_add(index) as *const u8;
let arg_ptr = unsafe { ptr.add(1) } as *const AtomicU8;
unsafe { &*arg_ptr }.load(Ordering::Relaxed)
}
Per coding guidelines: "Follow Rust best practices for error handling and memory management".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/compiler-core/src/bytecode.rs` around lines 485 - 499, The public safe
readers (read_op, read_arg, read_adaptive_counter) currently compute raw
pointers with wrapping_add(index) and dereference without bounds checks, which
can cause UB on out-of-range indices; update each function to load units first
(as in read_cache_u16) and assert the index (and any additional byte offsets,
e.g., index+1 for arg) is within units.len() before computing pointers and
dereferencing so out-of-bounds inputs panic instead of producing UB, keeping the
existing pointer/dereference logic otherwise.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
All reactions
/// Write a u16 value into a CACHE code unit at `index`.
/// Each CodeUnit is 2 bytes (#[repr(C)]: op u8 + arg u8), so one u16 fits exactly.
/// Uses Relaxed atomic store; ordering is provided by replace_op (Release).
///
/// # Safety
/// - `index` must be in bounds and point to a CACHE entry.
/// - The caller must ensure no concurrent reads/writes to the same slot.
The reason will be displayed to describe this comment to others. Learn more.
⚠️ Potential issue | 🔴 Critical
🧩 Analysis chain
🏁 Script executed:
# First, locate and read the relevant section of the bytecode file
fd bytecode.rs | head -1
Repository: RustPython/RustPython
Length of output: 101
🏁 Script executed:
# Read the file to check the CodeUnit struct and the atomic operations around the mentioned lines
cat -n crates/compiler-core/src/bytecode.rs | sed -n '1,50p'
Repository: RustPython/RustPython
Length of output: 2067
🏁 Script executed:
# Get the CodeUnit struct definition and surrounding code
cat -n crates/compiler-core/src/bytecode.rs | sed -n '30,100p'
Repository: RustPython/RustPython
Length of output: 2781
🏁 Script executed:
# Check lines 509-525 to see the actual code
cat -n crates/compiler-core/src/bytecode.rs | sed -n '500,530p'
Repository: RustPython/RustPython
Length of output: 1618
🏁 Script executed:
# Check the size and alignment assertions
cat -n crates/compiler-core/src/bytecode.rs | grep -A 3 "const _:"
Repository: RustPython/RustPython
Length of output: 275
🏁 Script executed:
# Find the CodeUnit struct definition
cat -n crates/compiler-core/src/bytecode.rs | sed -n '340,360p'
Repository: RustPython/RustPython
Length of output: 738
🏁 Script executed:
# Search for the CodeUnit struct definition more broadly
rg "struct CodeUnit" -A 5 crates/compiler-core/src/bytecode.rs
Repository: RustPython/RustPython
Length of output: 492
🏁 Script executed:
# Check if there's any alignment attribute or repr clause on CodeUnit
rg "CodeUnit" crates/compiler-core/src/bytecode.rs | head -20
Repository: RustPython/RustPython
Length of output: 857
🏁 Script executed:
# Check what Instruction and OpArgByte are
rg "type Instruction|struct Instruction|enum Instruction" crates/compiler-core/src/bytecode.rs
# Look at the instruction module for Instruction definition
cat -n crates/compiler-core/src/bytecode/instruction.rs | head -50
Repository: RustPython/RustPython
Length of output: 1871
🏁 Script executed:
# Look at the oparg module for OpArgByte
cat -n crates/compiler-core/src/bytecode/oparg.rs | head -30
Repository: RustPython/RustPython
Length of output: 857
🏁 Script executed:
# Check what the alignment of AtomicU16 is and the current alignment of CodeUnit
cat > /tmp/check_alignment.rs << 'EOF'use core::{mem, sync::atomic::AtomicU16};#[repr(C)]#[derive(Copy, Clone, Debug)]pub struct CodeUnit { pub op: u8, pub arg: u8,}fn main() { println!("CodeUnit size: {}", mem::size_of::<CodeUnit>()); println!("CodeUnit align: {}", mem::align_of::<CodeUnit>()); println!("AtomicU16 size: {}", mem::size_of::<AtomicU16>()); println!("AtomicU16 align: {}", mem::align_of::<AtomicU16>()); println!("u16 align: {}", mem::align_of::<u16>());}EOF
rustc /tmp/check_alignment.rs -o /tmp/check_alignment && /tmp/check_alignment
Repository: RustPython/RustPython
Length of output: 111
🏁 Script executed:
# Verify the actual lines 509-510 and 521-523 in context
cat -n crates/compiler-core/src/bytecode.rs | sed -n '507,524p'
Repository: RustPython/RustPython
Length of output: 961
🏁 Script executed:
# Check if there are any other alignment-related comments or safety notes
rg "align|Alignment" crates/compiler-core/src/bytecode.rs -B 2 -A 2
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Verify the actual Instruction and OpArgByte alignment requirements by checking their definitions
rg "pub struct Instruction|pub enum Instruction" crates/compiler-core/src/bytecode/instruction.rs -A 1
Repository: RustPython/RustPython
Length of output: 147
🏁 Script executed:
# Look for any repr attributes on Instruction
cat -n crates/compiler-core/src/bytecode/instruction.rs | sed -n '16,30p'
Repository: RustPython/RustPython
Length of output: 636
🏁 Script executed:
# Check if there are any tests or documentation about alignment
rg "align|unsafe.*AtomicU16" crates/compiler-core/src/ -B 3 -A 3
Repository: RustPython/RustPython
Length of output: 1165
🏁 Script executed:
# Check what the actual byte layout is for CodeUnit
python3 << 'EOF'# Instruction is #[repr(u8)] - 1 byte# OpArgByte is #[repr(transparent)] wrapping u8 - 1 byte# So CodeUnit = 1 + 1 = 2 bytes, but with #[repr(C)], alignment is 1 byteprint("With #[repr(C)] and two u8 fields:")print(" Size: 2 bytes")print(" Alignment: 1 byte (minimum of field alignments)")print()print("AtomicU16 requires:")print(" Size: 2 bytes")print(" Alignment: 2 bytes (natural alignment for atomic operations)")print()print("Issue: Casting 1-byte aligned pointer to 2-byte aligned is UB")EOF
Repository: RustPython/RustPython
Length of output: 328
🏁 Script executed:
# Check if there are any other unsafe pointer casts in the file that might have similar issues
rg "as \*const AtomicU" crates/compiler-core/src/bytecode.rs
Repository: RustPython/RustPython
Length of output: 623
🏁 Script executed:
# Check the SAFETY comment around these operations
cat -n crates/compiler-core/src/bytecode.rs | sed -n '495,535p'
Repository: RustPython/RustPython
Length of output: 2081
Add explicit 2-byte alignment guarantee to CodeUnit struct.
The casts to *const AtomicU16 at lines 509 and 521 require 2-byte alignment, but CodeUnit with #[repr(C)] and two u8 fields has only 1-byte alignment. This violates atomic type requirements and is undefined behavior on any target. The current size assertion only checks that CodeUnit is 2 bytes, not that it's 2-byte aligned.
Add align(2) to the repr attribute and assert alignment:
‼️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
let ptr = units.as_ptr().wrapping_add(index)as*constAtomicU16;
Verify each finding against the current code and only fix it if needed.
In `@crates/compiler-core/src/bytecode.rs` around lines 509 - 510, The code casts
CodeUnit pointers to *const AtomicU16 (see usages like
units.as_ptr().wrapping_add(index) as *const AtomicU16 and the subsequent .store
calls), but CodeUnit is currently #[repr(C)] with two u8 fields and only 1-byte
alignment, which violates atomic alignment requirements; fix by changing the
struct declaration to #[repr(C, align(2))] for CodeUnit and add a compile-time
assertion to ensure alignment (e.g. assert_eq!(std::mem::align_of::<CodeUnit>(),
2) or a const_assert) so the AtomicU16 casts are safe.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
All reactions
}
/// Read a u16 value from a CACHE code unit at `index`.
/// Uses Relaxed atomic load; ordering is provided by read_op (Acquire).
Verify each finding against the current code and only fix it if needed.
In `@crates/vm/src/builtins/function.rs` around lines 711 - 716, The update to
__code__ must be atomic with JIT-cache invalidation to avoid running stale
jitted code; modify set___code__ so it acquires the jitted_code mutex and clears
the cached entry before performing self.code.swap_to_temporary_refs, or hold the
same jitted_code lock while doing both operations so no other thread can observe
cleared/uncleared state in between. Specifically, in set___code__, lock
jitted_code (the Mutex used at *self.jitted_code.lock()), set it to None, then
call self.code.swap_to_temporary_refs(code, vm) while still holding the lock,
and finally release the lock so the swap and cache invalidation occur
atomically.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
The reason will be displayed to describe this comment to others. Learn more.
⚠️ Potential issue | 🔴 Critical
Close the version-tag race introduced by pre-invalidation.
Calling zelf.modified() only before mutation allows a concurrent find_name_in_mro to assign/cache under a fresh tag during the mutation window (see Line 761-Line 777), so stale results can remain valid after the write completes.
🔧 Suggested fix
// Invalidate inline caches before modifying attributes.
// This ensures other threads see the version invalidation before
// any attribute changes, preventing use-after-free of cached descriptors.
zelf.modified();
if let PySetterValue::Assign(value) = value {
zelf.attributes.write().insert(attr_name, value);
} else {
let prev_value = zelf.attributes.write().shift_remove(attr_name); // TODO: swap_remove applicable?
if prev_value.is_none() {
return Err(vm.new_attribute_error(format!(
"type object '{}' has no attribute '{}'",
zelf.name(),
attr_name,
)));
}
}
if attr_name.as_wtf8().starts_with("__") && attr_name.as_wtf8().ends_with("__") {
if assign {
zelf.update_slot::<true>(attr_name, &vm.ctx);
} else {
zelf.update_slot::<false>(attr_name, &vm.ctx);
}
}
+ // Close the race: another thread may have assigned/cached a fresh+ // version while mutation was in-flight.+ zelf.modified();
Ok(())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/vm/src/builtins/type.rs` around lines 2171 - 2175, The current
pre-invalidation (calling zelf.modified() once before attribute mutation) leaves
a window where a concurrent find_name_in_mro may cache results under the new tag
during the mutation; fix this by invoking zelf.modified() both before starting
the mutation and once more immediately after finishing attribute modifications
so any caches created during the mutation are replay-invalidated; update the
code path where attributes are changed (the block containing the single
zelf.modified() call) to perform a second modified() post-mutation and ensure
this behavior aligns with the cache-checking logic in find_name_in_mro.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
All reactions
if let PySetterValue::Assign(value) = value {
zelf.attributes.write().insert(attr_name, value);
} else {
Expand All
@@ -2180,8 +2185,6 @@ impl SetAttr for PyType {
)));
}
}
// Invalidate inline caches that depend on this type's attributes
zelf.modified();
if attr_name.as_wtf8().starts_with("__") && attr_name.as_wtf8().ends_with("__") {
There was an error while loading. Please reload this page.
Loading
Oops, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.