|  | 
| 1 |  | -use rustc_abi::{Align, Size}; | 
| 2 |  | -use rustc_ast::expand::allocator::AllocatorKind; | 
|  | 1 | +use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size}; | 
|  | 2 | +use rustc_ast::expand::allocator::SpecialAllocatorMethod; | 
|  | 3 | +use rustc_middle::ty::Ty; | 
|  | 4 | +use rustc_span::Symbol; | 
|  | 5 | +use rustc_target::callconv::FnAbi; | 
| 3 | 6 | 
 | 
| 4 | 7 | use crate::*; | 
| 5 | 8 | 
 | 
| @@ -54,30 +57,100 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | 
| 54 | 57 |         Align::from_bytes(prev_power_of_two(size)).unwrap() | 
| 55 | 58 |     } | 
| 56 | 59 | 
 | 
| 57 |  | -    /// Emulates calling the internal __rust_* allocator functions | 
| 58 |  | -    fn emulate_allocator( | 
|  | 60 | +    /// Check some basic requirements for this allocation request: | 
|  | 61 | +    /// non-zero size, power-of-two alignment. | 
|  | 62 | +    fn check_rust_alloc_request(&self, size: u64, align: u64) -> InterpResult<'tcx> { | 
|  | 63 | +        let this = self.eval_context_ref(); | 
|  | 64 | +        if size == 0 { | 
|  | 65 | +            throw_ub_format!("creating allocation with size 0"); | 
|  | 66 | +        } | 
|  | 67 | +        if size > this.max_size_of_val().bytes() { | 
|  | 68 | +            throw_ub_format!("creating an allocation larger than half the address space"); | 
|  | 69 | +        } | 
|  | 70 | +        if let Err(e) = Align::from_bytes(align) { | 
|  | 71 | +            match e { | 
|  | 72 | +                AlignFromBytesError::TooLarge(_) => { | 
|  | 73 | +                    throw_unsup_format!( | 
|  | 74 | +                        "creating allocation with alignment {align} exceeding rustc's maximum \ | 
|  | 75 | +                         supported value" | 
|  | 76 | +                    ); | 
|  | 77 | +                } | 
|  | 78 | +                AlignFromBytesError::NotPowerOfTwo(_) => { | 
|  | 79 | +                    throw_ub_format!("creating allocation with non-power-of-two alignment {align}"); | 
|  | 80 | +                } | 
|  | 81 | +            } | 
|  | 82 | +        } | 
|  | 83 | + | 
|  | 84 | +        interp_ok(()) | 
|  | 85 | +    } | 
|  | 86 | + | 
|  | 87 | +    fn rust_special_allocator_method( | 
| 59 | 88 |         &mut self, | 
| 60 |  | -        default: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx>, | 
| 61 |  | -    ) -> InterpResult<'tcx, EmulateItemResult> { | 
|  | 89 | +        method: SpecialAllocatorMethod, | 
|  | 90 | +        link_name: Symbol, | 
|  | 91 | +        abi: &FnAbi<'tcx, Ty<'tcx>>, | 
|  | 92 | +        args: &[OpTy<'tcx>], | 
|  | 93 | +        dest: &PlaceTy<'tcx>, | 
|  | 94 | +    ) -> InterpResult<'tcx> { | 
| 62 | 95 |         let this = self.eval_context_mut(); | 
| 63 | 96 | 
 | 
| 64 |  | -        let Some(allocator_kind) = this.tcx.allocator_kind(()) else { | 
| 65 |  | -            // in real code, this symbol does not exist without an allocator | 
| 66 |  | -            return interp_ok(EmulateItemResult::NotSupported); | 
| 67 |  | -        }; | 
|  | 97 | +        match method { | 
|  | 98 | +            SpecialAllocatorMethod::Alloc | SpecialAllocatorMethod::AllocZeroed => { | 
|  | 99 | +                let [size, align] = | 
|  | 100 | +                    this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; | 
|  | 101 | +                let size = this.read_target_usize(size)?; | 
|  | 102 | +                let align = this.read_target_usize(align)?; | 
|  | 103 | + | 
|  | 104 | +                this.check_rust_alloc_request(size, align)?; | 
| 68 | 105 | 
 | 
| 69 |  | -        match allocator_kind { | 
| 70 |  | -            AllocatorKind::Global => { | 
| 71 |  | -                // When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion | 
| 72 |  | -                // of this attribute. As such we have to call an exported Rust function, | 
| 73 |  | -                // and not execute any Miri shim. Somewhat unintuitively doing so is done | 
| 74 |  | -                // by returning `NotSupported`, which triggers the `lookup_exported_symbol` | 
| 75 |  | -                // fallback case in `emulate_foreign_item`. | 
| 76 |  | -                interp_ok(EmulateItemResult::NotSupported) | 
|  | 106 | +                let ptr = this.allocate_ptr( | 
|  | 107 | +                    Size::from_bytes(size), | 
|  | 108 | +                    Align::from_bytes(align).unwrap(), | 
|  | 109 | +                    MiriMemoryKind::Rust.into(), | 
|  | 110 | +                    if matches!(method, SpecialAllocatorMethod::AllocZeroed) { | 
|  | 111 | +                        AllocInit::Zero | 
|  | 112 | +                    } else { | 
|  | 113 | +                        AllocInit::Uninit | 
|  | 114 | +                    }, | 
|  | 115 | +                )?; | 
|  | 116 | + | 
|  | 117 | +                this.write_pointer(ptr, dest) | 
|  | 118 | +            } | 
|  | 119 | +            SpecialAllocatorMethod::Dealloc => { | 
|  | 120 | +                let [ptr, old_size, align] = | 
|  | 121 | +                    this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; | 
|  | 122 | +                let ptr = this.read_pointer(ptr)?; | 
|  | 123 | +                let old_size = this.read_target_usize(old_size)?; | 
|  | 124 | +                let align = this.read_target_usize(align)?; | 
|  | 125 | + | 
|  | 126 | +                // No need to check old_size/align; we anyway check that they match the allocation. | 
|  | 127 | +                this.deallocate_ptr( | 
|  | 128 | +                    ptr, | 
|  | 129 | +                    Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())), | 
|  | 130 | +                    MiriMemoryKind::Rust.into(), | 
|  | 131 | +                ) | 
| 77 | 132 |             } | 
| 78 |  | -            AllocatorKind::Default => { | 
| 79 |  | -                default(this)?; | 
| 80 |  | -                interp_ok(EmulateItemResult::NeedsReturn) | 
|  | 133 | +            SpecialAllocatorMethod::Realloc => { | 
|  | 134 | +                let [ptr, old_size, align, new_size] = | 
|  | 135 | +                    this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; | 
|  | 136 | +                let ptr = this.read_pointer(ptr)?; | 
|  | 137 | +                let old_size = this.read_target_usize(old_size)?; | 
|  | 138 | +                let align = this.read_target_usize(align)?; | 
|  | 139 | +                let new_size = this.read_target_usize(new_size)?; | 
|  | 140 | +                // No need to check old_size; we anyway check that they match the allocation. | 
|  | 141 | + | 
|  | 142 | +                this.check_rust_alloc_request(new_size, align)?; | 
|  | 143 | + | 
|  | 144 | +                let align = Align::from_bytes(align).unwrap(); | 
|  | 145 | +                let new_ptr = this.reallocate_ptr( | 
|  | 146 | +                    ptr, | 
|  | 147 | +                    Some((Size::from_bytes(old_size), align)), | 
|  | 148 | +                    Size::from_bytes(new_size), | 
|  | 149 | +                    align, | 
|  | 150 | +                    MiriMemoryKind::Rust.into(), | 
|  | 151 | +                    AllocInit::Uninit, | 
|  | 152 | +                )?; | 
|  | 153 | +                this.write_pointer(new_ptr, dest) | 
| 81 | 154 |             } | 
| 82 | 155 |         } | 
| 83 | 156 |     } | 
|  | 
0 commit comments