From f407f8caf55db98bdaf84a6cc2b703a997d006db Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Wed, 2 Jul 2025 21:57:01 +0800 Subject: [PATCH 01/17] Generalize pattern CVE-2020-35877 --- tests/ui/cve/cve_2020_35877/minimal.rs | 7 +++ tests/ui/cve_2020_35886/cve_2020_35886.rs | 66 +++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/ui/cve_2020_35886/cve_2020_35886.rs diff --git a/tests/ui/cve/cve_2020_35877/minimal.rs b/tests/ui/cve/cve_2020_35877/minimal.rs index 2063a1e8..74182b3c 100644 --- a/tests/ui/cve/cve_2020_35877/minimal.rs +++ b/tests/ui/cve/cve_2020_35877/minimal.rs @@ -17,6 +17,13 @@ fn unchecked_slice(slice: &[T], index: usize) -> *const T { } } +// #[rpl::dump_mir(dump_cfg, dump_ddg)] +fn slice_end(slice: &[T], index: usize) -> *const T { + let p = slice.as_ptr(); + let length = slice.len(); + unsafe { p.add(index) } +} + // #[rpl::dump_mir(dump_cfg, dump_ddg)] fn checked_lt(slice: &[T], index: usize) -> &T { let mut p: *const T = slice.as_ptr(); diff --git a/tests/ui/cve_2020_35886/cve_2020_35886.rs b/tests/ui/cve_2020_35886/cve_2020_35886.rs new file mode 100644 index 00000000..381b1d0c --- /dev/null +++ b/tests/ui/cve_2020_35886/cve_2020_35886.rs @@ -0,0 +1,66 @@ +//@ revisions: inline regular +//@[inline] compile-flags: -Z inline-mir=true +//@[regular] compile-flags: -Z inline-mir=false +use std::alloc::{Layout, alloc, alloc_zeroed, dealloc}; +use std::ops::{Index, IndexMut, Range}; + +pub struct Array { + size: usize, + ptr: *mut T, +} + +impl Array { + /// Convert to slice + pub fn to_slice<'a>(&'a self) -> &'a [T] { + unsafe { std::slice::from_raw_parts(self.ptr as *const T, self.size) } + } + + /// Convert to mutable slice + pub fn to_slice_mut<'a>(&'a mut self) -> &'a mut [T] { + unsafe { std::slice::from_raw_parts_mut(self.ptr, self.size) } + } + + /// The length of the array (number of elements T) + pub fn len(&self) -> usize { + self.size + } +} + +impl Index for Array { + type Output = T; + + #[rpl::dump_mir(dump_cfg, dump_ddg)] + fn index<'a>(&'a self, idx: usize) -> &'a Self::Output { + unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() + } +} + +impl IndexMut for Array { + fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut Self::Output { + unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() + } +} + +impl Index> for Array { + type Output = [T]; + + fn index<'a>(&'a self, idx: Range) -> &'a Self::Output { + &self.to_slice()[idx] + } +} + +impl IndexMut> for Array { + fn index_mut<'a>(&'a mut self, idx: Range) -> &'a mut Self::Output { + &mut self.to_slice_mut()[idx] + } +} + +impl Drop for Array { + fn drop(&mut self) { + let objsize = std::mem::size_of::(); + let layout = Layout::from_size_align(self.size * objsize, 8).unwrap(); + unsafe { + dealloc(self.ptr as *mut u8, layout); + } + } +} From aeeab61b8094b99d58bb7507b01b1fc8fa3c4ad5 Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Thu, 3 Jul 2025 12:36:30 +0800 Subject: [PATCH 02/17] Support more kinds of constants --- crates/rpl_match/src/fns.rs | 4 +- crates/rpl_match/src/lib.rs | 2 +- crates/rpl_match/src/matches/mod.rs | 21 ++++++-- crates/rpl_match/src/statement.rs | 1 + crates/rpl_match/src/ty.rs | 69 +++++++++++++++++++++++++- tests/ui/cve/cve_2020_35877/minimal.rs | 10 ++++ 6 files changed, 99 insertions(+), 8 deletions(-) diff --git a/crates/rpl_match/src/fns.rs b/crates/rpl_match/src/fns.rs index 00da55f2..85259128 100644 --- a/crates/rpl_match/src/fns.rs +++ b/crates/rpl_match/src/fns.rs @@ -24,7 +24,7 @@ impl<'a, 'pcx, 'tcx> MatchFnCtxt<'a, 'pcx, 'tcx> { Self { ty, fn_pat } } - #[instrument(level = "info", skip_all, fields(fn_pat = %self.fn_pat, fn_did = ?fn_did.into()), ret)] + #[instrument(level = "debug", skip_all, fields(fn_pat = %self.fn_pat, fn_did = ?fn_did.into()), ret)] pub fn match_fn(&self, fn_did: impl Into + Copy) -> bool { let fn_did = fn_did.into(); let poly_fn_sig = match self.ty.tcx.type_of(fn_did).instantiate_identity().kind() { @@ -33,7 +33,7 @@ impl<'a, 'pcx, 'tcx> MatchFnCtxt<'a, 'pcx, 'tcx> { _ => unimplemented!(), }; let fn_sig = self.ty.tcx.liberate_late_bound_regions(fn_did, poly_fn_sig); - info!(?fn_sig); + debug!(?fn_sig); (self.fn_pat.params.len() <= fn_sig.inputs().len() || self.fn_pat.params.non_exhaustive) && zip(self.fn_pat.params.iter(), fn_sig.inputs()) .all(|(param_pat, ¶m_ty)| self.match_param(param_pat, param_ty)) diff --git a/crates/rpl_match/src/lib.rs b/crates/rpl_match/src/lib.rs index c34431bf..60f8bb76 100644 --- a/crates/rpl_match/src/lib.rs +++ b/crates/rpl_match/src/lib.rs @@ -46,4 +46,4 @@ pub use adt::{AdtMatch, Candidates, MatchAdtCtxt}; pub use counted::CountedMatch; pub use fns::MatchFnCtxt; pub use place::MatchPlaceCtxt; -pub use ty::MatchTyCtxt; +pub use ty::{Const, MatchTyCtxt}; diff --git a/crates/rpl_match/src/matches/mod.rs b/crates/rpl_match/src/matches/mod.rs index dd9c9a44..4a623539 100644 --- a/crates/rpl_match/src/matches/mod.rs +++ b/crates/rpl_match/src/matches/mod.rs @@ -4,14 +4,15 @@ use std::ops::Index; use rpl_constraints::attributes::ExtraSpan; use rpl_context::pat::{LabelMap, Spanned}; +use rpl_match::{Const, CountedMatch}; use rpl_mir_graph::TerminatorEdges; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::FnDecl; use rustc_index::bit_set::MixedBitSet; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir::visit::PlaceContext; -use rustc_middle::mir::{self, Const, PlaceRef}; +use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext}; +use rustc_middle::mir::{self, Const, HasLocalDecls, PlaceRef}; use rustc_middle::ty::Ty; use rustc_span::{Span, Symbol}; @@ -348,6 +349,18 @@ impl StatementMatch { } source_info.span } + + pub fn is_arg(self, body: &mir::Body<'_>) -> bool { + match self { + StatementMatch::Arg(local) => local_is_arg(local, body), + StatementMatch::Location(_) => false, + } + } +} + +#[inline] +pub fn local_is_arg(local: mir::Local, body: &mir::Body<'_>) -> bool { + local.as_usize() > 0 && local.as_usize() < body.arg_count + 1 } struct MatchCtxt<'a, 'pcx, 'tcx> { @@ -1048,8 +1061,8 @@ impl<'a, 'pcx, 'tcx> MatchCtxt<'a, 'pcx, 'tcx> { self.matching[ty_var].matched.r#match(ty) } #[instrument(level = "debug", skip(self), ret)] - fn match_const_var(&self, const_var: pat::ConstVarIdx, ty: Const<'tcx>) -> bool { - self.matching[const_var].matched.r#match(ty) + fn match_const_var(&self, const_var: pat::ConstVarIdx, konst: Const<'tcx>) -> bool { + self.matching[const_var].matched.r#match(konst) } #[instrument(level = "debug", skip(self), ret)] fn match_place_var(&self, place_var: pat::PlaceVarIdx, place: PlaceRef<'tcx>) -> bool { diff --git a/crates/rpl_match/src/statement.rs b/crates/rpl_match/src/statement.rs index 99305ea5..3e4ce944 100644 --- a/crates/rpl_match/src/statement.rs +++ b/crates/rpl_match/src/statement.rs @@ -1,5 +1,6 @@ use std::iter::zip; +pub use matches::{Matched, StatementMatch, local_is_arg}; use rpl_context::PatCtxt; pub use rpl_context::pat; use rpl_mir_graph::TerminatorEdges; diff --git a/crates/rpl_match/src/ty.rs b/crates/rpl_match/src/ty.rs index f8d6e095..fd97acd6 100644 --- a/crates/rpl_match/src/ty.rs +++ b/crates/rpl_match/src/ty.rs @@ -1,6 +1,8 @@ use std::cell::RefCell; use std::iter::zip; +use derive_more::derive::{Debug, Display}; +use either::Either; use rpl_constraints::predicates::{PredicateArg, PredicateKind}; use rpl_context::{PatCtxt, pat}; use rpl_resolve::{PatItemKind, def_path_res}; @@ -11,13 +13,44 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::definitions::{DefPathData, DefPathDataName}; use rustc_index::IndexVec; use rustc_middle::mir; -use rustc_middle::ty::{self, TyCtxt, ValTreeKind}; +use rustc_middle::ty::{self, TyCtxt, TypingEnv, ValTreeKind}; use rustc_span::Symbol; use rustc_span::symbol::kw; use crate::resolve::{lang_item_res, ty_res}; use crate::{AdtMatch, Candidates, MatchAdtCtxt}; +#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Const<'tcx> { + #[debug("{_0:?}")] + #[display("{_0}")] + MIR(mir::Const<'tcx>), + #[debug("{_0:?}")] + #[display("{_0}")] + Param(ty::ParamConst), +} + +impl<'tcx> Const<'tcx> { + pub fn try_eval_target_usize(self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> Option { + match self { + Self::MIR(konst) => Some(konst.eval_target_usize(tcx, typing_env)), + Self::Param(_) => None, + } + } + /// Returns if `self` may be greater than or equal to `other`. + #[instrument(level = "info", skip(tcx, typing_env), ret)] + pub fn maybe_ge(self, other: Self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> bool { + match (self, other) { + (Self::MIR(konst1), Self::MIR(konst2)) => { + let val1 = konst1.eval_target_usize(tcx, typing_env); + let val2 = konst2.eval_target_usize(tcx, typing_env); + val1 > val2 + }, + (_, _) => true, + } + } +} + pub struct MatchTyCtxt<'pcx, 'tcx> { pub tcx: TyCtxt<'tcx>, pub pcx: PatCtxt<'pcx>, @@ -309,6 +342,40 @@ pub(crate) trait MatchTy<'pcx, 'tcx> { } } + #[instrument(level = "trace", skip(self), ret)] + pub fn match_ty_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: ty::Const<'tcx>) -> bool { + //FIXME: handle more cases of `ty::ConstKind` + match konst.kind() { + ty::ConstKind::Param(param) => { + let ty = param.find_ty_from_env(self.typing_env.param_env); + self.match_ty(const_var.ty, ty) && { + // We can't convert a const generic param into a `mir::Const` + self.const_vars[const_var.idx].borrow_mut().insert(Const::Param(param)); + true + } + }, + ty::ConstKind::Value(value) => { + self.match_ty(const_var.ty, value.ty) && { + let const_value = self.tcx.valtree_to_const_val(value); + self.const_vars[const_var.idx] + .borrow_mut() + .insert(Const::MIR(mir::Const::from_value(const_value, value.ty))); + true + } + }, + _ => false, + } + } + + #[instrument(level = "trace", skip(self), ret)] + pub fn match_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: mir::Const<'tcx>) -> bool { + if self.match_ty(const_var.ty, konst.ty()) { + self.const_vars[const_var.idx].borrow_mut().insert(Const::MIR(konst)); + return true; + } + false + } + #[instrument(level = "debug", skip(self), ret)] fn match_region(&self, pat: pat::RegionKind, region: ty::Region<'tcx>) -> bool { // FIXME: implement region matching diff --git a/tests/ui/cve/cve_2020_35877/minimal.rs b/tests/ui/cve/cve_2020_35877/minimal.rs index 74182b3c..039a82e2 100644 --- a/tests/ui/cve/cve_2020_35877/minimal.rs +++ b/tests/ui/cve/cve_2020_35877/minimal.rs @@ -24,6 +24,15 @@ fn slice_end(slice: &[T], index: usize) -> *const T { unsafe { p.add(index) } } +// #[rpl::dump_mir(dump_cfg, dump_ddg)] +fn vec_iter(slice: &Vec) -> usize { + let mut x = 0; + for i in slice { + x += 1000000007 % (*i + 1); + } + x +} + // #[rpl::dump_mir(dump_cfg, dump_ddg)] fn checked_lt(slice: &[T], index: usize) -> &T { let mut p: *const T = slice.as_ptr(); @@ -155,6 +164,7 @@ fn safe_unchecked_2_const_rem(slice: &[T; N], index: usize) - unsafe { &*ptr.add(index % N) } } +// #[rpl::dump_mir(dump_cfg, dump_ddg)] fn safe_unchecked_2_const(slice: &[T; N]) -> &T { let ptr = slice.as_ptr(); unsafe { &*ptr.add(N) } From 843d4d5f5089694a7d8d1f7dd53058c884f6a46a Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Thu, 3 Jul 2025 14:28:03 +0800 Subject: [PATCH 03/17] Improve pattern CVE-2020-35877 --- crates/rpl_match/src/lib.rs | 2 +- crates/rpl_match/src/ty.rs | 27 ++- .../rpl_patterns/src/inline/cve_2020_35887.rs | 218 ++++++++++++++++++ tests/ui/cve/cve_2020_35877/minimal.rs | 46 +++- 4 files changed, 283 insertions(+), 10 deletions(-) create mode 100644 crates/rpl_patterns/src/inline/cve_2020_35887.rs diff --git a/crates/rpl_match/src/lib.rs b/crates/rpl_match/src/lib.rs index 60f8bb76..6327aafc 100644 --- a/crates/rpl_match/src/lib.rs +++ b/crates/rpl_match/src/lib.rs @@ -46,4 +46,4 @@ pub use adt::{AdtMatch, Candidates, MatchAdtCtxt}; pub use counted::CountedMatch; pub use fns::MatchFnCtxt; pub use place::MatchPlaceCtxt; -pub use ty::{Const, MatchTyCtxt}; +pub use ty::{Const, MatchTyCtxt, TryCmpAs}; diff --git a/crates/rpl_match/src/ty.rs b/crates/rpl_match/src/ty.rs index fd97acd6..f59df9a3 100644 --- a/crates/rpl_match/src/ty.rs +++ b/crates/rpl_match/src/ty.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::cmp::Ordering; use std::iter::zip; use derive_more::derive::{Debug, Display}; @@ -37,16 +38,32 @@ impl<'tcx> Const<'tcx> { Self::Param(_) => None, } } - /// Returns if `self` may be greater than or equal to `other`. - #[instrument(level = "info", skip(tcx, typing_env), ret)] - pub fn maybe_ge(self, other: Self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> bool { +} + +/// FIXME: this generic parameter is not as convenient as intended, as `self.try_cmp_as(other, tcx, +/// typing_env)` does not provide a way to specify `T` +pub trait TryCmpAs<'tcx, T>: Copy { + /// Compare two `Const` values, returning `Some(Ordering)` if they can be compared. + fn try_cmp_as(self, other: Self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> Option; +} + +impl<'tcx> TryCmpAs<'tcx, usize> for Const<'tcx> { + #[instrument(level = "debug", skip(tcx, typing_env), ret)] + fn try_cmp_as(self, other: Self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> Option { match (self, other) { (Self::MIR(konst1), Self::MIR(konst2)) => { let val1 = konst1.eval_target_usize(tcx, typing_env); let val2 = konst2.eval_target_usize(tcx, typing_env); - val1 > val2 + Some(val1.cmp(&val2)) + }, + (Self::Param(param1), Self::Param(param2)) => { + if param1.index == param2.index { + Some(Ordering::Equal) + } else { + None + } }, - (_, _) => true, + (_, _) => None, } } } diff --git a/crates/rpl_patterns/src/inline/cve_2020_35887.rs b/crates/rpl_patterns/src/inline/cve_2020_35887.rs new file mode 100644 index 00000000..ecce76c6 --- /dev/null +++ b/crates/rpl_patterns/src/inline/cve_2020_35887.rs @@ -0,0 +1,218 @@ +use crate::lints::UNCHECKED_POINTER_OFFSET; +use rpl_context::PatCtxt; +use rpl_mir::{CheckMirCtxt, pat}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{self as hir}; +use rustc_middle::hir::nested_filter::All; +use rustc_middle::ty::TyCtxt; +use rustc_span::{Span, Symbol}; +use std::ops::Not; + +#[instrument(level = "info", skip_all)] +pub fn check_item(tcx: TyCtxt<'_>, pcx: PatCtxt<'_>, item_id: hir::ItemId) { + let item = tcx.hir().item(item_id); + // let def_id = item_id.owner_id.def_id; + let mut check_ctxt = CheckFnCtxt::new(tcx, pcx); + check_ctxt.visit_item(item); +} + +struct CheckFnCtxt<'pcx, 'tcx> { + tcx: TyCtxt<'tcx>, + pcx: PatCtxt<'pcx>, +} + +impl<'pcx, 'tcx> CheckFnCtxt<'pcx, 'tcx> { + fn new(tcx: TyCtxt<'tcx>, pcx: PatCtxt<'pcx>) -> Self { + Self { tcx, pcx } + } +} + +impl<'tcx> Visitor<'tcx> for CheckFnCtxt<'_, 'tcx> { + type NestedFilter = All; + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + #[instrument(level = "debug", skip_all, fields(?item.owner_id))] + fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) -> Self::Result { + match item.kind { + hir::ItemKind::Trait(hir::IsAuto::No, ..) | hir::ItemKind::Impl(_) | hir::ItemKind::Fn { .. } => {}, + _ => return, + } + intravisit::walk_item(self, item); + } + + #[instrument(level = "info", skip_all, fields(?def_id))] + fn visit_fn( + &mut self, + kind: intravisit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + body_id: hir::BodyId, + _span: Span, + def_id: LocalDefId, + ) -> Self::Result { + // let attrs: Vec<_> = self + // .tcx + // .get_attrs_by_path(def_id.to_def_id(), &[Symbol::intern("rpl"), Symbol::intern("check")]) + // .collect(); + // info!("attrs: {:?}", attrs); + // if attrs.is_empty() { + // return; + // } + + if kind.header().is_none_or(|header| header.is_unsafe().not()) && self.tcx.is_mir_available(def_id) { + let body = self.tcx.optimized_mir(def_id); + + let pattern = pattern_unchecked_ptr_offset_(self.pcx); + let matches = CheckMirCtxt::new(self.tcx, self.pcx, body, pattern.pattern, pattern.fn_pat).check(); + for matches in matches { + let len = matches[pattern.len]; + if !len.is_arg(body) { + continue; + } + let ptr = matches[pattern.ptr]; + let offset = matches[pattern.offset]; + let span_ptr = ptr.span_no_inline(body); + let span_offset = offset.span_no_inline(body); + debug!(?ptr, ?offset, ?pattern.ptr, ?pattern.offset, ?span_ptr, ?span_offset, "unchecked offset found"); + let ptr = span_ptr; + let offset = span_offset; + self.tcx.emit_node_span_lint( + UNCHECKED_POINTER_OFFSET, + self.tcx.local_def_id_to_hir_id(def_id), + offset, + crate::errors::UncheckedPtrOffset { ptr, offset }, + ); + } + + let pattern = pattern_unchecked_mut_ptr_offset_(self.pcx); + let matches = CheckMirCtxt::new(self.tcx, self.pcx, body, pattern.pattern, pattern.fn_pat).check(); + for matches in matches { + let len = matches[pattern.len]; + if !len.is_arg(body) { + continue; + } + let ptr = matches[pattern.ptr]; + let offset = matches[pattern.offset]; + let span_ptr = ptr.span_no_inline(body); + let span_offset = offset.span_no_inline(body); + debug!(?ptr, ?offset, ?pattern.ptr, ?pattern.offset, ?span_ptr, ?span_offset, "unchecked offset found"); + let ptr = span_ptr; + let offset = span_offset; + self.tcx.emit_node_span_lint( + UNCHECKED_POINTER_OFFSET, + self.tcx.local_def_id_to_hir_id(def_id), + offset, + crate::errors::UncheckedPtrOffset { ptr, offset }, + ); + } + } + intravisit::walk_fn(self, kind, decl, body_id, def_id); + } +} + +struct PatternUncheckedPtrOffsetGeneral<'pcx> { + pattern: &'pcx pat::Pattern<'pcx>, + fn_pat: &'pcx pat::Fn<'pcx>, + len: pat::Location, + ptr: pat::Location, + offset: pat::Location, +} + +macro_rules! template { + ($name:ident -> $ret:ident { $($fields:ident),* $(,)? } {$($inner:tt)*}) => { + #[rpl_macros::pattern_def] + fn $name(pcx: PatCtxt<'_>) -> $ret<'_> { + $( + let $fields; + )* + let pattern = rpl! { + $($inner)* + }; + let fn_pat = pattern.fns.get_fn_pat(Symbol::intern("pattern")).unwrap(); + + $ret { pattern, fn_pat, $($fields),* } + } + }; +} + +// #[rpl_macros::pattern_def] +// fn pattern_unchecked_ptr_offset_(pcx: PatCtxt<'_>) -> PatternUncheckedPtrOffsetGeneral<'_> { +// let ptr; +// let offset; +// let pattern = rpl! { +// #[meta($T:ty)] +// fn $pattern(..) -> _ = mir! { +// #[export(ptr)] +// let $ptr: *const $T = _; +// #[export(offset)] +// let $ptr_1: *const $T = Offset(copy $ptr, _); +// } +// }; +// let fn_pat = pattern.fns.get_fn_pat(Symbol::intern("pattern")).unwrap(); + +// PatternUncheckedPtrOffsetGeneral { +// pattern, +// fn_pat, +// ptr, +// offset, +// } +// } + +// macro_rules! pattern_checked_ptr_offset { +// ($name:ident, $($cmp_expr:tt)*) => { +// #[rpl_macros::pattern_def] +// fn $name(pcx: PatCtxt<'_>) -> PatternUncheckedPtrOffsetGeneral<'_> { +// let ptr; +// let offset; +// let pattern = rpl! { +// #[meta($T:ty, $U:ty)] +// fn $pattern(..) -> _ = mir! { +// let $index: $U = _; +// #[export(ptr)] +// let $ptr: *const $T = _; +// let $cmp: bool = $($cmp_expr)*; +// #[export(offset)] +// let $ptr_1: *const $T = Offset(copy $ptr, _); +// } +// }; +// let fn_pat = pattern.fns.get_fn_pat(Symbol::intern("pattern")).unwrap(); + +// PatternUncheckedPtrOffsetGeneral { +// pattern, +// fn_pat, +// ptr, +// offset, +// } +// } +// }; +// } + +template! { + pattern_unchecked_ptr_offset_ -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { + #[meta($T:ty)] + fn $pattern(..) -> _ = mir! { + #[export(len)] + let $len: usize = _; + #[export(ptr)] + let $ptr: *const $T = _; + #[export(offset)] + let $ptr_1: *const $T = Offset(copy $ptr, copy $len); + } + } +} + +template! { + pattern_unchecked_mut_ptr_offset_ -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { + #[meta($T:ty)] + fn $pattern(..) -> _ = mir! { + #[export(len)] + let $len: usize = _; + #[export(ptr)] + let $ptr: *mut $T = _; + #[export(offset)] + let $ptr_1: *mut $T = Offset(copy $ptr, copy $len); + } + } +} diff --git a/tests/ui/cve/cve_2020_35877/minimal.rs b/tests/ui/cve/cve_2020_35877/minimal.rs index 039a82e2..2d1af696 100644 --- a/tests/ui/cve/cve_2020_35877/minimal.rs +++ b/tests/ui/cve/cve_2020_35877/minimal.rs @@ -18,17 +18,54 @@ fn unchecked_slice(slice: &[T], index: usize) -> *const T { } // #[rpl::dump_mir(dump_cfg, dump_ddg)] -fn slice_end(slice: &[T], index: usize) -> *const T { +fn slice_end(slice: &[T]) -> *const T { let p = slice.as_ptr(); let length = slice.len(); - unsafe { p.add(index) } + unsafe { p.add(length) } } // #[rpl::dump_mir(dump_cfg, dump_ddg)] -fn vec_iter(slice: &Vec) -> usize { +fn slice_at(slice: &[T], index: usize) -> *const T { + let p = slice.as_ptr(); + let length = slice.len(); + assert!(index < length); + unsafe { p.add(length) } +} + +// #[rpl::dump_mir(dump_cfg, dump_ddg)] +fn vec_iter(vec: &Vec) -> usize { + let mut x = 0; + for i in vec { + x += 1000000007 % (*i + 1); + } + x +} + +// #[rpl::dump_mir(dump_cfg, dump_ddg)] +fn vec_iter_mut(vec: &mut Vec) -> usize { + let mut x = 0; + for i in vec.iter_mut() { + x += 1000000007 % (*i + 1); + *i += 1; + } + x +} + +// #[rpl::dump_mir(dump_cfg, dump_ddg)] +fn slice_iter(vec: &[usize]) -> usize { + let mut x = 0; + for i in vec { + x += 1000000007 % (*i + 1); + } + x +} + +// #[rpl::dump_mir(dump_cfg, dump_ddg)] +fn slice_iter_mut(vec: &mut [usize]) -> usize { let mut x = 0; - for i in slice { + for i in vec.iter_mut() { x += 1000000007 % (*i + 1); + *i += 1; } x } @@ -209,6 +246,7 @@ unsafe fn unsafe_unchecked_in_unsafe(p: *const T) -> *const T { unsafe { p.add(1) } } +// #[rpl::dump_mir(dump_cfg, dump_ddg)] fn unsafe_unchecked_in_safe(p: *const T) -> *const T { // Sorry, it's in a safe function :( unsafe { p.add(1) } From ad04486648e49ad3feacf4f41030c629be383164 Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Thu, 3 Jul 2025 14:59:03 +0800 Subject: [PATCH 04/17] Add a pattern for CVE-2020-35887 --- .../rpl_patterns/src/inline/cve_2020_35887.rs | 228 ++++++++++-------- .../cve_2020_35886.inline.stderr | 28 +++ tests/ui/cve_2020_35886/cve_2020_35886.rs | 6 +- 3 files changed, 161 insertions(+), 101 deletions(-) create mode 100644 tests/ui/cve_2020_35886/cve_2020_35886.inline.stderr diff --git a/crates/rpl_patterns/src/inline/cve_2020_35887.rs b/crates/rpl_patterns/src/inline/cve_2020_35887.rs index ecce76c6..ab24f207 100644 --- a/crates/rpl_patterns/src/inline/cve_2020_35887.rs +++ b/crates/rpl_patterns/src/inline/cve_2020_35887.rs @@ -61,51 +61,43 @@ impl<'tcx> Visitor<'tcx> for CheckFnCtxt<'_, 'tcx> { // return; // } - if kind.header().is_none_or(|header| header.is_unsafe().not()) && self.tcx.is_mir_available(def_id) { + if kind.header().is_none_or(|header| header.is_unsafe().not()) + && self.tcx.visibility(def_id).is_public() + && self.tcx.is_mir_available(def_id) + { let body = self.tcx.optimized_mir(def_id); - let pattern = pattern_unchecked_ptr_offset_(self.pcx); - let matches = CheckMirCtxt::new(self.tcx, self.pcx, body, pattern.pattern, pattern.fn_pat).check(); - for matches in matches { - let len = matches[pattern.len]; - if !len.is_arg(body) { - continue; + for pattern in [ + pattern_unchecked_ptr_offset(self.pcx), + pattern_unchecked_mut_ptr_offset(self.pcx), + pattern_unchecked_ptr_casted_offset(self.pcx), + pattern_unchecked_mut_ptr_casted_offset(self.pcx), + pattern_unchecked_ptr_arith_offset(self.pcx), + pattern_unchecked_mut_ptr_arith_offset(self.pcx), + pattern_unchecked_ptr_casted_arith_offset(self.pcx), + pattern_unchecked_mut_ptr_casted_arith_offset(self.pcx), + ] { + let matches = CheckMirCtxt::new(self.tcx, self.pcx, body, pattern.pattern, pattern.fn_pat).check(); + for matches in matches { + let len = matches[pattern.len]; + if !len.is_arg(body) { + trace!(?len, "not an argument, skipping"); + continue; + } + let ptr = matches[pattern.ptr]; + let offset = matches[pattern.offset]; + let span_ptr = ptr.span_no_inline(body); + let span_offset = offset.span_no_inline(body); + debug!(?ptr, ?offset, ?pattern.ptr, ?pattern.offset, ?span_ptr, ?span_offset, "unchecked offset found"); + let ptr = span_ptr; + let offset = span_offset; + self.tcx.emit_node_span_lint( + UNCHECKED_POINTER_OFFSET, + self.tcx.local_def_id_to_hir_id(def_id), + offset, + crate::errors::UncheckedPtrOffset { ptr, offset }, + ); } - let ptr = matches[pattern.ptr]; - let offset = matches[pattern.offset]; - let span_ptr = ptr.span_no_inline(body); - let span_offset = offset.span_no_inline(body); - debug!(?ptr, ?offset, ?pattern.ptr, ?pattern.offset, ?span_ptr, ?span_offset, "unchecked offset found"); - let ptr = span_ptr; - let offset = span_offset; - self.tcx.emit_node_span_lint( - UNCHECKED_POINTER_OFFSET, - self.tcx.local_def_id_to_hir_id(def_id), - offset, - crate::errors::UncheckedPtrOffset { ptr, offset }, - ); - } - - let pattern = pattern_unchecked_mut_ptr_offset_(self.pcx); - let matches = CheckMirCtxt::new(self.tcx, self.pcx, body, pattern.pattern, pattern.fn_pat).check(); - for matches in matches { - let len = matches[pattern.len]; - if !len.is_arg(body) { - continue; - } - let ptr = matches[pattern.ptr]; - let offset = matches[pattern.offset]; - let span_ptr = ptr.span_no_inline(body); - let span_offset = offset.span_no_inline(body); - debug!(?ptr, ?offset, ?pattern.ptr, ?pattern.offset, ?span_ptr, ?span_offset, "unchecked offset found"); - let ptr = span_ptr; - let offset = span_offset; - self.tcx.emit_node_span_lint( - UNCHECKED_POINTER_OFFSET, - self.tcx.local_def_id_to_hir_id(def_id), - offset, - crate::errors::UncheckedPtrOffset { ptr, offset }, - ); } } intravisit::walk_fn(self, kind, decl, body_id, def_id); @@ -137,64 +129,12 @@ macro_rules! template { }; } -// #[rpl_macros::pattern_def] -// fn pattern_unchecked_ptr_offset_(pcx: PatCtxt<'_>) -> PatternUncheckedPtrOffsetGeneral<'_> { -// let ptr; -// let offset; -// let pattern = rpl! { -// #[meta($T:ty)] -// fn $pattern(..) -> _ = mir! { -// #[export(ptr)] -// let $ptr: *const $T = _; -// #[export(offset)] -// let $ptr_1: *const $T = Offset(copy $ptr, _); -// } -// }; -// let fn_pat = pattern.fns.get_fn_pat(Symbol::intern("pattern")).unwrap(); - -// PatternUncheckedPtrOffsetGeneral { -// pattern, -// fn_pat, -// ptr, -// offset, -// } -// } - -// macro_rules! pattern_checked_ptr_offset { -// ($name:ident, $($cmp_expr:tt)*) => { -// #[rpl_macros::pattern_def] -// fn $name(pcx: PatCtxt<'_>) -> PatternUncheckedPtrOffsetGeneral<'_> { -// let ptr; -// let offset; -// let pattern = rpl! { -// #[meta($T:ty, $U:ty)] -// fn $pattern(..) -> _ = mir! { -// let $index: $U = _; -// #[export(ptr)] -// let $ptr: *const $T = _; -// let $cmp: bool = $($cmp_expr)*; -// #[export(offset)] -// let $ptr_1: *const $T = Offset(copy $ptr, _); -// } -// }; -// let fn_pat = pattern.fns.get_fn_pat(Symbol::intern("pattern")).unwrap(); - -// PatternUncheckedPtrOffsetGeneral { -// pattern, -// fn_pat, -// ptr, -// offset, -// } -// } -// }; -// } - template! { - pattern_unchecked_ptr_offset_ -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { - #[meta($T:ty)] + pattern_unchecked_ptr_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { + #[meta($T: ty, $U: ty)] fn $pattern(..) -> _ = mir! { #[export(len)] - let $len: usize = _; + let $len: $U = _; #[export(ptr)] let $ptr: *const $T = _; #[export(offset)] @@ -204,11 +144,11 @@ template! { } template! { - pattern_unchecked_mut_ptr_offset_ -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { - #[meta($T:ty)] + pattern_unchecked_mut_ptr_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { + #[meta($T: ty, $U: ty)] fn $pattern(..) -> _ = mir! { #[export(len)] - let $len: usize = _; + let $len: $U = _; #[export(ptr)] let $ptr: *mut $T = _; #[export(offset)] @@ -216,3 +156,91 @@ template! { } } } + +template! { + pattern_unchecked_ptr_casted_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { + #[meta($T: ty, $U1: ty, $U2: ty)] + fn $pattern(..) -> _ = mir! { + #[export(len)] + let $len1: $U1 = _; + let $len2: $U2 = copy $len1 as $U2 (IntToInt); + #[export(ptr)] + let $ptr: *const $T = _; + #[export(offset)] + let $ptr_1: *const $T = Offset(copy $ptr, copy $len2); + } + } +} + +template! { + pattern_unchecked_mut_ptr_casted_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { + #[meta($T: ty, $U1: ty, $U2: ty)] + fn $pattern(..) -> _ = mir! { + #[export(len)] + let $len1: $U1 = _; + let $len2: $U2 = copy $len1 as $U2 (IntToInt); + #[export(ptr)] + let $ptr: *mut $T = _; + #[export(offset)] + let $ptr_1: *mut $T = Offset(copy $ptr, copy $len2); + } + } +} + +template! { + pattern_unchecked_ptr_arith_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { + #[meta($T: ty, $U: ty)] + fn $pattern(..) -> _ = mir! { + #[export(len)] + let $len: $U = _; // _6 + #[export(ptr)] + let $ptr: *const $T = _; // _8 + #[export(offset)] + let $ptr_1: *const $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len); // _7 + } + } +} + +template! { + pattern_unchecked_mut_ptr_arith_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { + #[meta($T: ty, $U: ty)] + fn $pattern(..) -> _ = mir! { + #[export(len)] + let $len: $U = _; // _6 + #[export(ptr)] + let $ptr: *mut $T = _; // _8 + #[export(offset)] + let $ptr_1: *mut $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len); // _7 + } + } +} + +template! { + pattern_unchecked_ptr_casted_arith_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { + #[meta($T: ty, $U1: ty, $U2: ty)] + fn $pattern(..) -> _ = mir! { + #[export(len)] + let $len1: $U1 = _; // _2 + let $len2: $U2 = copy $len1 as $U2 (IntToInt); // _6 + #[export(ptr)] + let $ptr: *const $T = _; // _8 + #[export(offset)] + let $ptr_1: *const $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len2); // _7 + } + } +} + +template! { + pattern_unchecked_mut_ptr_casted_arith_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { + #[meta($T: ty, $U1: ty, $U2: ty)] + fn $pattern(..) -> _ = mir! { + #[export(len)] + let $len1: $U1 = _; // _2 + let $len2: $U2 = copy $len1 as $U2 (IntToInt); // _6 + #[export(ptr)] + let $ptr: *mut $T = _; // _8 + #[export(offset)] + let $ptr_1: *mut $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len2); // _7 + } + } +} diff --git a/tests/ui/cve_2020_35886/cve_2020_35886.inline.stderr b/tests/ui/cve_2020_35886/cve_2020_35886.inline.stderr new file mode 100644 index 00000000..07a7b404 --- /dev/null +++ b/tests/ui/cve_2020_35886/cve_2020_35886.inline.stderr @@ -0,0 +1,28 @@ +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35886/cve_2020_35886.rs:36:27 + | +LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35886/cve_2020_35886.rs:43:27 + | +LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + +error: aborting due to 2 previous errors + diff --git a/tests/ui/cve_2020_35886/cve_2020_35886.rs b/tests/ui/cve_2020_35886/cve_2020_35886.rs index 381b1d0c..1a435b09 100644 --- a/tests/ui/cve_2020_35886/cve_2020_35886.rs +++ b/tests/ui/cve_2020_35886/cve_2020_35886.rs @@ -1,6 +1,8 @@ //@ revisions: inline regular //@[inline] compile-flags: -Z inline-mir=true //@[regular] compile-flags: -Z inline-mir=false +//@[regular] check-pass +// FIXME: write a non-inline pattern use std::alloc::{Layout, alloc, alloc_zeroed, dealloc}; use std::ops::{Index, IndexMut, Range}; @@ -29,15 +31,17 @@ impl Array { impl Index for Array { type Output = T; - #[rpl::dump_mir(dump_cfg, dump_ddg)] + // #[rpl::dump_mir(dump_cfg, dump_ddg)] fn index<'a>(&'a self, idx: usize) -> &'a Self::Output { unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() + //~[inline]^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } } impl IndexMut for Array { fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut Self::Output { unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() + //~[inline]^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } } From 7b58d04550854f478c206041314c0eb87041ecc1 Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Thu, 3 Jul 2025 16:53:38 +0800 Subject: [PATCH 05/17] Refactoring pattern CVE-2020-35877 --- .../rpl_patterns/src/inline/cve_2020_35887.rs | 12 +++---- tests/ui/cve/cve_2020_35877/cve_2020_35877.rs | 2 +- .../cve_2020_35887.inline.stderr | 32 ++++++++++++++++++- tests/ui/cve/cve_2020_35887/cve_2020_35887.rs | 2 ++ .../cve/cve_2020_35892_3/cve_2020_35892_3.rs | 4 ++- .../cve_2020_35892_3/cve_2020_35892_3.stderr | 31 +++++++++++++++++- .../cve_2020_35886.inline.stderr | 8 +++-- 7 files changed, 79 insertions(+), 12 deletions(-) diff --git a/crates/rpl_patterns/src/inline/cve_2020_35887.rs b/crates/rpl_patterns/src/inline/cve_2020_35887.rs index ab24f207..7f30ba06 100644 --- a/crates/rpl_patterns/src/inline/cve_2020_35887.rs +++ b/crates/rpl_patterns/src/inline/cve_2020_35887.rs @@ -86,16 +86,16 @@ impl<'tcx> Visitor<'tcx> for CheckFnCtxt<'_, 'tcx> { } let ptr = matches[pattern.ptr]; let offset = matches[pattern.offset]; - let span_ptr = ptr.span_no_inline(body); - let span_offset = offset.span_no_inline(body); - debug!(?ptr, ?offset, ?pattern.ptr, ?pattern.offset, ?span_ptr, ?span_offset, "unchecked offset found"); - let ptr = span_ptr; - let offset = span_offset; + let len = matches[pattern.len]; + debug!(?ptr, ?offset, ?len, ?pattern.ptr, ?pattern.offset, ?pattern.len, "unchecked offset found"); + let ptr = ptr.span_no_inline(body); + let offset = offset.span_no_inline(body); + let len = len.span_no_inline(body); self.tcx.emit_node_span_lint( UNCHECKED_POINTER_OFFSET, self.tcx.local_def_id_to_hir_id(def_id), offset, - crate::errors::UncheckedPtrOffset { ptr, offset }, + crate::errors::UncheckedPtrPublicOffset { ptr, offset, len }, ); } } diff --git a/tests/ui/cve/cve_2020_35877/cve_2020_35877.rs b/tests/ui/cve/cve_2020_35877/cve_2020_35877.rs index 69df62df..00af5054 100644 --- a/tests/ui/cve/cve_2020_35877/cve_2020_35877.rs +++ b/tests/ui/cve/cve_2020_35877/cve_2020_35877.rs @@ -44,7 +44,7 @@ where while count > 0 { count -= 1; p = p.offset(1); - //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer + //FIXME: ~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } &*p //~^ERROR: it is unsound to dereference a pointer that is offset using an unchecked integer diff --git a/tests/ui/cve/cve_2020_35887/cve_2020_35887.inline.stderr b/tests/ui/cve/cve_2020_35887/cve_2020_35887.inline.stderr index d26c9da5..31447665 100644 --- a/tests/ui/cve/cve_2020_35887/cve_2020_35887.inline.stderr +++ b/tests/ui/cve/cve_2020_35887/cve_2020_35887.inline.stderr @@ -14,5 +14,35 @@ LL | (*(ptr.wrapping_offset(i as isize))) = template.clone(); = help: assigning to a dereferenced pointer will cause previous value to be dropped, and try using `ptr::write` instead = note: `#[deny(rpl::drop_uninit_value)]` on by default -error: aborting due to 1 previous error +error: it may be an undefined behavior to offset a pointer using a passed-in integer + --> tests/ui/cve_2020_35887/cve_2020_35887.rs:77:27 + | +LL | fn index<'a>(&'a self, idx: usize) -> &'a Self::Output { + | --- length passed in here +LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + +error: it may be an undefined behavior to offset a pointer using a passed-in integer + --> tests/ui/cve_2020_35887/cve_2020_35887.rs:84:27 + | +LL | fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut Self::Output { + | --- length passed in here +LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + +error: aborting due to 3 previous errors diff --git a/tests/ui/cve/cve_2020_35887/cve_2020_35887.rs b/tests/ui/cve/cve_2020_35887/cve_2020_35887.rs index a17d9cb8..48ace6bf 100644 --- a/tests/ui/cve/cve_2020_35887/cve_2020_35887.rs +++ b/tests/ui/cve/cve_2020_35887/cve_2020_35887.rs @@ -75,12 +75,14 @@ impl Index for Array { // #[rpl::dump_mir(dump_cfg, dump_ddg)] fn index<'a>(&'a self, idx: usize) -> &'a Self::Output { unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() + //~[inline]^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } } impl IndexMut for Array { fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut Self::Output { unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() + //~[inline]^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } } diff --git a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs index 8e445a39..28772382 100644 --- a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs +++ b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs @@ -26,7 +26,9 @@ impl Index for Slab { type Output = T; fn index(&self, index: usize) -> &Self::Output { unsafe { &(*(self.mem.offset(index as isize))) } - // FIXME: should report error + //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer + //~|HELP: check whether it's in bound before offsetting + //~|HELP: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` } } diff --git a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr index be919515..48a470f5 100644 --- a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr +++ b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr @@ -1,3 +1,32 @@ +error: it may be an undefined behavior to offset a pointer using a passed-in integer + --> tests/ui/cve_2020_35892_3/cve_2020_35892_3.rs:28:31 + | +LL | fn index(&self, index: usize) -> &Self::Output { + | ----- length passed in here +LL | unsafe { &(*(self.mem.offset(index as isize))) } + | -------- ^^^^^^^^^^^^^^^^^^^^^^ offset here + | | + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + +error: it may be an undefined behavior to offset a pointer using a passed-in integer + --> tests/ui/cve_2020_35892_3/cve_2020_35892_3.rs:49:33 + | +LL | pub fn remove(&mut self, offset: usize) -> T { + | ------ length passed in here +... +LL | elem_ptr = self.mem.offset(offset as isize); + | -------- ^^^^^^^^^^^^^^^^^^^^^^^ offset here + | | + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + error: pointer out of bound --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:53:25 | @@ -33,5 +62,5 @@ LL | | } = note: `-D rpl::generic-function-marked-inline` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(rpl::generic_function_marked_inline)]` -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/cve_2020_35886/cve_2020_35886.inline.stderr b/tests/ui/cve_2020_35886/cve_2020_35886.inline.stderr index 07a7b404..4ca2375e 100644 --- a/tests/ui/cve_2020_35886/cve_2020_35886.inline.stderr +++ b/tests/ui/cve_2020_35886/cve_2020_35886.inline.stderr @@ -1,6 +1,8 @@ -error: it is an undefined behavior to offset a pointer using an unchecked integer +error: it may be an undefined behavior to offset a pointer using a passed-in integer --> tests/ui/cve_2020_35886/cve_2020_35886.rs:36:27 | +LL | fn index<'a>(&'a self, idx: usize) -> &'a Self::Output { + | --- length passed in here LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | @@ -12,9 +14,11 @@ LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` -error: it is an undefined behavior to offset a pointer using an unchecked integer +error: it may be an undefined behavior to offset a pointer using a passed-in integer --> tests/ui/cve_2020_35886/cve_2020_35886.rs:43:27 | +LL | fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut Self::Output { + | --- length passed in here LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | From 98c214f8882b6866229e01d8eb276589a774dea6 Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Sat, 5 Jul 2025 22:33:47 +0800 Subject: [PATCH 06/17] Test adjusting lint levels --- tests/ui/utils/allow_by_arg.rs | 27 +++++++++++++++++++++++++++ tests/ui/utils/allow_by_attr.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 tests/ui/utils/allow_by_arg.rs create mode 100644 tests/ui/utils/allow_by_attr.rs diff --git a/tests/ui/utils/allow_by_arg.rs b/tests/ui/utils/allow_by_arg.rs new file mode 100644 index 00000000..02832556 --- /dev/null +++ b/tests/ui/utils/allow_by_arg.rs @@ -0,0 +1,27 @@ +//@revisions: inline normal +//@compile-flags: -A rpl::all +//@[inline]compile-flags: -Z inline-mir=true +//@[normal]compile-flags: -Z inline-mir=false +//@check-pass + +use std::cell::UnsafeCell; +use std::mem::{ManuallyDrop, transmute}; + +#[rpl::dynamic(primary_message = "This is a warning")] +fn a() {} + +fn b() { + unsafe { + let mut a = ManuallyDrop::new("1".to_owned()); + ManuallyDrop::drop(&mut a); + ManuallyDrop::drop(&mut a); + } +} + +fn c() -> &'static u8 { + thread_local! { + static VALUE: UnsafeCell = UnsafeCell::new(42); + } + // unsafe { transmute(&VALUE) } + VALUE.with(|l| unsafe { &*l.get() }) +} diff --git a/tests/ui/utils/allow_by_attr.rs b/tests/ui/utils/allow_by_attr.rs new file mode 100644 index 00000000..f0434a17 --- /dev/null +++ b/tests/ui/utils/allow_by_attr.rs @@ -0,0 +1,27 @@ +//@revisions: inline normal +//@[inline]compile-flags: -Z inline-mir=true +//@[normal]compile-flags: -Z inline-mir=false +//@check-pass +#![allow(rpl::all)] + +use std::cell::UnsafeCell; +use std::mem::{ManuallyDrop, transmute}; + +#[rpl::dynamic(primary_message = "This is a warning")] +fn a() {} + +fn b() { + unsafe { + let mut a = ManuallyDrop::new("1".to_owned()); + ManuallyDrop::drop(&mut a); + ManuallyDrop::drop(&mut a); + } +} + +fn c() -> &'static u8 { + thread_local! { + static VALUE: UnsafeCell = UnsafeCell::new(42); + } + // unsafe { transmute(&VALUE) } + VALUE.with(|l| unsafe { &*l.get() }) +} From 1a05888ccd78886a0df808d85b0e492b2826f5aa Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Tue, 8 Jul 2025 17:47:58 +0800 Subject: [PATCH 07/17] Updating pattern CVE-2020-35877 --- crates/rpl_match/src/matches/mod.rs | 1 + crates/rpl_match/src/resolve.rs | 227 ++++++++++++++++++ .../cve/cve_2019_16138/src/lib.inline.stderr | 45 +++- tests/ui/cve/cve_2020_35877/minimal.rs | 4 +- tests/ui/cve/cve_2020_35877/minimal.stderr | 2 +- .../cve_2020_35887.inline.stderr | 30 ++- .../cve_2020_35887.regular.stderr | 40 ++- tests/ui/cve/cve_2020_35887/cve_2020_35887.rs | 6 +- .../cve/cve_2020_35888/cve_2020_35888.stderr | 16 +- .../cve_2020_35892_3/cve_2020_35892_3.stderr | 37 ++- .../cve_2021_25904.inline.stderr | 28 ++- .../cve/cve_2021_25905/minimal.inline.stderr | 64 +++++ .../cve_2021_29935/simplified.inline.stderr | 13 + .../cve_2021_29941_2.inline.stderr | 16 +- .../cve_2020_35886.inline.stderr | 8 +- .../cve_2020_35886.regular.stderr | 28 +++ tests/ui/utils/dynamic.stderr | 2 +- 17 files changed, 529 insertions(+), 38 deletions(-) create mode 100644 tests/ui/cve/cve_2021_25905/minimal.inline.stderr create mode 100644 tests/ui/cve_2020_35886/cve_2020_35886.regular.stderr diff --git a/crates/rpl_match/src/matches/mod.rs b/crates/rpl_match/src/matches/mod.rs index 4a623539..4f50d2a0 100644 --- a/crates/rpl_match/src/matches/mod.rs +++ b/crates/rpl_match/src/matches/mod.rs @@ -359,6 +359,7 @@ impl StatementMatch { } #[inline] +#[instrument(level = "trace", skip(body), ret)] pub fn local_is_arg(local: mir::Local, body: &mir::Body<'_>) -> bool { local.as_usize() > 0 && local.as_usize() < body.arg_count + 1 } diff --git a/crates/rpl_match/src/resolve.rs b/crates/rpl_match/src/resolve.rs index 6c6b6a0d..b5342e9d 100644 --- a/crates/rpl_match/src/resolve.rs +++ b/crates/rpl_match/src/resolve.rs @@ -44,3 +44,230 @@ pub fn lang_item_res<'pcx>(pcx: PatCtxt<'pcx>, tcx: TyCtxt<'_>, item: LangItem) .get(item) .map(|def_id| pat::Ty::from_def(pcx, def_id, pat::GenericArgsRef(&[]))) } + +/// Resolves a def path like `std::vec::Vec`. +/// +/// Can return multiple resolutions when there are multiple versions of the same crate, e.g. +/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0. +/// +/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec` +/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. +/// +/// This function is expensive and should be used sparingly. +#[instrument(level = "trace", skip(tcx), ret)] +pub fn def_path_res(tcx: TyCtxt<'_>, path: &[Symbol], kind: PatItemKind) -> Vec { + let full_path = path; + let (base, path) = match path { + [primitive] => { + return vec![PrimTy::from_name(*primitive).map_or(Res::Err, Res::PrimTy)]; + }, + [base, path @ ..] => (base, path), + [] => return Vec::new(), + }; + + // let base_sym = Symbol::intern(base); + + let local_crate = if tcx.crate_name(LOCAL_CRATE) == *base || "crate" == base.as_str() { + Some(LOCAL_CRATE.as_def_id()) + } else { + None + }; + + let crates = find_primitive_impls(tcx, *base) + .chain(local_crate) + .map(|id| Res::Def(tcx.def_kind(id), id)) + .chain(find_crates(tcx, *base)) + .collect(); + + // trace!(?crates); + + let results = def_path_res_with_base(tcx, crates, path, kind); + if results.is_empty() { + info!(?full_path, "no results found for path"); + } + results +} + +/// Resolves a def path like `vec::Vec` with the base `std`. +/// +/// This is lighter than [`def_path_res`], and should be called with [`find_crates`] looking up +/// items from the same crate repeatedly, although should still be used sparingly. +// #[instrument(level = "trace", skip(tcx), ret)] +pub(crate) fn def_path_res_with_base( + tcx: TyCtxt<'_>, + mut base: Vec, + mut path: &[Symbol], + kind: PatItemKind, +) -> Vec { + while let [segment, rest @ ..] = path { + path = rest; + // let segment = Symbol::intern(segment); + let segment = *segment; + + base = base + .into_iter() + .filter_map(|res| res.opt_def_id()) + .flat_map(|def_id| { + let mut children = Vec::new(); + + // Some items that may be contained in an `impl`. + if matches!( + kind, + PatItemKind::Const | PatItemKind::Fn | PatItemKind::Type | PatItemKind::Variant + ) { + // When the current def_id is e.g. `struct S`, check the impl items in + // `impl S { ... }` + children.extend( + tcx.inherent_impls(def_id) + .iter() + .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)), + ); + } + + children.extend(item_children_by_name(tcx, def_id, segment)); + + children + }) + .collect(); + + // trace!(?segment, ?rest, ?base); + } + + // trace!(?base); + + base.into_iter().filter(|res| kind.match_resolve(res)).collect() +} + +// #[instrument(level = "trace", skip(tcx), ret)] +fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { + match tcx.def_kind(def_id) { + DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx + .module_children(def_id) + .iter() + .filter(|item| item.ident.name == name) + .map(|child| child.res.expect_non_local()) + .collect(), + DefKind::Impl { .. } => tcx + .associated_item_def_ids(def_id) + .iter() + .copied() + .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name) + .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)) + .collect(), + _ => Vec::new(), + } +} + +// #[instrument(level = "trace", skip(tcx), ret)] +fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec { + let hir = tcx.hir(); + + let root_mod; + let item_kind = match tcx.hir_node_by_def_id(local_id) { + Node::Crate(r#mod) => { + root_mod = ItemKind::Mod(r#mod); + &root_mod + }, + Node::Item(item) => &item.kind, + _ => return Vec::new(), + }; + + // trace!(?item_kind); + + let res = |ident: Ident, owner_id: OwnerId| { + // trace!(?ident, ?name, ?owner_id); + if ident.name == name { + let def_id = owner_id.to_def_id(); + Some(Res::Def(tcx.def_kind(def_id), def_id)) + } else { + None + } + }; + + match item_kind { + ItemKind::Mod(r#mod) => r#mod + .item_ids + .iter() + .filter_map(|&item_id| { + let item = hir.item(item_id); + match item.kind { + ItemKind::ForeignMod { abi: _, items } => { + items.iter().find_map(|item| res(item.ident, item.id.owner_id)) + }, + _ => res(item.ident, item_id.owner_id), + } + }) + .collect(), + ItemKind::Impl(r#impl) => r#impl + .items + .iter() + .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)) + .collect(), + ItemKind::Trait(.., trait_item_refs) => trait_item_refs + .iter() + .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)) + .collect(), + _ => Vec::new(), + } +} + +// #[instrument(level = "trace", skip(tcx), ret)] +fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { + if let Some(local_id) = def_id.as_local() { + local_item_children_by_name(tcx, local_id, name) + } else { + non_local_item_children_by_name(tcx, def_id, name) + } +} + +/// Finds the crates called `name`, may be multiple due to multiple major versions. +pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec { + tcx.crates(()) + .iter() + .copied() + .filter(move |&num| tcx.crate_name(num) == name) + .filter(move |&num| { + // Find crates that are + // either has been included as a part of prelude + // or directly depended by local crate + matches!(name.as_str(), "std" | "core" | "alloc") + || tcx.extern_crate(num).map(|krate| krate.is_direct()).unwrap_or(false) + }) + .map(CrateNum::as_def_id) + .map(|id| Res::Def(tcx.def_kind(id), id)) + .collect() +} + +fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator + '_ { + let ty = match name.as_str() { + "bool" => SimplifiedType::Bool, + "char" => SimplifiedType::Char, + "str" => SimplifiedType::Str, + "array" => SimplifiedType::Array, + "slice" => SimplifiedType::Slice, + // FIXME: rustdoc documents these two using just `pointer`. + // + // Maybe this is something we should do here too. + "const_ptr" => SimplifiedType::Ptr(Mutability::Not), + "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut), + "isize" => SimplifiedType::Int(IntTy::Isize), + "i8" => SimplifiedType::Int(IntTy::I8), + "i16" => SimplifiedType::Int(IntTy::I16), + "i32" => SimplifiedType::Int(IntTy::I32), + "i64" => SimplifiedType::Int(IntTy::I64), + "i128" => SimplifiedType::Int(IntTy::I128), + "usize" => SimplifiedType::Uint(UintTy::Usize), + "u8" => SimplifiedType::Uint(UintTy::U8), + "u16" => SimplifiedType::Uint(UintTy::U16), + "u32" => SimplifiedType::Uint(UintTy::U32), + "u64" => SimplifiedType::Uint(UintTy::U64), + "u128" => SimplifiedType::Uint(UintTy::U128), + "f32" => SimplifiedType::Float(FloatTy::F32), + "f64" => SimplifiedType::Float(FloatTy::F64), + _ => { + return [].iter().copied(); + }, + }; + + tcx.incoherent_impls(ty).iter().copied() +} diff --git a/tests/ui/cve/cve_2019_16138/src/lib.inline.stderr b/tests/ui/cve/cve_2019_16138/src/lib.inline.stderr index 61e56a62..03289433 100644 --- a/tests/ui/cve/cve_2019_16138/src/lib.inline.stderr +++ b/tests/ui/cve/cve_2019_16138/src/lib.inline.stderr @@ -1,3 +1,17 @@ +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2019_16138/src/lib.rs:221:77 + | +LL | ... for (dst, &pix) in chunk.iter_mut().zip(buf.iter()) { + | ^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + error: it usually isn't necessary to apply #[inline] to private functions --> tests/ui/cve/cve_2019_16138/src/lib.rs:265:13 | @@ -68,6 +82,18 @@ LL | | } = help: See https://matklad.github.io/2021/07/09/inline-in-rust.html and https://rustc-dev-guide.rust-lang.org/backend/monomorph.html = note: generic functions are always `#[inline]` (monomorphization) +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2019_16138/src/lib.rs:382:69 + | +LL | for (offset, &value) in buf[0..rl as usize].iter().enumerate() { + | ^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + error: it usually isn't necessary to apply #[inline] to private functions --> tests/ui/cve/cve_2019_16138/src/lib.rs:360:9 | @@ -118,5 +144,22 @@ LL | | } = help: See https://matklad.github.io/2021/07/09/inline-in-rust.html = note: the compiler generally makes good inline decisions about private functions -error: aborting due to 7 previous errors +error: it usually isn't necessary to apply #[inline] to private functions + --> tests/ui/cve_2019_16138/src/lib.rs:425:13 + | +LL | #[inline] + | --------- `#[inline]` here +LL | / fn rl_marker(pix: RGBE8Pixel) -> Option { +LL | | +LL | | +LL | | if pix.c == [1, 1, 1] { +... | +LL | | } + | |_____________^ `#[inline]` applied here + | + = help: See https://matklad.github.io/2021/07/09/inline-in-rust.html + = note: the compiler generally makes good inline decisions about private functions + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 10 previous errors diff --git a/tests/ui/cve/cve_2020_35877/minimal.rs b/tests/ui/cve/cve_2020_35877/minimal.rs index 2d1af696..a5a18c9d 100644 --- a/tests/ui/cve/cve_2020_35877/minimal.rs +++ b/tests/ui/cve/cve_2020_35877/minimal.rs @@ -52,9 +52,9 @@ fn vec_iter_mut(vec: &mut Vec) -> usize { } // #[rpl::dump_mir(dump_cfg, dump_ddg)] -fn slice_iter(vec: &[usize]) -> usize { +fn slice_iter(slice: &[usize]) -> usize { let mut x = 0; - for i in vec { + for i in slice { x += 1000000007 % (*i + 1); } x diff --git a/tests/ui/cve/cve_2020_35877/minimal.stderr b/tests/ui/cve/cve_2020_35877/minimal.stderr index c69c0a07..cc42822b 100644 --- a/tests/ui/cve/cve_2020_35877/minimal.stderr +++ b/tests/ui/cve/cve_2020_35877/minimal.stderr @@ -75,5 +75,5 @@ LL | unsafe { p.add(1) } = help: check whether it's in bound before offsetting = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset -error: aborting due to 7 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/cve/cve_2020_35887/cve_2020_35887.inline.stderr b/tests/ui/cve/cve_2020_35887/cve_2020_35887.inline.stderr index 31447665..fab4b35b 100644 --- a/tests/ui/cve/cve_2020_35887/cve_2020_35887.inline.stderr +++ b/tests/ui/cve/cve_2020_35887/cve_2020_35887.inline.stderr @@ -1,3 +1,17 @@ +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35887/cve_2020_35887.rs:44:24 + | +LL | (*(ptr.wrapping_offset(i as isize))) = default; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + error: dropped an possibly-uninitialized value --> tests/ui/cve/cve_2020_35887/cve_2020_35887.rs:63:17 | @@ -14,11 +28,9 @@ LL | (*(ptr.wrapping_offset(i as isize))) = template.clone(); = help: assigning to a dereferenced pointer will cause previous value to be dropped, and try using `ptr::write` instead = note: `#[deny(rpl::drop_uninit_value)]` on by default -error: it may be an undefined behavior to offset a pointer using a passed-in integer - --> tests/ui/cve_2020_35887/cve_2020_35887.rs:77:27 +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35887/cve_2020_35887.rs:79:27 | -LL | fn index<'a>(&'a self, idx: usize) -> &'a Self::Output { - | --- length passed in here LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | @@ -27,14 +39,10 @@ LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() | = help: check whether it's in bound before offsetting = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` -error: it may be an undefined behavior to offset a pointer using a passed-in integer - --> tests/ui/cve_2020_35887/cve_2020_35887.rs:84:27 +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35887/cve_2020_35887.rs:86:27 | -LL | fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut Self::Output { - | --- length passed in here LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | @@ -44,5 +52,5 @@ LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() = help: check whether it's in bound before offsetting = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/cve/cve_2020_35887/cve_2020_35887.regular.stderr b/tests/ui/cve/cve_2020_35887/cve_2020_35887.regular.stderr index 301b907c..efc4ffbb 100644 --- a/tests/ui/cve/cve_2020_35887/cve_2020_35887.regular.stderr +++ b/tests/ui/cve/cve_2020_35887/cve_2020_35887.regular.stderr @@ -1,3 +1,17 @@ +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35887/cve_2020_35887.rs:44:19 + | +LL | let ptr = unsafe { alloc(layout) as *mut T }; + | ----------------------- pointer used here +... +LL | (*(ptr.wrapping_offset(i as isize))) = default; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ offset here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + error: resulting pointer `*mut T` has a different alignment than the original alignment that the pointer was created with --> tests/ui/cve/cve_2020_35887/cve_2020_35887.rs:38:28 | @@ -23,5 +37,29 @@ LL | let ptr = unsafe { alloc(layout) as *mut T }; = note: See https://doc.rust-lang.org/std/alloc/fn.alloc.html and https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html#method.alloc = note: `#[deny(rpl::alloc_maybe_zero)]` on by default -error: aborting due to 2 previous errors +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35887/cve_2020_35887.rs:79:18 + | +LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() + | --------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35887/cve_2020_35887.rs:86:18 + | +LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() + | --------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + +error: aborting due to 5 previous errors diff --git a/tests/ui/cve/cve_2020_35887/cve_2020_35887.rs b/tests/ui/cve/cve_2020_35887/cve_2020_35887.rs index 48ace6bf..b1d57baa 100644 --- a/tests/ui/cve/cve_2020_35887/cve_2020_35887.rs +++ b/tests/ui/cve/cve_2020_35887/cve_2020_35887.rs @@ -42,6 +42,8 @@ where for i in 0..size { unsafe { (*(ptr.wrapping_offset(i as isize))) = default; + //~^ ERROR: it is an undefined behavior to offset a pointer using an unchecked integer + // FIXME: false positive } } Self { size, ptr } @@ -75,14 +77,14 @@ impl Index for Array { // #[rpl::dump_mir(dump_cfg, dump_ddg)] fn index<'a>(&'a self, idx: usize) -> &'a Self::Output { unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() - //~[inline]^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer + //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } } impl IndexMut for Array { fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut Self::Output { unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() - //~[inline]^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer + //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } } diff --git a/tests/ui/cve/cve_2020_35888/cve_2020_35888.stderr b/tests/ui/cve/cve_2020_35888/cve_2020_35888.stderr index a3ef899b..85195642 100644 --- a/tests/ui/cve/cve_2020_35888/cve_2020_35888.stderr +++ b/tests/ui/cve/cve_2020_35888/cve_2020_35888.stderr @@ -1,3 +1,17 @@ +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35888/cve_2020_35888.rs:18:24 + | +LL | (*(ptr.wrapping_offset(i as isize))) = template.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + error: dropped an possibly-uninitialized value --> tests/ui/cve/cve_2020_35888/cve_2020_35888.rs:18:17 | @@ -14,5 +28,5 @@ LL | (*(ptr.wrapping_offset(i as isize))) = template.clone(); = help: assigning to a dereferenced pointer will cause previous value to be dropped, and try using `ptr::write` instead = note: `#[deny(rpl::drop_uninit_value)]` on by default -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr index 48a470f5..300038cb 100644 --- a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr +++ b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr @@ -1,8 +1,19 @@ -error: it may be an undefined behavior to offset a pointer using a passed-in integer +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35892_3/cve_2020_35892_3.rs:16:41 + | +LL | let elem_ptr = self.mem.offset(x as isize); + | -------- ^^^^^^^^^^^^^^^^^^ offset here + | | + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + +error: it is an undefined behavior to offset a pointer using an unchecked integer --> tests/ui/cve_2020_35892_3/cve_2020_35892_3.rs:28:31 | -LL | fn index(&self, index: usize) -> &Self::Output { - | ----- length passed in here LL | unsafe { &(*(self.mem.offset(index as isize))) } | -------- ^^^^^^^^^^^^^^^^^^^^^^ offset here | | @@ -10,15 +21,10 @@ LL | unsafe { &(*(self.mem.offset(index as isize))) } | = help: check whether it's in bound before offsetting = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` -error: it may be an undefined behavior to offset a pointer using a passed-in integer +error: it is an undefined behavior to offset a pointer using an unchecked integer --> tests/ui/cve_2020_35892_3/cve_2020_35892_3.rs:49:33 | -LL | pub fn remove(&mut self, offset: usize) -> T { - | ------ length passed in here -... LL | elem_ptr = self.mem.offset(offset as isize); | -------- ^^^^^^^^^^^^^^^^^^^^^^^ offset here | | @@ -27,6 +33,17 @@ LL | elem_ptr = self.mem.offset(offset as isize); = help: check whether it's in bound before offsetting = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35892_3/cve_2020_35892_3.rs:50:38 + | +LL | last_elem_ptr = self.mem.offset(self.len as isize); + | -------- ^^^^^^^^^^^^^^^^^^^^^^^^^ offset here + | | + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + error: pointer out of bound --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:53:25 | @@ -62,5 +79,5 @@ LL | | } = note: `-D rpl::generic-function-marked-inline` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(rpl::generic_function_marked_inline)]` -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/cve/cve_2021_25904/cve_2021_25904.inline.stderr b/tests/ui/cve/cve_2021_25904/cve_2021_25904.inline.stderr index 637e1d0b..6ff74575 100644 --- a/tests/ui/cve/cve_2021_25904/cve_2021_25904.inline.stderr +++ b/tests/ui/cve/cve_2021_25904/cve_2021_25904.inline.stderr @@ -1,3 +1,29 @@ +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2021_25904/cve_2021_25904.rs:213:24 + | +LL | self.comp_info.iter() + | ^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2021_25904/cve_2021_25904.rs:450:41 + | +LL | let mut f_iter = fmt.format.iter(); + | ^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + error: it is unsound to trust pointers from passed-in iterators in a public safe function --> tests/ui/cve/cve_2021_25904/cve_2021_25904.rs:443:50 | @@ -13,5 +39,5 @@ LL | let ss = unsafe { slice::from_raw_parts(rr, hb * s_linesize = help: consider marking the function as unsafe = note: `#[deny(rpl::unvalidated_slice_from_raw_parts)]` on by default -error: aborting due to 1 previous error +error: aborting due to 3 previous errors diff --git a/tests/ui/cve/cve_2021_25905/minimal.inline.stderr b/tests/ui/cve/cve_2021_25905/minimal.inline.stderr new file mode 100644 index 00000000..b5bb106c --- /dev/null +++ b/tests/ui/cve/cve_2021_25905/minimal.inline.stderr @@ -0,0 +1,64 @@ +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2021_25905/minimal.rs:20:38 + | +LL | buf.as_mut_ptr().offset(b as isize), + | ------------ ^^^^^^^^^^^^^^^^^^ offset here + | | + | pointer used here +... +LL | cases!(Vec::new()); + | ------------------ in this macro invocation + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + = note: this error originates in the macro `cases` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2021_25905/minimal.rs:20:38 + | +LL | buf.as_mut_ptr().offset(b as isize), + | ------------ ^^^^^^^^^^^^^^^^^^ offset here + | | + | pointer used here +... +LL | cases!(vec![1, 2, 3]); + | --------------------- in this macro invocation + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: this error originates in the macro `cases` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2021_25905/minimal.rs:20:38 + | +LL | buf.as_mut_ptr().offset(b as isize), + | ------------ ^^^^^^^^^^^^^^^^^^ offset here + | | + | pointer used here +... +LL | cases!(Vec::with_capacity(0)); + | ----------------------------- in this macro invocation + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: this error originates in the macro `cases` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2021_25905/minimal.rs:20:38 + | +LL | buf.as_mut_ptr().offset(b as isize), + | ------------ ^^^^^^^^^^^^^^^^^^ offset here + | | + | pointer used here +... +LL | cases!(Vec::with_capacity(1)); + | ----------------------------- in this macro invocation + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: this error originates in the macro `cases` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/tests/ui/cve/cve_2021_29935/simplified.inline.stderr b/tests/ui/cve/cve_2021_29935/simplified.inline.stderr index a65a08f6..e9b80cac 100644 --- a/tests/ui/cve/cve_2021_29935/simplified.inline.stderr +++ b/tests/ui/cve/cve_2021_29935/simplified.inline.stderr @@ -6,6 +6,19 @@ LL | for (i, prefix) in self.prefixes.iter().enumerate() { | = note: `-D rpl::cast-slice-from-raw-parts` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(rpl::cast_slice_from_raw_parts)]` +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2021_29935/simplified.rs:239:50 + | +LL | for (i, prefix) in self.prefixes.iter().enumerate() { + | ^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` error: aborting due to 1 previous error diff --git a/tests/ui/cve/cve_2021_29941_2/cve_2021_29941_2.inline.stderr b/tests/ui/cve/cve_2021_29941_2/cve_2021_29941_2.inline.stderr index 30577920..4c9f50e6 100644 --- a/tests/ui/cve/cve_2021_29941_2/cve_2021_29941_2.inline.stderr +++ b/tests/ui/cve/cve_2021_29941_2/cve_2021_29941_2.inline.stderr @@ -10,6 +10,20 @@ LL | vec.set_len(len); = help: incorrect implementation of `std::iter::ExactSizeIterator::len` must not cause safety issues, and consider using `std::iter::TrustedLen` instead if it's stabilized = note: `#[deny(rpl::trust_exact_size_iterator)]` on by default +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2021_29941_2/cve_2021_29941_2.rs:30:17 + | +LL | let ptr = vec.as_mut_ptr(); + | ------------ pointer used here +... +LL | ptr.add(a as usize).write(i as u32); + | ^^^^^^^^^^^^^^^ offset here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + error: it is unsound to trust return value of `std::iter::ExactSizeIterator::len` and pass it to an unsafe function like `std::vec::Vec::set_len`, which may leak uninitialized memory --> tests/ui/cve/cve_2021_29941_2/cve_2021_29941_2.rs:58:13 | @@ -43,5 +57,5 @@ LL | vec.set_len(len); | = help: incorrect implementation of `std::iter::ExactSizeIterator::len` must not cause safety issues, and consider using `std::iter::TrustedLen` instead if it's stabilized -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/cve_2020_35886/cve_2020_35886.inline.stderr b/tests/ui/cve_2020_35886/cve_2020_35886.inline.stderr index 4ca2375e..07a7b404 100644 --- a/tests/ui/cve_2020_35886/cve_2020_35886.inline.stderr +++ b/tests/ui/cve_2020_35886/cve_2020_35886.inline.stderr @@ -1,8 +1,6 @@ -error: it may be an undefined behavior to offset a pointer using a passed-in integer +error: it is an undefined behavior to offset a pointer using an unchecked integer --> tests/ui/cve_2020_35886/cve_2020_35886.rs:36:27 | -LL | fn index<'a>(&'a self, idx: usize) -> &'a Self::Output { - | --- length passed in here LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | @@ -14,11 +12,9 @@ LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` -error: it may be an undefined behavior to offset a pointer using a passed-in integer +error: it is an undefined behavior to offset a pointer using an unchecked integer --> tests/ui/cve_2020_35886/cve_2020_35886.rs:43:27 | -LL | fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut Self::Output { - | --- length passed in here LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | | diff --git a/tests/ui/cve_2020_35886/cve_2020_35886.regular.stderr b/tests/ui/cve_2020_35886/cve_2020_35886.regular.stderr new file mode 100644 index 00000000..37335bf7 --- /dev/null +++ b/tests/ui/cve_2020_35886/cve_2020_35886.regular.stderr @@ -0,0 +1,28 @@ +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35886/cve_2020_35886.rs:36:18 + | +LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() + | --------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve_2020_35886/cve_2020_35886.rs:43:18 + | +LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() + | --------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + +error: aborting due to 2 previous errors + diff --git a/tests/ui/utils/dynamic.stderr b/tests/ui/utils/dynamic.stderr index 17663b03..630f393d 100644 --- a/tests/ui/utils/dynamic.stderr +++ b/tests/ui/utils/dynamic.stderr @@ -6,7 +6,7 @@ LL | fn f1() { | = help: You can use `#[rpl::dynamic]` to create a customizable lint. = note: This is a dynamic RPL pattern, which can be customized during runtime. - = note: `#[forbid(rpl::dynamic)]` on by default + = note: `#[deny(rpl::dynamic)]` on by default error: Unknown attribute key --> tests/ui/utils/dynamic.rs:15:5 From a4a14ef8fa7e795b2f54060c8b3bc31e5bae80e7 Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Tue, 29 Jul 2025 10:17:46 +0800 Subject: [PATCH 08/17] Migrate CVE-2020-35887 from old front-end --- README.md | 8 +- .../rpl_patterns/src/inline/cve_2020_35887.rs | 246 ------------------ .../CVE-2020-35887.rpl | 64 +++++ 3 files changed, 68 insertions(+), 250 deletions(-) delete mode 100644 crates/rpl_patterns/src/inline/cve_2020_35887.rs create mode 100644 docs/development/patterns-may-not-pass-parsing/CVE-2020-35887.rpl diff --git a/README.md b/README.md index 5da1fe05..21d09486 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ This is the main source code repository of RPL. It contains the toolchain and do RPL is a Rust linter which decouples the definition of rules from the detection logic. In particular, RPL consists of two primary components: -- a Domain-Specific Language (DSL) that allows developers to model/define code patterns, -- a detection engine to detect instances of these patterns. +- a Domain-Specific Language (DSL) that allows developers to model/define code patterns, +- a detection engine to detect instances of these patterns. The toolchain of RPL, which is a custom configuration of Rust compiler, enables accurate identification of code instances that demonstrate semantic equivalence to existing patterns. @@ -28,8 +28,8 @@ The toolchain of RPL, which is a custom configuration of Rust compiler, enables 3. Run RPL analysis on your Rust project: - - `RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl` (using built-in RPL pattern definitions based on inline MIR) - - `RUSTFLAGS="-Zinline-mir=false" RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl` (using built-in RPL pattern definitions based on MIR) + - `RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl` (using built-in RPL pattern definitions based on inline MIR) + - `RUSTFLAGS="-Zinline-mir=false" RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl` (using built-in RPL pattern definitions based on MIR) or `RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl -- -Zinline-mir=false` ## RPL Book diff --git a/crates/rpl_patterns/src/inline/cve_2020_35887.rs b/crates/rpl_patterns/src/inline/cve_2020_35887.rs deleted file mode 100644 index 7f30ba06..00000000 --- a/crates/rpl_patterns/src/inline/cve_2020_35887.rs +++ /dev/null @@ -1,246 +0,0 @@ -use crate::lints::UNCHECKED_POINTER_OFFSET; -use rpl_context::PatCtxt; -use rpl_mir::{CheckMirCtxt, pat}; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir}; -use rustc_middle::hir::nested_filter::All; -use rustc_middle::ty::TyCtxt; -use rustc_span::{Span, Symbol}; -use std::ops::Not; - -#[instrument(level = "info", skip_all)] -pub fn check_item(tcx: TyCtxt<'_>, pcx: PatCtxt<'_>, item_id: hir::ItemId) { - let item = tcx.hir().item(item_id); - // let def_id = item_id.owner_id.def_id; - let mut check_ctxt = CheckFnCtxt::new(tcx, pcx); - check_ctxt.visit_item(item); -} - -struct CheckFnCtxt<'pcx, 'tcx> { - tcx: TyCtxt<'tcx>, - pcx: PatCtxt<'pcx>, -} - -impl<'pcx, 'tcx> CheckFnCtxt<'pcx, 'tcx> { - fn new(tcx: TyCtxt<'tcx>, pcx: PatCtxt<'pcx>) -> Self { - Self { tcx, pcx } - } -} - -impl<'tcx> Visitor<'tcx> for CheckFnCtxt<'_, 'tcx> { - type NestedFilter = All; - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - #[instrument(level = "debug", skip_all, fields(?item.owner_id))] - fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) -> Self::Result { - match item.kind { - hir::ItemKind::Trait(hir::IsAuto::No, ..) | hir::ItemKind::Impl(_) | hir::ItemKind::Fn { .. } => {}, - _ => return, - } - intravisit::walk_item(self, item); - } - - #[instrument(level = "info", skip_all, fields(?def_id))] - fn visit_fn( - &mut self, - kind: intravisit::FnKind<'tcx>, - decl: &'tcx hir::FnDecl<'tcx>, - body_id: hir::BodyId, - _span: Span, - def_id: LocalDefId, - ) -> Self::Result { - // let attrs: Vec<_> = self - // .tcx - // .get_attrs_by_path(def_id.to_def_id(), &[Symbol::intern("rpl"), Symbol::intern("check")]) - // .collect(); - // info!("attrs: {:?}", attrs); - // if attrs.is_empty() { - // return; - // } - - if kind.header().is_none_or(|header| header.is_unsafe().not()) - && self.tcx.visibility(def_id).is_public() - && self.tcx.is_mir_available(def_id) - { - let body = self.tcx.optimized_mir(def_id); - - for pattern in [ - pattern_unchecked_ptr_offset(self.pcx), - pattern_unchecked_mut_ptr_offset(self.pcx), - pattern_unchecked_ptr_casted_offset(self.pcx), - pattern_unchecked_mut_ptr_casted_offset(self.pcx), - pattern_unchecked_ptr_arith_offset(self.pcx), - pattern_unchecked_mut_ptr_arith_offset(self.pcx), - pattern_unchecked_ptr_casted_arith_offset(self.pcx), - pattern_unchecked_mut_ptr_casted_arith_offset(self.pcx), - ] { - let matches = CheckMirCtxt::new(self.tcx, self.pcx, body, pattern.pattern, pattern.fn_pat).check(); - for matches in matches { - let len = matches[pattern.len]; - if !len.is_arg(body) { - trace!(?len, "not an argument, skipping"); - continue; - } - let ptr = matches[pattern.ptr]; - let offset = matches[pattern.offset]; - let len = matches[pattern.len]; - debug!(?ptr, ?offset, ?len, ?pattern.ptr, ?pattern.offset, ?pattern.len, "unchecked offset found"); - let ptr = ptr.span_no_inline(body); - let offset = offset.span_no_inline(body); - let len = len.span_no_inline(body); - self.tcx.emit_node_span_lint( - UNCHECKED_POINTER_OFFSET, - self.tcx.local_def_id_to_hir_id(def_id), - offset, - crate::errors::UncheckedPtrPublicOffset { ptr, offset, len }, - ); - } - } - } - intravisit::walk_fn(self, kind, decl, body_id, def_id); - } -} - -struct PatternUncheckedPtrOffsetGeneral<'pcx> { - pattern: &'pcx pat::Pattern<'pcx>, - fn_pat: &'pcx pat::Fn<'pcx>, - len: pat::Location, - ptr: pat::Location, - offset: pat::Location, -} - -macro_rules! template { - ($name:ident -> $ret:ident { $($fields:ident),* $(,)? } {$($inner:tt)*}) => { - #[rpl_macros::pattern_def] - fn $name(pcx: PatCtxt<'_>) -> $ret<'_> { - $( - let $fields; - )* - let pattern = rpl! { - $($inner)* - }; - let fn_pat = pattern.fns.get_fn_pat(Symbol::intern("pattern")).unwrap(); - - $ret { pattern, fn_pat, $($fields),* } - } - }; -} - -template! { - pattern_unchecked_ptr_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { - #[meta($T: ty, $U: ty)] - fn $pattern(..) -> _ = mir! { - #[export(len)] - let $len: $U = _; - #[export(ptr)] - let $ptr: *const $T = _; - #[export(offset)] - let $ptr_1: *const $T = Offset(copy $ptr, copy $len); - } - } -} - -template! { - pattern_unchecked_mut_ptr_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { - #[meta($T: ty, $U: ty)] - fn $pattern(..) -> _ = mir! { - #[export(len)] - let $len: $U = _; - #[export(ptr)] - let $ptr: *mut $T = _; - #[export(offset)] - let $ptr_1: *mut $T = Offset(copy $ptr, copy $len); - } - } -} - -template! { - pattern_unchecked_ptr_casted_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { - #[meta($T: ty, $U1: ty, $U2: ty)] - fn $pattern(..) -> _ = mir! { - #[export(len)] - let $len1: $U1 = _; - let $len2: $U2 = copy $len1 as $U2 (IntToInt); - #[export(ptr)] - let $ptr: *const $T = _; - #[export(offset)] - let $ptr_1: *const $T = Offset(copy $ptr, copy $len2); - } - } -} - -template! { - pattern_unchecked_mut_ptr_casted_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { - #[meta($T: ty, $U1: ty, $U2: ty)] - fn $pattern(..) -> _ = mir! { - #[export(len)] - let $len1: $U1 = _; - let $len2: $U2 = copy $len1 as $U2 (IntToInt); - #[export(ptr)] - let $ptr: *mut $T = _; - #[export(offset)] - let $ptr_1: *mut $T = Offset(copy $ptr, copy $len2); - } - } -} - -template! { - pattern_unchecked_ptr_arith_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { - #[meta($T: ty, $U: ty)] - fn $pattern(..) -> _ = mir! { - #[export(len)] - let $len: $U = _; // _6 - #[export(ptr)] - let $ptr: *const $T = _; // _8 - #[export(offset)] - let $ptr_1: *const $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len); // _7 - } - } -} - -template! { - pattern_unchecked_mut_ptr_arith_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { - #[meta($T: ty, $U: ty)] - fn $pattern(..) -> _ = mir! { - #[export(len)] - let $len: $U = _; // _6 - #[export(ptr)] - let $ptr: *mut $T = _; // _8 - #[export(offset)] - let $ptr_1: *mut $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len); // _7 - } - } -} - -template! { - pattern_unchecked_ptr_casted_arith_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { - #[meta($T: ty, $U1: ty, $U2: ty)] - fn $pattern(..) -> _ = mir! { - #[export(len)] - let $len1: $U1 = _; // _2 - let $len2: $U2 = copy $len1 as $U2 (IntToInt); // _6 - #[export(ptr)] - let $ptr: *const $T = _; // _8 - #[export(offset)] - let $ptr_1: *const $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len2); // _7 - } - } -} - -template! { - pattern_unchecked_mut_ptr_casted_arith_offset -> PatternUncheckedPtrOffsetGeneral { len, ptr, offset } { - #[meta($T: ty, $U1: ty, $U2: ty)] - fn $pattern(..) -> _ = mir! { - #[export(len)] - let $len1: $U1 = _; // _2 - let $len2: $U2 = copy $len1 as $U2 (IntToInt); // _6 - #[export(ptr)] - let $ptr: *mut $T = _; // _8 - #[export(offset)] - let $ptr_1: *mut $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len2); // _7 - } - } -} diff --git a/docs/development/patterns-may-not-pass-parsing/CVE-2020-35887.rpl b/docs/development/patterns-may-not-pass-parsing/CVE-2020-35887.rpl new file mode 100644 index 00000000..f4abbaaa --- /dev/null +++ b/docs/development/patterns-may-not-pass-parsing/CVE-2020-35887.rpl @@ -0,0 +1,64 @@ +pattern CVE-2020-35887 + +patt { + pattern_unchecked_ptr_offset[$T: ty, $U: ty] = + fn $pattern($len: $U, ..) -> _ { + 'ptr: + let $ptr: *const $T = _; + 'offset: + let $ptr_1: *const $T = Offset(copy $ptr, copy $len); + } + pattern_unchecked_mut_ptr_offset[$T: ty, $U: ty] + fn $pattern($len: $U, ..) -> _ { + 'ptr: + let $ptr: *mut $T = _; + 'offset: + let $ptr_1: *mut $T = Offset(copy $ptr, copy $len); + } + pattern_unchecked_ptr_casted_offset[$T: ty, $U1: ty, $U2: ty] + fn $pattern($len: $U, ..) -> _ { + let $len2: $U2 = copy $len as $U2 (IntToInt); + 'ptr: + let $ptr: *const $T = _; + 'offset: + let $ptr_1: *const $T = Offset(copy $ptr, copy $len2); + } + pattern_unchecked_mut_ptr_casted_offset[$T: ty, $U1: ty, $U2: ty] + fn $pattern($len: $U, ..) -> _ { + let $len2: $U2 = copy $len as $U2 (IntToInt); + 'ptr: + let $ptr: *mut $T = _; + 'offset: + let $ptr_1: *mut $T = Offset(copy $ptr, copy $len2); + } + pattern_unchecked_ptr_arith_offset[$T: ty, $U: ty] + fn $pattern($len: $U, ..) -> _ { + 'ptr: + let $ptr: *const $T = _; // _8 + 'offset: + let $ptr_1: *const $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len); // _7 + } + pattern_unchecked_mut_ptr_arith_offset[$T: ty, $U: ty] + fn $pattern($len: $U, ..) -> _ { + 'ptr: + let $ptr: *mut $T = _; // _8 + 'offset: + let $ptr_1: *mut $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len); // _7 + } + pattern_unchecked_ptr_casted_arith_offset[$T: ty, $U1: ty, $U2: ty] + fn $pattern($len: $U, ..) -> _ { + let $len2: $U2 = copy $len as $U2 (IntToInt); // _6 + 'ptr: + let $ptr: *const $T = _; // _8 + 'offset: + let $ptr_1: *const $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len2); // _7 + } + pattern_unchecked_mut_ptr_casted_arith_offset[$T: ty, $U1: ty, $U2: ty] + fn $pattern($len: $U, ..) -> _ { + let $len2: $U2 = copy $len as $U2 (IntToInt); // _6 + 'ptr: + let $ptr: *mut $T = _; // _8 + 'offset: + let $ptr_1: *mut $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len2); // _7 + } +} From e1619dd836f1aadc3df8309a4c93f140449e78ac Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Tue, 29 Jul 2025 22:13:23 +0800 Subject: [PATCH 09/17] Fix compile errors --- crates/rpl_constraints/src/konst.rs | 35 +++ crates/rpl_constraints/src/lib.rs | 3 + .../src/predicates/multiple_consts.rs | 12 +- .../src/predicates/single_const.rs | 3 +- .../src/predicates/ty_const.rs | 10 +- crates/rpl_context/src/pat/error.rs | 18 +- crates/rpl_context/src/pat/matched.rs | 3 +- crates/rpl_match/src/lib.rs | 3 +- crates/rpl_match/src/matches/artifact.rs | 6 +- crates/rpl_match/src/matches/color.rs | 41 ++-- crates/rpl_match/src/matches/mod.rs | 10 +- crates/rpl_match/src/predicate_evaluator.rs | 4 +- crates/rpl_match/src/resolve.rs | 227 ------------------ crates/rpl_match/src/statement.rs | 18 +- crates/rpl_match/src/ty.rs | 109 +++------ crates/rpl_meta/src/lib.rs | 9 + tests/ui/utils/dynamic.rs | 2 +- 17 files changed, 149 insertions(+), 364 deletions(-) create mode 100644 crates/rpl_constraints/src/konst.rs diff --git a/crates/rpl_constraints/src/konst.rs b/crates/rpl_constraints/src/konst.rs new file mode 100644 index 00000000..706519d8 --- /dev/null +++ b/crates/rpl_constraints/src/konst.rs @@ -0,0 +1,35 @@ +use derive_more::{Debug, Display}; +use rustc_const_eval::interpret::Scalar; +use rustc_middle::mir; +use rustc_middle::ty::{self, ScalarInt, TyCtxt, TypingEnv}; + +#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Const<'tcx> { + #[debug("{_0:?}")] + #[display("{_0}")] + MIR(mir::Const<'tcx>), + #[debug("{_0:?}")] + #[display("{_0}")] + Param(ty::ParamConst), +} + +impl<'tcx> Const<'tcx> { + pub fn try_eval_target_usize(self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> Option { + match self { + Self::MIR(konst) => Some(konst.eval_target_usize(tcx, typing_env)), + Self::Param(_) => None, + } + } + pub fn try_eval_scalar(self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> Option { + match self { + Self::MIR(konst) => konst.try_eval_scalar(tcx, typing_env), + Self::Param(_) => None, + } + } + pub fn try_eval_scalar_int(self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> Option { + match self { + Self::MIR(konst) => konst.try_eval_scalar_int(tcx, typing_env), + Self::Param(_) => None, + } + } +} diff --git a/crates/rpl_constraints/src/lib.rs b/crates/rpl_constraints/src/lib.rs index 697e399c..cffabb97 100644 --- a/crates/rpl_constraints/src/lib.rs +++ b/crates/rpl_constraints/src/lib.rs @@ -5,6 +5,7 @@ #![feature(box_patterns)] extern crate rustc_abi; +extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; @@ -26,6 +27,7 @@ extern crate either; use std::ops::Deref; use attributes::FnAttr; +pub use konst::Const; use predicates::PredicateConjunction; use rpl_parser::generics::Choice2; use rpl_parser::pairs; @@ -33,6 +35,7 @@ use rpl_parser::pairs; use crate::predicates::PredicateError; pub mod attributes; +mod konst; pub mod predicates; pub mod tribool; diff --git a/crates/rpl_constraints/src/predicates/multiple_consts.rs b/crates/rpl_constraints/src/predicates/multiple_consts.rs index 5fbd3e6b..2fb3b4f5 100644 --- a/crates/rpl_constraints/src/predicates/multiple_consts.rs +++ b/crates/rpl_constraints/src/predicates/multiple_consts.rs @@ -1,14 +1,18 @@ -use rustc_middle::mir::{self}; use rustc_middle::ty::{self, TyCtxt}; +use crate::Const; + // FIX: consider a more general way for error handling -pub type MultipleConstsPredsFnPtr = for<'tcx> fn(TyCtxt<'tcx>, ty::TypingEnv<'tcx>, Vec>) -> bool; +pub type MultipleConstsPredsFnPtr = for<'tcx> fn(TyCtxt<'tcx>, ty::TypingEnv<'tcx>, Vec>) -> bool; /// Check if those constants are in a strictly increasing order #[instrument(level = "debug", skip(tcx), ret)] -pub fn usize_lt<'tcx>(tcx: TyCtxt<'tcx>, _: ty::TypingEnv<'tcx>, consts: Vec>) -> bool { +pub fn usize_lt<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, consts: Vec>) -> bool { consts.windows(2).all(|w| { - if let (Some(c1), Some(c2)) = (w[0].try_to_scalar_int(), w[1].try_to_scalar_int()) { + if let (Some(c1), Some(c2)) = ( + w[0].try_eval_scalar_int(tcx, typing_env), + w[1].try_eval_scalar_int(tcx, typing_env), + ) { let c1 = c1.to_target_usize(tcx); let c2 = c2.to_target_usize(tcx); c1 < c2 diff --git a/crates/rpl_constraints/src/predicates/single_const.rs b/crates/rpl_constraints/src/predicates/single_const.rs index e0232041..e74d4b19 100644 --- a/crates/rpl_constraints/src/predicates/single_const.rs +++ b/crates/rpl_constraints/src/predicates/single_const.rs @@ -1,6 +1,7 @@ -use rustc_middle::mir::Const; use rustc_middle::ty::{self, TyCtxt}; +use crate::Const; + pub type SingleConstPredsFnPtr = for<'tcx> fn(TyCtxt<'tcx>, ty::TypingEnv<'tcx>, Const<'tcx>) -> bool; /// A predicate that checks if a type is a null pointer. diff --git a/crates/rpl_constraints/src/predicates/ty_const.rs b/crates/rpl_constraints/src/predicates/ty_const.rs index 6bdd8ccf..63203518 100644 --- a/crates/rpl_constraints/src/predicates/ty_const.rs +++ b/crates/rpl_constraints/src/predicates/ty_const.rs @@ -1,8 +1,10 @@ use rustc_middle::mir; use rustc_middle::ty::{self, Ty, TyCtxt}; +use crate::Const; + pub type TyConstPredsFnPtr = - for<'tcx> fn(TyCtxt<'tcx>, body: &mir::Body<'tcx>, ty::TypingEnv<'tcx>, Ty<'tcx>, mir::Const<'tcx>) -> bool; + for<'tcx> fn(TyCtxt<'tcx>, body: &mir::Body<'tcx>, ty::TypingEnv<'tcx>, Ty<'tcx>, Const<'tcx>) -> bool; /// Check if `alignment` is enough for the given type `ty`. #[instrument(level = "debug", skip(tcx), ret)] @@ -11,7 +13,7 @@ pub fn maybe_misaligned<'tcx>( body: &mir::Body<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, - alignment: mir::Const<'tcx>, + alignment: Const<'tcx>, ) -> bool { let typing_env = ty::TypingEnv::post_analysis(tcx, body.source.def_id()); match ty.kind() { @@ -22,7 +24,9 @@ pub fn maybe_misaligned<'tcx>( ty::TyKind::Foreign(_) => true, _ => { let layout = tcx.layout_of(typing_env.as_query_input(ty)).unwrap(); - alignment.eval_target_usize(tcx, typing_env) < layout.align.abi.bytes() + alignment + .try_eval_target_usize(tcx, typing_env) + .is_none_or(|alignment| alignment < layout.align.abi.bytes()) }, } } diff --git a/crates/rpl_context/src/pat/error.rs b/crates/rpl_context/src/pat/error.rs index 4736e51e..c7ed8df9 100644 --- a/crates/rpl_context/src/pat/error.rs +++ b/crates/rpl_context/src/pat/error.rs @@ -9,8 +9,8 @@ // )] use derive_more::{Debug, Display}; -use rpl_meta::collect_elems_separated_by_comma; use rpl_meta::symbol_table::{DiagSymbolTable, MetaVariableType, NonLocalMetaSymTab, WithPath}; +use rpl_meta::{DYNAMIC, collect_elems_separated_by_comma}; use rpl_parser::generics::Choice2; use rpl_parser::pairs::diagMessageInner; use rpl_parser::{SpanWrapper, pairs}; @@ -77,12 +77,6 @@ impl LintDiagnostic<'_, ()> for Box { } } -const LINT: Lint = Lint { - name: "RPL::DYNAMIC", - desc: "dynamic RPL pattern", - ..Lint::default_fields_for_macro() -}; - impl DynamicError { // const fn attr_error(span: Span) -> DynamicError { // DynamicError { @@ -102,7 +96,7 @@ impl DynamicError { )], helps: Vec::new(), suggestions: Vec::new(), - lint: &LINT, + lint: &DYNAMIC, } } fn missing_primary_message_error(attr: &rustc_hir::Attribute) -> Self { @@ -112,7 +106,7 @@ impl DynamicError { notes: Vec::new(), helps: Vec::new(), suggestions: Vec::new(), - lint: &LINT, + lint: &DYNAMIC, } } fn item_to_value_str(item: &rustc_ast::MetaItemInner) -> Result> { @@ -125,7 +119,7 @@ impl DynamicError { notes: Vec::new(), helps: Vec::new(), suggestions: Vec::new(), - lint: &LINT, + lint: &DYNAMIC, } .into() }) @@ -137,7 +131,7 @@ impl DynamicError { notes: Vec::new(), helps: Vec::new(), suggestions: Vec::new(), - lint: &LINT, + lint: &DYNAMIC, } .into() } @@ -195,7 +189,7 @@ impl DynamicError { notes, helps, suggestions: Vec::new(), - lint: &LINT, + lint: &DYNAMIC, } .into()) } diff --git a/crates/rpl_context/src/pat/matched.rs b/crates/rpl_context/src/pat/matched.rs index 57d529b6..577a4e3c 100644 --- a/crates/rpl_context/src/pat/matched.rs +++ b/crates/rpl_context/src/pat/matched.rs @@ -1,13 +1,14 @@ use core::fmt; use std::collections::HashMap; +use rpl_constraints::Const; use rpl_meta::collect_elems_separated_by_comma; use rpl_parser::generics::{Choice2, Choice3}; use rpl_parser::pairs; use rustc_errors::MultiSpan; use rustc_hir::FnDecl; use rustc_index::IndexVec; -use rustc_middle::mir::{Body, Const, PlaceRef}; +use rustc_middle::mir::{Body, PlaceRef}; use rustc_middle::ty::Ty; use rustc_span::{Span, Symbol}; diff --git a/crates/rpl_match/src/lib.rs b/crates/rpl_match/src/lib.rs index 6327aafc..58390eb6 100644 --- a/crates/rpl_match/src/lib.rs +++ b/crates/rpl_match/src/lib.rs @@ -9,6 +9,7 @@ #![feature(iter_chain)] #![feature(iterator_try_collect)] #![feature(cell_update)] +#![warn(unused_qualifications)] extern crate either; extern crate rustc_abi; @@ -46,4 +47,4 @@ pub use adt::{AdtMatch, Candidates, MatchAdtCtxt}; pub use counted::CountedMatch; pub use fns::MatchFnCtxt; pub use place::MatchPlaceCtxt; -pub use ty::{Const, MatchTyCtxt, TryCmpAs}; +pub use ty::{MatchTyCtxt, TryCmpAs}; diff --git a/crates/rpl_match/src/matches/artifact.rs b/crates/rpl_match/src/matches/artifact.rs index 97cba9d8..61a43052 100644 --- a/crates/rpl_match/src/matches/artifact.rs +++ b/crates/rpl_match/src/matches/artifact.rs @@ -2,11 +2,11 @@ use rpl_constraints::attributes::ExtraSpan; use rpl_context::pat::{MatchedMap, Spanned}; use rustc_hir::FnDecl; use rustc_index::IndexVec; -use rustc_middle::mir::{Body, Const, Local, PlaceRef}; +use rustc_middle::mir::{Body, Local, PlaceRef}; use rustc_middle::ty::Ty; use rustc_span::{Span, Symbol}; -use super::{Matched, StatementMatch, pat}; +use super::{Const, Matched, StatementMatch, pat}; /// A normalized version of [`Spanned`]. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -159,7 +159,7 @@ impl<'tcx> NormalizedMatched<'tcx> { } impl<'tcx> pat::Matched<'tcx> for NormalizedMatched<'tcx> { - fn span(&self, body: &rustc_middle::mir::Body<'_>, decl: &FnDecl<'tcx>, name: &str) -> Span { + fn span(&self, body: &Body<'_>, decl: &FnDecl<'tcx>, name: &str) -> Span { let labels = &self.extra; let i = labels .binary_search_by_key(&Symbol::intern(name), |(label, _)| *label) diff --git a/crates/rpl_match/src/matches/color.rs b/crates/rpl_match/src/matches/color.rs index 7eb85ca4..7ef58c3e 100644 --- a/crates/rpl_match/src/matches/color.rs +++ b/crates/rpl_match/src/matches/color.rs @@ -1,6 +1,7 @@ //! Check if the pattern statement matches MIR statement, //! A.K.A. if we're using building blocks with the right color. +use rpl_constraints::Const; use rustc_middle::{mir, ty}; use crate::matches::MatchCtxt; @@ -41,11 +42,11 @@ impl<'pcx, 'tcx> MatchStatement<'pcx, 'tcx> for MatchCtxt<'_, 'pcx, 'tcx> { self.cx.ty.pcx } - fn tcx(&self) -> rustc_middle::ty::TyCtxt<'tcx> { + fn tcx(&self) -> ty::TyCtxt<'tcx> { self.cx.ty.tcx } - fn typing_env(&self) -> rustc_middle::ty::TypingEnv<'tcx> { + fn typing_env(&self) -> ty::TypingEnv<'tcx> { self.cx.ty.typing_env } @@ -77,11 +78,11 @@ impl<'pcx, 'tcx> MatchTy<'pcx, 'tcx> for MatchCtxt<'_, 'pcx, 'tcx> { fn pcx(&self) -> rpl_context::PatCtxt<'pcx> { self.cx.ty.pcx } - fn tcx(&self) -> rustc_middle::ty::TyCtxt<'tcx> { + fn tcx(&self) -> ty::TyCtxt<'tcx> { self.cx.ty.tcx } - fn typing_env(&self) -> rustc_middle::ty::TypingEnv<'tcx> { + fn typing_env(&self) -> ty::TypingEnv<'tcx> { self.cx.ty.typing_env } @@ -89,27 +90,33 @@ impl<'pcx, 'tcx> MatchTy<'pcx, 'tcx> for MatchCtxt<'_, 'pcx, 'tcx> { self.cx.self_ty } - fn match_ty_var(&self, ty_var: pat::TyVar, ty: rustc_middle::ty::Ty<'tcx>) -> bool { + fn match_ty_var(&self, ty_var: pat::TyVar, ty: ty::Ty<'tcx>) -> bool { self.matching.ty_vars[ty_var.idx].force_get_matched() == ty } #[instrument(level = "trace", skip(self), ret)] - fn match_ty_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: rustc_middle::ty::Const<'tcx>) -> bool { - let konst_matched = self.matching.const_vars[const_var.idx].force_get_matched(); - match (konst_matched, konst.kind()) { - (mir::Const::Ty(_, konst_matched), _) => return konst_matched == konst, - (mir::Const::Val(value, ty), ty::ConstKind::Value(konst_value)) => { - return konst_value.ty == ty && value == self.cx.ty.tcx.valtree_to_const_val(konst_value); + fn match_ty_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: ty::Const<'tcx>) -> bool { + match konst.kind() { + ty::ConstKind::Param(param) => { + let ty = param.find_ty_from_env(self.cx.typing_env().param_env); + self.match_ty(const_var.ty, ty) && { + // We can't convert a const generic param into a `mir::Const` + self.matching.const_vars[const_var.idx].force_get_matched() == Const::Param(param) + } }, - _ => (), + ty::ConstKind::Value(value) => { + self.match_ty(const_var.ty, value.ty) && { + let const_value = self.cx.tcx().valtree_to_const_val(value); + self.matching.const_vars[const_var.idx].force_get_matched() + == Const::MIR(mir::Const::from_value(const_value, value.ty)) + } + }, + _ => false, } - // FIXME: handle constants better - info!("expected a type constant, got {:?}", konst_matched); - false } - fn match_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: mir::Const<'tcx>) -> bool { - self.matching.const_vars[const_var.idx].force_get_matched() == konst + fn match_mir_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: mir::Const<'tcx>) -> bool { + self.matching.const_vars[const_var.idx].force_get_matched() == Const::MIR(konst) } fn match_adt_matches(&self, pat: rustc_span::Symbol, adt_match: crate::AdtMatch<'tcx>) -> bool { diff --git a/crates/rpl_match/src/matches/mod.rs b/crates/rpl_match/src/matches/mod.rs index 4f50d2a0..0ced5108 100644 --- a/crates/rpl_match/src/matches/mod.rs +++ b/crates/rpl_match/src/matches/mod.rs @@ -2,17 +2,17 @@ use std::cell::Cell; use std::fmt; use std::ops::Index; +use rpl_constraints::Const; use rpl_constraints::attributes::ExtraSpan; use rpl_context::pat::{LabelMap, Spanned}; -use rpl_match::{Const, CountedMatch}; use rpl_mir_graph::TerminatorEdges; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::FnDecl; use rustc_index::bit_set::MixedBitSet; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext}; -use rustc_middle::mir::{self, Const, HasLocalDecls, PlaceRef}; +use rustc_middle::mir::visit::PlaceContext; +use rustc_middle::mir::{self, PlaceRef}; use rustc_middle::ty::Ty; use rustc_span::{Span, Symbol}; @@ -61,7 +61,7 @@ impl Matched<'_> { } } - fn span_spanned<'tcx>(&self, spanned: Spanned, body: &rustc_middle::mir::Body<'tcx>, decl: &FnDecl<'tcx>) -> Span { + fn span_spanned<'tcx>(&self, spanned: Spanned, body: &mir::Body<'tcx>, decl: &FnDecl<'tcx>) -> Span { match spanned { Spanned::Location(location) => self[location].span_no_inline(body), Spanned::Local(local) => body.local_decls[self[local]].source_info.span, @@ -76,7 +76,7 @@ impl Matched<'_> { pub struct MatchedWithLabelMap<'a, 'tcx>(pub &'a LabelMap, pub &'a Matched<'tcx>, pub &'a ExtraSpan<'tcx>); impl<'tcx> pat::Matched<'tcx> for MatchedWithLabelMap<'_, 'tcx> { - fn span(&self, body: &rustc_middle::mir::Body<'tcx>, decl: &FnDecl<'tcx>, name: &str) -> Span { + fn span(&self, body: &mir::Body<'tcx>, decl: &FnDecl<'tcx>, name: &str) -> Span { let MatchedWithLabelMap(labels, matched, attr) = self; let name = Symbol::intern(name); labels diff --git a/crates/rpl_match/src/predicate_evaluator.rs b/crates/rpl_match/src/predicate_evaluator.rs index 8c13350b..57d699de 100644 --- a/crates/rpl_match/src/predicate_evaluator.rs +++ b/crates/rpl_match/src/predicate_evaluator.rs @@ -1,10 +1,10 @@ -use rpl_constraints::Constraints; use rpl_constraints::predicates::{ BodyInfoCache, PredicateArg, PredicateClause, PredicateConjunction, PredicateKind, PredicateTerm, }; +use rpl_constraints::{Const, Constraints}; use rpl_context::pat::{self, ConstVarIdx, LabelMap, PlaceVarIdx, Spanned, TyVarIdx}; use rpl_meta::symbol_table::MetaVariable; -use rustc_middle::mir::{self, Const, PlaceRef}; +use rustc_middle::mir::{self, PlaceRef}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Symbol; diff --git a/crates/rpl_match/src/resolve.rs b/crates/rpl_match/src/resolve.rs index b5342e9d..6c6b6a0d 100644 --- a/crates/rpl_match/src/resolve.rs +++ b/crates/rpl_match/src/resolve.rs @@ -44,230 +44,3 @@ pub fn lang_item_res<'pcx>(pcx: PatCtxt<'pcx>, tcx: TyCtxt<'_>, item: LangItem) .get(item) .map(|def_id| pat::Ty::from_def(pcx, def_id, pat::GenericArgsRef(&[]))) } - -/// Resolves a def path like `std::vec::Vec`. -/// -/// Can return multiple resolutions when there are multiple versions of the same crate, e.g. -/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0. -/// -/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec` -/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`]. -/// -/// This function is expensive and should be used sparingly. -#[instrument(level = "trace", skip(tcx), ret)] -pub fn def_path_res(tcx: TyCtxt<'_>, path: &[Symbol], kind: PatItemKind) -> Vec { - let full_path = path; - let (base, path) = match path { - [primitive] => { - return vec![PrimTy::from_name(*primitive).map_or(Res::Err, Res::PrimTy)]; - }, - [base, path @ ..] => (base, path), - [] => return Vec::new(), - }; - - // let base_sym = Symbol::intern(base); - - let local_crate = if tcx.crate_name(LOCAL_CRATE) == *base || "crate" == base.as_str() { - Some(LOCAL_CRATE.as_def_id()) - } else { - None - }; - - let crates = find_primitive_impls(tcx, *base) - .chain(local_crate) - .map(|id| Res::Def(tcx.def_kind(id), id)) - .chain(find_crates(tcx, *base)) - .collect(); - - // trace!(?crates); - - let results = def_path_res_with_base(tcx, crates, path, kind); - if results.is_empty() { - info!(?full_path, "no results found for path"); - } - results -} - -/// Resolves a def path like `vec::Vec` with the base `std`. -/// -/// This is lighter than [`def_path_res`], and should be called with [`find_crates`] looking up -/// items from the same crate repeatedly, although should still be used sparingly. -// #[instrument(level = "trace", skip(tcx), ret)] -pub(crate) fn def_path_res_with_base( - tcx: TyCtxt<'_>, - mut base: Vec, - mut path: &[Symbol], - kind: PatItemKind, -) -> Vec { - while let [segment, rest @ ..] = path { - path = rest; - // let segment = Symbol::intern(segment); - let segment = *segment; - - base = base - .into_iter() - .filter_map(|res| res.opt_def_id()) - .flat_map(|def_id| { - let mut children = Vec::new(); - - // Some items that may be contained in an `impl`. - if matches!( - kind, - PatItemKind::Const | PatItemKind::Fn | PatItemKind::Type | PatItemKind::Variant - ) { - // When the current def_id is e.g. `struct S`, check the impl items in - // `impl S { ... }` - children.extend( - tcx.inherent_impls(def_id) - .iter() - .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment)), - ); - } - - children.extend(item_children_by_name(tcx, def_id, segment)); - - children - }) - .collect(); - - // trace!(?segment, ?rest, ?base); - } - - // trace!(?base); - - base.into_iter().filter(|res| kind.match_resolve(res)).collect() -} - -// #[instrument(level = "trace", skip(tcx), ret)] -fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { - match tcx.def_kind(def_id) { - DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx - .module_children(def_id) - .iter() - .filter(|item| item.ident.name == name) - .map(|child| child.res.expect_non_local()) - .collect(), - DefKind::Impl { .. } => tcx - .associated_item_def_ids(def_id) - .iter() - .copied() - .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name) - .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)) - .collect(), - _ => Vec::new(), - } -} - -// #[instrument(level = "trace", skip(tcx), ret)] -fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec { - let hir = tcx.hir(); - - let root_mod; - let item_kind = match tcx.hir_node_by_def_id(local_id) { - Node::Crate(r#mod) => { - root_mod = ItemKind::Mod(r#mod); - &root_mod - }, - Node::Item(item) => &item.kind, - _ => return Vec::new(), - }; - - // trace!(?item_kind); - - let res = |ident: Ident, owner_id: OwnerId| { - // trace!(?ident, ?name, ?owner_id); - if ident.name == name { - let def_id = owner_id.to_def_id(); - Some(Res::Def(tcx.def_kind(def_id), def_id)) - } else { - None - } - }; - - match item_kind { - ItemKind::Mod(r#mod) => r#mod - .item_ids - .iter() - .filter_map(|&item_id| { - let item = hir.item(item_id); - match item.kind { - ItemKind::ForeignMod { abi: _, items } => { - items.iter().find_map(|item| res(item.ident, item.id.owner_id)) - }, - _ => res(item.ident, item_id.owner_id), - } - }) - .collect(), - ItemKind::Impl(r#impl) => r#impl - .items - .iter() - .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)) - .collect(), - ItemKind::Trait(.., trait_item_refs) => trait_item_refs - .iter() - .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)) - .collect(), - _ => Vec::new(), - } -} - -// #[instrument(level = "trace", skip(tcx), ret)] -fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec { - if let Some(local_id) = def_id.as_local() { - local_item_children_by_name(tcx, local_id, name) - } else { - non_local_item_children_by_name(tcx, def_id, name) - } -} - -/// Finds the crates called `name`, may be multiple due to multiple major versions. -pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec { - tcx.crates(()) - .iter() - .copied() - .filter(move |&num| tcx.crate_name(num) == name) - .filter(move |&num| { - // Find crates that are - // either has been included as a part of prelude - // or directly depended by local crate - matches!(name.as_str(), "std" | "core" | "alloc") - || tcx.extern_crate(num).map(|krate| krate.is_direct()).unwrap_or(false) - }) - .map(CrateNum::as_def_id) - .map(|id| Res::Def(tcx.def_kind(id), id)) - .collect() -} - -fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator + '_ { - let ty = match name.as_str() { - "bool" => SimplifiedType::Bool, - "char" => SimplifiedType::Char, - "str" => SimplifiedType::Str, - "array" => SimplifiedType::Array, - "slice" => SimplifiedType::Slice, - // FIXME: rustdoc documents these two using just `pointer`. - // - // Maybe this is something we should do here too. - "const_ptr" => SimplifiedType::Ptr(Mutability::Not), - "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut), - "isize" => SimplifiedType::Int(IntTy::Isize), - "i8" => SimplifiedType::Int(IntTy::I8), - "i16" => SimplifiedType::Int(IntTy::I16), - "i32" => SimplifiedType::Int(IntTy::I32), - "i64" => SimplifiedType::Int(IntTy::I64), - "i128" => SimplifiedType::Int(IntTy::I128), - "usize" => SimplifiedType::Uint(UintTy::Usize), - "u8" => SimplifiedType::Uint(UintTy::U8), - "u16" => SimplifiedType::Uint(UintTy::U16), - "u32" => SimplifiedType::Uint(UintTy::U32), - "u64" => SimplifiedType::Uint(UintTy::U64), - "u128" => SimplifiedType::Uint(UintTy::U128), - "f32" => SimplifiedType::Float(FloatTy::F32), - "f64" => SimplifiedType::Float(FloatTy::F64), - _ => { - return [].iter().copied(); - }, - }; - - tcx.incoherent_impls(ty).iter().copied() -} diff --git a/crates/rpl_match/src/statement.rs b/crates/rpl_match/src/statement.rs index 3e4ce944..99b8f412 100644 --- a/crates/rpl_match/src/statement.rs +++ b/crates/rpl_match/src/statement.rs @@ -1,6 +1,5 @@ use std::iter::zip; -pub use matches::{Matched, StatementMatch, local_is_arg}; use rpl_context::PatCtxt; pub use rpl_context::pat; use rpl_mir_graph::TerminatorEdges; @@ -326,7 +325,7 @@ pub(crate) trait MatchStatement<'pcx, 'tcx> { (pat::Rvalue::Any, _) => true, (pat::Rvalue::Use(operand_pat), mir::Rvalue::Use(operand)) => self.match_operand(operand_pat, operand), (&pat::Rvalue::Repeat(ref operand_pat, konst_pat), &mir::Rvalue::Repeat(ref operand, konst)) => { - self.match_operand(operand_pat, operand) && self.ty().match_const(konst_pat, konst) + self.match_operand(operand_pat, operand) && self.ty().match_ty_const(konst_pat, konst) }, ( &pat::Rvalue::Ref(region_pat, borrow_kind_pat, place_pat), @@ -337,9 +336,9 @@ pub(crate) trait MatchStatement<'pcx, 'tcx> { // FIXME: #[allow(clippy::match_like_matches_macro)] #[allow(clippy::match_like_matches_macro)] let is_borrow_kind_equal: bool = match (borrow_kind_pat, borrow_kind) { - (rustc_middle::mir::BorrowKind::Shared, rustc_middle::mir::BorrowKind::Shared) - | (rustc_middle::mir::BorrowKind::Mut { .. }, rustc_middle::mir::BorrowKind::Mut { .. }) - | (rustc_middle::mir::BorrowKind::Fake(_), rustc_middle::mir::BorrowKind::Fake(_)) => true, + (mir::BorrowKind::Shared, mir::BorrowKind::Shared) + | (mir::BorrowKind::Mut { .. }, mir::BorrowKind::Mut { .. }) + | (mir::BorrowKind::Fake(_), mir::BorrowKind::Fake(_)) => true, _ => false, }; self.ty().match_region(region_pat, region) && is_borrow_kind_equal && self.match_place(place_pat, place) @@ -459,14 +458,13 @@ pub(crate) trait MatchStatement<'pcx, 'tcx> { fn match_operands(&self, operands_pat: &[pat::Operand<'pcx>], operands: &[mir::Operand<'tcx>]) -> bool { operands_pat.len() == operands.len() - && core::iter::zip(operands_pat, operands) - .all(|(operand_pat, operand)| self.match_operand(operand_pat, operand)) + && zip(operands_pat, operands).all(|(operand_pat, operand)| self.match_operand(operand_pat, operand)) } #[instrument(level = "trace", skip(self), ret)] fn match_const_operand(&self, pat: &pat::ConstOperand<'pcx>, konst: mir::Const<'tcx>) -> bool { let matched = match (pat, konst) { - (&pat::ConstOperand::ConstVar(const_var), konst) => self.ty().match_const_var(const_var, konst), + (&pat::ConstOperand::ConstVar(const_var), konst) => self.ty().match_mir_const_var(const_var, konst), (&pat::ConstOperand::ScalarInt(value_pat), mir::Const::Val(mir::ConstValue::Scalar(value), ty)) => { (match (value_pat.ty, *ty.kind()) { (pat::IntTy::NegInt(ty_pat), ty::Int(ty)) => ty_pat == ty, @@ -756,7 +754,7 @@ pub(crate) trait MatchStatement<'pcx, 'tcx> { return false; } pat.projection.len() == place.projection.len() - && std::iter::zip( + && zip( iter_place_pat_proj_and_ty(self.pat(), pat, self.get_place_ty_from_base(pat.base)), iter_place_proj_and_ty(self.body(), self.tcx(), place), ) @@ -789,7 +787,7 @@ pub(crate) trait MatchStatement<'pcx, 'tcx> { fn unmatch_place_ref(&self, pat: pat::Place<'pcx>, place: mir::PlaceRef<'tcx>) { use mir::ProjectionElem::*; - std::iter::zip( + zip( iter_place_pat_proj_and_ty(self.pat(), pat, self.get_place_ty_from_base(pat.base)), iter_place_proj_and_ty(self.body(), self.tcx(), place), ) diff --git a/crates/rpl_match/src/ty.rs b/crates/rpl_match/src/ty.rs index f59df9a3..a03f72d9 100644 --- a/crates/rpl_match/src/ty.rs +++ b/crates/rpl_match/src/ty.rs @@ -2,8 +2,7 @@ use std::cell::RefCell; use std::cmp::Ordering; use std::iter::zip; -use derive_more::derive::{Debug, Display}; -use either::Either; +use rpl_constraints::Const; use rpl_constraints::predicates::{PredicateArg, PredicateKind}; use rpl_context::{PatCtxt, pat}; use rpl_resolve::{PatItemKind, def_path_res}; @@ -21,25 +20,6 @@ use rustc_span::symbol::kw; use crate::resolve::{lang_item_res, ty_res}; use crate::{AdtMatch, Candidates, MatchAdtCtxt}; -#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Const<'tcx> { - #[debug("{_0:?}")] - #[display("{_0}")] - MIR(mir::Const<'tcx>), - #[debug("{_0:?}")] - #[display("{_0}")] - Param(ty::ParamConst), -} - -impl<'tcx> Const<'tcx> { - pub fn try_eval_target_usize(self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> Option { - match self { - Self::MIR(konst) => Some(konst.eval_target_usize(tcx, typing_env)), - Self::Param(_) => None, - } - } -} - /// FIXME: this generic parameter is not as convenient as intended, as `self.try_cmp_as(other, tcx, /// typing_env)` does not provide a way to specify `T` pub trait TryCmpAs<'tcx, T>: Copy { @@ -72,9 +52,9 @@ pub struct MatchTyCtxt<'pcx, 'tcx> { pub tcx: TyCtxt<'tcx>, pub pcx: PatCtxt<'pcx>, pub pat: &'pcx pat::RustItems<'pcx>, - pub typing_env: ty::TypingEnv<'tcx>, + pub typing_env: TypingEnv<'tcx>, pub self_ty: Option>, - pub const_vars: IndexVec>>>, + pub const_vars: IndexVec>>>, pub ty_vars: IndexVec>>>, pub adt_matches: RefCell>>>, } @@ -84,7 +64,7 @@ impl<'pcx, 'tcx> MatchTyCtxt<'pcx, 'tcx> { pub fn new( tcx: TyCtxt<'tcx>, pcx: PatCtxt<'pcx>, - typing_env: ty::TypingEnv<'tcx>, + typing_env: TypingEnv<'tcx>, self_ty: Option>, pat: &'pcx pat::RustItems<'pcx>, meta: &pat::NonLocalMetaVars<'pcx>, @@ -112,7 +92,7 @@ impl<'pcx, 'tcx> MatchTy<'pcx, 'tcx> for MatchTyCtxt<'pcx, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } - fn typing_env(&self) -> ty::TypingEnv<'tcx> { + fn typing_env(&self) -> TypingEnv<'tcx> { self.typing_env } @@ -126,22 +106,31 @@ impl<'pcx, 'tcx> MatchTy<'pcx, 'tcx> for MatchTyCtxt<'pcx, 'tcx> { } #[instrument(level = "trace", skip(self), ret)] fn match_ty_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: ty::Const<'tcx>) -> bool { - //FIXME: handle more cases of `ty::ConstKind` - if let ty::ConstKind::Value(value) = konst.kind() - && self.match_ty(const_var.ty, value.ty) - { - let const_value = self.tcx.valtree_to_const_val(konst.to_value()); - self.const_vars[const_var.idx] - .borrow_mut() - .insert(mir::Const::from_value(const_value, value.ty)); - return true; + match konst.kind() { + ty::ConstKind::Param(param) => { + let ty = param.find_ty_from_env(self.typing_env.param_env); + self.match_ty(const_var.ty, ty) && { + // We can't convert a const generic param into a `mir::Const` + self.const_vars[const_var.idx].borrow_mut().insert(Const::Param(param)); + true + } + }, + ty::ConstKind::Value(value) => { + self.match_ty(const_var.ty, value.ty) && { + let const_value = self.tcx.valtree_to_const_val(value); + self.const_vars[const_var.idx] + .borrow_mut() + .insert(Const::MIR(mir::Const::from_value(const_value, value.ty))); + true + } + }, + _ => false, } - false } #[instrument(level = "trace", skip(self), ret)] - fn match_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: mir::Const<'tcx>) -> bool { + fn match_mir_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: mir::Const<'tcx>) -> bool { if self.match_ty(const_var.ty, konst.ty()) { - self.const_vars[const_var.idx].borrow_mut().insert(konst); + self.const_vars[const_var.idx].borrow_mut().insert(Const::MIR(konst)); return true; } false @@ -170,14 +159,14 @@ pub(crate) trait MatchTy<'pcx, 'tcx> { fn pat(&self) -> &'pcx pat::RustItems<'pcx>; fn pcx(&self) -> PatCtxt<'pcx>; fn tcx(&self) -> TyCtxt<'tcx>; - fn typing_env(&self) -> ty::TypingEnv<'tcx>; + fn typing_env(&self) -> TypingEnv<'tcx>; #[must_use] fn match_ty_var(&self, ty_var: pat::TyVar, ty: ty::Ty<'tcx>) -> bool; #[must_use] fn match_ty_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: ty::Const<'tcx>) -> bool; #[must_use] - fn match_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: mir::Const<'tcx>) -> bool; + fn match_mir_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: mir::Const<'tcx>) -> bool; #[must_use] fn match_adt_matches(&self, pat: Symbol, adt_match: AdtMatch<'tcx>) -> bool; @@ -218,7 +207,7 @@ pub(crate) trait MatchTy<'pcx, 'tcx> { self.match_ty_var(ty_var, ty) }, (pat::TyKind::Array(ty_pat, konst_pat), ty::Array(ty, konst)) => { - self.match_ty(ty_pat, ty) && self.match_const(konst_pat, konst) + self.match_ty(ty_pat, ty) && self.match_ty_const(konst_pat, konst) }, (pat::TyKind::Slice(ty_pat), ty::Slice(ty)) => self.match_ty(ty_pat, ty), (pat::TyKind::Tuple(tys_pat), ty::Tuple(tys)) => { @@ -331,7 +320,7 @@ pub(crate) trait MatchTy<'pcx, 'tcx> { } #[instrument(level = "trace", skip(self), ret)] - fn match_const(&self, konst_pat: pat::Const<'pcx>, konst: ty::Const<'tcx>) -> bool { + fn match_ty_const(&self, konst_pat: pat::Const<'pcx>, konst: ty::Const<'tcx>) -> bool { match (konst_pat, konst.kind()) { (pat::Const::ConstVar(const_var), _) => self.match_ty_const_var(const_var, konst), //(pat::Const::Value(_value_pat), ty::Value(_ty, ty::ValTree::Leaf(_value))) => todo!(), @@ -359,40 +348,6 @@ pub(crate) trait MatchTy<'pcx, 'tcx> { } } - #[instrument(level = "trace", skip(self), ret)] - pub fn match_ty_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: ty::Const<'tcx>) -> bool { - //FIXME: handle more cases of `ty::ConstKind` - match konst.kind() { - ty::ConstKind::Param(param) => { - let ty = param.find_ty_from_env(self.typing_env.param_env); - self.match_ty(const_var.ty, ty) && { - // We can't convert a const generic param into a `mir::Const` - self.const_vars[const_var.idx].borrow_mut().insert(Const::Param(param)); - true - } - }, - ty::ConstKind::Value(value) => { - self.match_ty(const_var.ty, value.ty) && { - let const_value = self.tcx.valtree_to_const_val(value); - self.const_vars[const_var.idx] - .borrow_mut() - .insert(Const::MIR(mir::Const::from_value(const_value, value.ty))); - true - } - }, - _ => false, - } - } - - #[instrument(level = "trace", skip(self), ret)] - pub fn match_const_var(&self, const_var: pat::ConstVar<'pcx>, konst: mir::Const<'tcx>) -> bool { - if self.match_ty(const_var.ty, konst.ty()) { - self.const_vars[const_var.idx].borrow_mut().insert(Const::MIR(konst)); - return true; - } - false - } - #[instrument(level = "debug", skip(self), ret)] fn match_region(&self, pat: pat::RegionKind, region: ty::Region<'tcx>) -> bool { // FIXME: implement region matching @@ -489,7 +444,7 @@ pub(crate) trait MatchTy<'pcx, 'tcx> { .iter() .filter(|data| matches!(data.data, Impl | TypeNs(_) | ValueNs(_))); let matched = matched - && std::iter::zip(pat_iter.by_ref(), iter.by_ref()) + && zip(pat_iter.by_ref(), iter.by_ref()) .all(|(&path, data)| data.data.get_opt_name().is_some_and(|name| name == path)); // Check that `iter` (from `def_path`) is not longer than `pat_iter` (from `path`) let matched = matched && iter.next().is_none(); @@ -545,7 +500,7 @@ pub(crate) trait MatchTy<'pcx, 'tcx> { }, (pat::GenericArgKind::Type(ty_pat), ty::GenericArgKind::Type(ty)) => self.match_ty(ty_pat, ty), (pat::GenericArgKind::Const(konst_pat), ty::GenericArgKind::Const(konst)) => { - self.match_const(konst_pat, konst) + self.match_ty_const(konst_pat, konst) }, ( pat::GenericArgKind::Lifetime(_) | pat::GenericArgKind::Type(_) | pat::GenericArgKind::Const(_), diff --git a/crates/rpl_meta/src/lib.rs b/crates/rpl_meta/src/lib.rs index d4be0e38..474b696b 100644 --- a/crates/rpl_meta/src/lib.rs +++ b/crates/rpl_meta/src/lib.rs @@ -43,6 +43,14 @@ pub use error::RPLMetaError; use itertools::Itertools as _; pub use map::FlatMap; use meta::SymbolTables; +use rustc_lint::{Level, Lint}; + +pub static DYNAMIC: &Lint = &Lint { + name: "RPL::DYNAMIC", + desc: "dynamic RPL pattern", + default_level: Level::Deny, + ..Lint::default_fields_for_macro() +}; pub fn parse_and_collect<'mcx>( arena: &'mcx Arena<'mcx>, @@ -83,6 +91,7 @@ pub fn parse_and_collect<'mcx>( } let mut lints = mctx.collect_lints().collect_vec(); + lints.push(DYNAMIC); let prev_len = lints.len(); lints.sort_by(|a, b| a.name.cmp(b.name)); //FIXME: show warnings if two lints share the same name but have different configs. diff --git a/tests/ui/utils/dynamic.rs b/tests/ui/utils/dynamic.rs index 17a7fbd7..8e019b0e 100644 --- a/tests/ui/utils/dynamic.rs +++ b/tests/ui/utils/dynamic.rs @@ -7,7 +7,7 @@ fn f1() { //~^ERROR: Dynamic RPL pattern //~|HELP: You can use `#[rpl::dynamic]` to create a customizable lint. //~|NOTE: This is a dynamic RPL pattern, which can be customized during runtime. - //~|NOTE: `#[forbid(rpl::dynamic)]` on by default + //~|NOTE: `#[deny(rpl::dynamic)]` on by default } #[rpl::dynamic( From c47013442d1ce0eedd341375d976cd4c3836c8f9 Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Wed, 30 Jul 2025 21:23:24 +0800 Subject: [PATCH 10/17] Fix pattern for CVE-2020-35887 Commit b88c3c90c6f79dacd4a28923f169bb1e14e4342f is discarded --- .../cve}/CVE-2020-35887.rpl | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) rename docs/{development/patterns-may-not-pass-parsing => patterns-pest/cve}/CVE-2020-35887.rpl (53%) diff --git a/docs/development/patterns-may-not-pass-parsing/CVE-2020-35887.rpl b/docs/patterns-pest/cve/CVE-2020-35887.rpl similarity index 53% rename from docs/development/patterns-may-not-pass-parsing/CVE-2020-35887.rpl rename to docs/patterns-pest/cve/CVE-2020-35887.rpl index f4abbaaa..fdcae049 100644 --- a/docs/development/patterns-may-not-pass-parsing/CVE-2020-35887.rpl +++ b/docs/patterns-pest/cve/CVE-2020-35887.rpl @@ -1,60 +1,68 @@ pattern CVE-2020-35887 patt { - pattern_unchecked_ptr_offset[$T: ty, $U: ty] = + #[diag = "p_unchecked_ptr_offset"] + pattern_unchecked_ptr_offset[$T: type, $U: type] = fn $pattern($len: $U, ..) -> _ { 'ptr: let $ptr: *const $T = _; 'offset: let $ptr_1: *const $T = Offset(copy $ptr, copy $len); } - pattern_unchecked_mut_ptr_offset[$T: ty, $U: ty] + #[diag = "p_unchecked_ptr_offset"] + pattern_unchecked_mut_ptr_offset[$T: type, $U: type] = fn $pattern($len: $U, ..) -> _ { 'ptr: let $ptr: *mut $T = _; 'offset: let $ptr_1: *mut $T = Offset(copy $ptr, copy $len); } - pattern_unchecked_ptr_casted_offset[$T: ty, $U1: ty, $U2: ty] - fn $pattern($len: $U, ..) -> _ { + #[diag = "p_unchecked_ptr_offset"] + pattern_unchecked_ptr_casted_offset[$T: type, $U1: type, $U2: type] = + fn $pattern($len: $U1, ..) -> _ { let $len2: $U2 = copy $len as $U2 (IntToInt); 'ptr: let $ptr: *const $T = _; 'offset: let $ptr_1: *const $T = Offset(copy $ptr, copy $len2); } - pattern_unchecked_mut_ptr_casted_offset[$T: ty, $U1: ty, $U2: ty] - fn $pattern($len: $U, ..) -> _ { + #[diag = "p_unchecked_ptr_offset"] + pattern_unchecked_mut_ptr_casted_offset[$T: type, $U1: type, $U2: type] = + fn $pattern($len: $U1, ..) -> _ { let $len2: $U2 = copy $len as $U2 (IntToInt); 'ptr: let $ptr: *mut $T = _; 'offset: let $ptr_1: *mut $T = Offset(copy $ptr, copy $len2); } - pattern_unchecked_ptr_arith_offset[$T: ty, $U: ty] + #[diag = "p_unchecked_ptr_offset"] + pattern_unchecked_ptr_arith_offset[$T: type, $U: type] = fn $pattern($len: $U, ..) -> _ { 'ptr: let $ptr: *const $T = _; // _8 'offset: let $ptr_1: *const $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len); // _7 } - pattern_unchecked_mut_ptr_arith_offset[$T: ty, $U: ty] + #[diag = "p_unchecked_ptr_offset"] + pattern_unchecked_mut_ptr_arith_offset[$T: type, $U: type] = fn $pattern($len: $U, ..) -> _ { 'ptr: let $ptr: *mut $T = _; // _8 'offset: let $ptr_1: *mut $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len); // _7 } - pattern_unchecked_ptr_casted_arith_offset[$T: ty, $U1: ty, $U2: ty] - fn $pattern($len: $U, ..) -> _ { + #[diag = "p_unchecked_ptr_offset"] + pattern_unchecked_ptr_casted_arith_offset[$T: type, $U1: type, $U2: type] = + fn $pattern($len: $U1, ..) -> _ { let $len2: $U2 = copy $len as $U2 (IntToInt); // _6 'ptr: let $ptr: *const $T = _; // _8 'offset: let $ptr_1: *const $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len2); // _7 } - pattern_unchecked_mut_ptr_casted_arith_offset[$T: ty, $U1: ty, $U2: ty] - fn $pattern($len: $U, ..) -> _ { + #[diag = "p_unchecked_ptr_offset"] + pattern_unchecked_mut_ptr_casted_arith_offset[$T: type, $U1: type, $U2: type] = + fn $pattern($len: $U1, ..) -> _ { let $len2: $U2 = copy $len as $U2 (IntToInt); // _6 'ptr: let $ptr: *mut $T = _; // _8 @@ -62,3 +70,15 @@ patt { let $ptr_1: *mut $T = std::intrinsics::arith_offset::<$T>(copy $ptr, copy $len2); // _7 } } + +diag { + p_unchecked_ptr_offset = { + primary(offset) = "it is an undefined behavior to offset a pointer using an unchecked integer", + label(offset) = "offset here", + label(ptr) = "pointer used here", + help = "check whether it's in bound before offsetting", + note = "See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset", + name = "unchecked_pointer_offset", + level = "warn", + } +} From 22e60a834c8317715bf660de5f7e7228a98adb81 Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Wed, 30 Jul 2025 21:39:26 +0800 Subject: [PATCH 11/17] Fix ui tests results --- .../src/zero_offset.inline.stderr | 20 ++++- .../src/zero_offset.rs | 1 + .../cve/cve_2019_16138/src/lib.inline.stderr | 45 +--------- tests/ui/cve/cve_2020_35877/cve_2020_35877.rs | 2 +- tests/ui/cve/cve_2020_35877/minimal.rs | 65 ++++++++------ tests/ui/cve/cve_2020_35877/minimal.stderr | 86 +++++++++++++++++-- .../cve_2020_35887.inline.stderr | 24 ++---- .../cve_2020_35887.regular.stderr | 40 +-------- tests/ui/cve/cve_2020_35887/cve_2020_35887.rs | 7 +- .../cve/cve_2020_35888/cve_2020_35888.stderr | 16 +--- .../cve/cve_2020_35892_3/cve_2020_35892_3.rs | 2 + .../cve_2020_35892_3/cve_2020_35892_3.stderr | 38 ++------ .../cve_2021_25904.inline.stderr | 28 +----- .../cve/cve_2021_25905/minimal.inline.stderr | 64 -------------- .../cve_2021_29935/simplified.inline.stderr | 13 --- .../cve_2021_29941_2.inline.stderr | 16 +--- .../cve_2020_35886.regular.stderr | 28 ------ 17 files changed, 162 insertions(+), 333 deletions(-) delete mode 100644 tests/ui/cve/cve_2021_25905/minimal.inline.stderr delete mode 100644 tests/ui/cve_2020_35886/cve_2020_35886.regular.stderr diff --git a/tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.inline.stderr b/tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.inline.stderr index a420ef34..84e0f2a7 100644 --- a/tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.inline.stderr +++ b/tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.inline.stderr @@ -16,7 +16,7 @@ LL | let n = m.wrapping_add(0); = note: `()` is a zero-sized type, offsetting a pointer to it will always yield the same address error: offset calculation on zero-sized value - --> tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.rs:34:17 + --> tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.rs:35:17 | LL | let n = offset(m, 0); | ^^^^^^^^^^^^ @@ -24,12 +24,26 @@ LL | let n = offset(m, 0); = note: `()` is a zero-sized type, offsetting a pointer to it will always yield the same address error: offset calculation on zero-sized value - --> tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.rs:38:17 + --> tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.rs:39:17 | LL | let n = wrapping_add(m, 0); | ^^^^^^^^^^^^^^^^^^ | = note: `()` is a zero-sized type, offsetting a pointer to it will always yield the same address -error: aborting due to 4 previous errors +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.rs:28:11 + | +LL | m.wrapping_add(n) + | ^^^^^^^^^^^^^^^ + | | + | offset here + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` + +error: aborting due to 5 previous errors diff --git a/tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.rs b/tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.rs index 0f06c829..e8912e3c 100644 --- a/tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.rs +++ b/tests/ui/cross-stmt-and-func-comparison-with-clippy/src/zero_offset.rs @@ -26,6 +26,7 @@ fn cross_function() { } fn wrapping_add(m: *mut T, n: usize) -> *mut T { m.wrapping_add(n) + //~[inline]^ unchecked_pointer_offset } unsafe { let mut m = (); diff --git a/tests/ui/cve/cve_2019_16138/src/lib.inline.stderr b/tests/ui/cve/cve_2019_16138/src/lib.inline.stderr index 03289433..61e56a62 100644 --- a/tests/ui/cve/cve_2019_16138/src/lib.inline.stderr +++ b/tests/ui/cve/cve_2019_16138/src/lib.inline.stderr @@ -1,17 +1,3 @@ -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2019_16138/src/lib.rs:221:77 - | -LL | ... for (dst, &pix) in chunk.iter_mut().zip(buf.iter()) { - | ^^^^^^ - | | - | offset here - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` - error: it usually isn't necessary to apply #[inline] to private functions --> tests/ui/cve/cve_2019_16138/src/lib.rs:265:13 | @@ -82,18 +68,6 @@ LL | | } = help: See https://matklad.github.io/2021/07/09/inline-in-rust.html and https://rustc-dev-guide.rust-lang.org/backend/monomorph.html = note: generic functions are always `#[inline]` (monomorphization) -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2019_16138/src/lib.rs:382:69 - | -LL | for (offset, &value) in buf[0..rl as usize].iter().enumerate() { - | ^^^^^^ - | | - | offset here - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - error: it usually isn't necessary to apply #[inline] to private functions --> tests/ui/cve/cve_2019_16138/src/lib.rs:360:9 | @@ -144,22 +118,5 @@ LL | | } = help: See https://matklad.github.io/2021/07/09/inline-in-rust.html = note: the compiler generally makes good inline decisions about private functions -error: it usually isn't necessary to apply #[inline] to private functions - --> tests/ui/cve_2019_16138/src/lib.rs:425:13 - | -LL | #[inline] - | --------- `#[inline]` here -LL | / fn rl_marker(pix: RGBE8Pixel) -> Option { -LL | | -LL | | -LL | | if pix.c == [1, 1, 1] { -... | -LL | | } - | |_____________^ `#[inline]` applied here - | - = help: See https://matklad.github.io/2021/07/09/inline-in-rust.html - = note: the compiler generally makes good inline decisions about private functions - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 10 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/cve/cve_2020_35877/cve_2020_35877.rs b/tests/ui/cve/cve_2020_35877/cve_2020_35877.rs index 00af5054..69df62df 100644 --- a/tests/ui/cve/cve_2020_35877/cve_2020_35877.rs +++ b/tests/ui/cve/cve_2020_35877/cve_2020_35877.rs @@ -44,7 +44,7 @@ where while count > 0 { count -= 1; p = p.offset(1); - //FIXME: ~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer + //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } &*p //~^ERROR: it is unsound to dereference a pointer that is offset using an unchecked integer diff --git a/tests/ui/cve/cve_2020_35877/minimal.rs b/tests/ui/cve/cve_2020_35877/minimal.rs index a5a18c9d..38e8bd96 100644 --- a/tests/ui/cve/cve_2020_35877/minimal.rs +++ b/tests/ui/cve/cve_2020_35877/minimal.rs @@ -1,30 +1,35 @@ +// should lint fn unchecked(ptr: *const T, index: usize, length: usize) -> *const T { unsafe { let mut p = ptr; p = p.add(index); //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer + //~|ERROR: it is an undefined behavior to offset a pointer using an unchecked integer p } } +// should lint fn unchecked_slice(slice: &[T], index: usize) -> *const T { let mut p = slice.as_ptr(); let length = slice.len(); unsafe { p = p.add(index); //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer + //~|ERROR: it is an undefined behavior to offset a pointer using an unchecked integer &*p } } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn slice_end(slice: &[T]) -> *const T { let p = slice.as_ptr(); let length = slice.len(); unsafe { p.add(length) } + //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// weird but should not lint fn slice_at(slice: &[T], index: usize) -> *const T { let p = slice.as_ptr(); let length = slice.len(); @@ -32,7 +37,7 @@ fn slice_at(slice: &[T], index: usize) -> *const T { unsafe { p.add(length) } } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn vec_iter(vec: &Vec) -> usize { let mut x = 0; for i in vec { @@ -41,7 +46,7 @@ fn vec_iter(vec: &Vec) -> usize { x } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn vec_iter_mut(vec: &mut Vec) -> usize { let mut x = 0; for i in vec.iter_mut() { @@ -51,7 +56,7 @@ fn vec_iter_mut(vec: &mut Vec) -> usize { x } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn slice_iter(slice: &[usize]) -> usize { let mut x = 0; for i in slice { @@ -60,7 +65,7 @@ fn slice_iter(slice: &[usize]) -> usize { x } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn slice_iter_mut(vec: &mut [usize]) -> usize { let mut x = 0; for i in vec.iter_mut() { @@ -70,111 +75,116 @@ fn slice_iter_mut(vec: &mut [usize]) -> usize { x } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should lint fn checked_lt(slice: &[T], index: usize) -> &T { let mut p: *const T = slice.as_ptr(); let length: usize = slice.len(); assert!(index < length); unsafe { p = p.add(index); + //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer &*p } } +// should lint fn checked_le(ptr: *const T, index: usize, length: usize) -> *const T { unsafe { let mut p = ptr; // Though `index + 1` is moved in MIR, the negative pattern is still detected, so no false positive here assert!(index + 1 <= length); p = p.add(index); + //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer p } } +// should lint fn checked_le_1(ptr: *const T, index: usize, right: usize) -> *const T { unsafe { let mut p = ptr; assert!(index <= right); p = p.add(index); + //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer p } } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_vec_deref(slice: &Vec) -> &[T] { &*slice // This is safe because the length will be checked at runtime } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_vec_deref_mut(slice: &mut Vec) -> &mut [T] { &mut *slice // This is safe because the length will be checked at runtime } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_slice_range_from(slice: &[T]) -> &[T] { &slice[1..] // This is safe because the length will be checked at runtime } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_slice_mut_range_from(slice: &mut [T]) -> &mut [T] { &mut slice[1..] // This is safe because the length will be checked at runtime } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_slice_range_to(slice: &[T]) -> &[T] { &slice[..2] // This is safe because the length will be checked at runtime } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_slice_mut_range_to(slice: &mut [T]) -> &mut [T] { &mut slice[..2] // This is safe because the length will be checked at runtime } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_slice_range_full(slice: &[T]) -> &[T] { &slice[..] // This is safe as there is no out-of-bounds access } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_slice_mut_range_full(slice: &mut [T]) -> &mut [T] { &mut slice[..] // This is safe as there is no out-of-bounds access } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_vec_ref_range_full(slice: &Vec) -> &[T] { &slice[..] // This is safe as there is no out-of-bounds access } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_vec_ref_mut_range_full(slice: &mut Vec) -> &mut [T] { &mut slice[..] // This is safe as there is no out-of-bounds access } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_vec_range_full() { let v = Vec::new(); let slice: &[T] = &v[..]; // This is safe as there is no out-of-bounds access } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_vec_mut_range_full() { let mut v = Vec::new(); let slice: &mut [T] = &mut v[..]; // This is safe as there is no out-of-bounds access } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should not lint fn safe_array_in_bound(slice: &[T; 2]) -> &T { let ptr = slice.as_ptr(); unsafe { &*ptr.add(1) } @@ -182,32 +192,34 @@ fn safe_array_in_bound(slice: &[T; 2]) -> &T { // and the index is guaranteed to be less than the length. } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should lint fn unsafe_array_out_of_bound_1(slice: &[T; 2]) -> &T { let ptr = slice.as_ptr(); unsafe { &*ptr.add(2) } //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should lint fn unsafe_array_out_of_bound_2(slice: &[T; 2]) -> &T { let ptr = slice.as_ptr(); unsafe { &*ptr.add(4) } //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } +// should not lint fn safe_unchecked_2_const_rem(slice: &[T; N], index: usize) -> &T { let ptr = slice.as_ptr(); unsafe { &*ptr.add(index % N) } } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should lint fn safe_unchecked_2_const(slice: &[T; N]) -> &T { let ptr = slice.as_ptr(); unsafe { &*ptr.add(N) } //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } +// should not lint fn safe_unchecked_2_const_literal_2(slice: &[T; 2], index: usize) -> &T { let ptr = slice.as_ptr(); unsafe { &*ptr.add(index % 2) } @@ -219,34 +231,39 @@ fn safe_unchecked_2_const_literal_0(slice: &[T; 0], index: usize) -> &T { //~^ERROR: this operation will panic at runtime } +// should lint fn safe_unchecked_2_mismatched(slice: &[T], index: usize) -> &T { let ptr = slice.as_ptr(); unsafe { &*ptr.add(index % 2) } //FIXME: this is a false negative } +// should lint fn safe_unchecked_2_const_literal_2_3_mismatched(slice: &[T; 2], index: usize) -> &T { let ptr = slice.as_ptr(); unsafe { &*ptr.add(index % 3) } //FIXME: this is a false negative } +// should not lint fn safe_unchecked_2(slice: &[T], index: usize) -> &T { let ptr = slice.as_ptr(); let length = slice.len(); unsafe { &*ptr.add(index % length) } } +// should not lint fn safe_unchecked_without_offset(slice: &[T; 2]) -> &T { &slice[1] } +// should not lint unsafe fn unsafe_unchecked_in_unsafe(p: *const T) -> *const T { // Do anything you want with `p`, as it's in an `unsafe` function unsafe { p.add(1) } } -// #[rpl::dump_mir(dump_cfg, dump_ddg)] +// should lint fn unsafe_unchecked_in_safe(p: *const T) -> *const T { // Sorry, it's in a safe function :( unsafe { p.add(1) } diff --git a/tests/ui/cve/cve_2020_35877/minimal.stderr b/tests/ui/cve/cve_2020_35877/minimal.stderr index cc42822b..4d2e8b6e 100644 --- a/tests/ui/cve/cve_2020_35877/minimal.stderr +++ b/tests/ui/cve/cve_2020_35877/minimal.stderr @@ -1,5 +1,5 @@ error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve/cve_2020_35877/minimal.rs:4:15 + --> tests/ui/cve/cve_2020_35877/minimal.rs:5:15 | LL | p = p.add(index); | - ^^^^^^^^^^ offset here @@ -12,7 +12,7 @@ LL | p = p.add(index); = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve/cve_2020_35877/minimal.rs:14:15 + --> tests/ui/cve/cve_2020_35877/minimal.rs:5:15 | LL | p = p.add(index); | - ^^^^^^^^^^ offset here @@ -21,9 +21,78 @@ LL | p = p.add(index); | = help: check whether it's in bound before offsetting = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve/cve_2020_35877/minimal.rs:135:20 + --> tests/ui/cve/cve_2020_35877/minimal.rs:17:15 + | +LL | p = p.add(index); + | - ^^^^^^^^^^ offset here + | | + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve/cve_2020_35877/minimal.rs:17:15 + | +LL | p = p.add(index); + | - ^^^^^^^^^^ offset here + | | + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve/cve_2020_35877/minimal.rs:28:16 + | +LL | let p = slice.as_ptr(); + | -------- pointer used here +LL | let length = slice.len(); +LL | unsafe { p.add(length) } + | ^^^^^^^^^^^ offset here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve/cve_2020_35877/minimal.rs:84:15 + | +LL | p = p.add(index); + | - ^^^^^^^^^^ offset here + | | + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve/cve_2020_35877/minimal.rs:96:15 + | +LL | p = p.add(index); + | - ^^^^^^^^^^ offset here + | | + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve/cve_2020_35877/minimal.rs:107:15 + | +LL | p = p.add(index); + | - ^^^^^^^^^^ offset here + | | + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve/cve_2020_35877/minimal.rs:198:20 | LL | let ptr = slice.as_ptr(); | -------- pointer used here @@ -34,7 +103,7 @@ LL | unsafe { &*ptr.add(2) } = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve/cve_2020_35877/minimal.rs:142:20 + --> tests/ui/cve/cve_2020_35877/minimal.rs:205:20 | LL | let ptr = slice.as_ptr(); | -------- pointer used here @@ -44,8 +113,9 @@ LL | unsafe { &*ptr.add(4) } = help: check whether it's in bound before offsetting = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + WARN rpl_constraints::predicates::multiple_consts Encountered non-integer constants in usize_lt predicate error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve/cve_2020_35877/minimal.rs:153:20 + --> tests/ui/cve/cve_2020_35877/minimal.rs:218:20 | LL | let ptr = slice.as_ptr(); | -------- pointer used here @@ -56,7 +126,7 @@ LL | unsafe { &*ptr.add(N) } = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset error: this operation will panic at runtime - --> tests/ui/cve/cve_2020_35877/minimal.rs:164:24 + --> tests/ui/cve/cve_2020_35877/minimal.rs:230:24 | LL | unsafe { &*ptr.add(index % 0) } | ^^^^^^^^^ attempt to calculate the remainder of `_` with a divisor of zero @@ -64,7 +134,7 @@ LL | unsafe { &*ptr.add(index % 0) } = note: `#[deny(unconditional_panic)]` on by default error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve/cve_2020_35877/minimal.rs:197:16 + --> tests/ui/cve/cve_2020_35877/minimal.rs:269:16 | LL | fn unsafe_unchecked_in_safe(p: *const T) -> *const T { | - pointer used here @@ -75,5 +145,5 @@ LL | unsafe { p.add(1) } = help: check whether it's in bound before offsetting = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset -error: aborting due to 9 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/cve/cve_2020_35887/cve_2020_35887.inline.stderr b/tests/ui/cve/cve_2020_35887/cve_2020_35887.inline.stderr index fab4b35b..76ee4768 100644 --- a/tests/ui/cve/cve_2020_35887/cve_2020_35887.inline.stderr +++ b/tests/ui/cve/cve_2020_35887/cve_2020_35887.inline.stderr @@ -1,19 +1,5 @@ -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35887/cve_2020_35887.rs:44:24 - | -LL | (*(ptr.wrapping_offset(i as isize))) = default; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | offset here - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` - error: dropped an possibly-uninitialized value - --> tests/ui/cve/cve_2020_35887/cve_2020_35887.rs:63:17 + --> tests/ui/cve/cve_2020_35887/cve_2020_35887.rs:64:17 | LL | let ptr = unsafe { alloc(layout) as *mut T }; | ------------- memory allocated here @@ -29,7 +15,7 @@ LL | (*(ptr.wrapping_offset(i as isize))) = template.clone(); = note: `#[deny(rpl::drop_uninit_value)]` on by default error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35887/cve_2020_35887.rs:79:27 + --> tests/ui/cve/cve_2020_35887/cve_2020_35887.rs:78:27 | LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,9 +25,11 @@ LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() | = help: check whether it's in bound before offsetting = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35887/cve_2020_35887.rs:86:27 + --> tests/ui/cve/cve_2020_35887/cve_2020_35887.rs:85:27 | LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,5 +40,5 @@ LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() = help: check whether it's in bound before offsetting = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/cve/cve_2020_35887/cve_2020_35887.regular.stderr b/tests/ui/cve/cve_2020_35887/cve_2020_35887.regular.stderr index efc4ffbb..301b907c 100644 --- a/tests/ui/cve/cve_2020_35887/cve_2020_35887.regular.stderr +++ b/tests/ui/cve/cve_2020_35887/cve_2020_35887.regular.stderr @@ -1,17 +1,3 @@ -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35887/cve_2020_35887.rs:44:19 - | -LL | let ptr = unsafe { alloc(layout) as *mut T }; - | ----------------------- pointer used here -... -LL | (*(ptr.wrapping_offset(i as isize))) = default; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ offset here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` - error: resulting pointer `*mut T` has a different alignment than the original alignment that the pointer was created with --> tests/ui/cve/cve_2020_35887/cve_2020_35887.rs:38:28 | @@ -37,29 +23,5 @@ LL | let ptr = unsafe { alloc(layout) as *mut T }; = note: See https://doc.rust-lang.org/std/alloc/fn.alloc.html and https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html#method.alloc = note: `#[deny(rpl::alloc_maybe_zero)]` on by default -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35887/cve_2020_35887.rs:79:18 - | -LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() - | --------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | offset here - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35887/cve_2020_35887.rs:86:18 - | -LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() - | --------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | offset here - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/cve/cve_2020_35887/cve_2020_35887.rs b/tests/ui/cve/cve_2020_35887/cve_2020_35887.rs index b1d57baa..8f6cc9fc 100644 --- a/tests/ui/cve/cve_2020_35887/cve_2020_35887.rs +++ b/tests/ui/cve/cve_2020_35887/cve_2020_35887.rs @@ -42,8 +42,7 @@ where for i in 0..size { unsafe { (*(ptr.wrapping_offset(i as isize))) = default; - //~^ ERROR: it is an undefined behavior to offset a pointer using an unchecked integer - // FIXME: false positive + // FIXME: ~^ ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } } Self { size, ptr } @@ -77,14 +76,14 @@ impl Index for Array { // #[rpl::dump_mir(dump_cfg, dump_ddg)] fn index<'a>(&'a self, idx: usize) -> &'a Self::Output { unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() - //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer + //~[inline]^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } } impl IndexMut for Array { fn index_mut<'a>(&'a mut self, idx: usize) -> &'a mut Self::Output { unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() - //~^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer + //~[inline]^ERROR: it is an undefined behavior to offset a pointer using an unchecked integer } } diff --git a/tests/ui/cve/cve_2020_35888/cve_2020_35888.stderr b/tests/ui/cve/cve_2020_35888/cve_2020_35888.stderr index 85195642..a3ef899b 100644 --- a/tests/ui/cve/cve_2020_35888/cve_2020_35888.stderr +++ b/tests/ui/cve/cve_2020_35888/cve_2020_35888.stderr @@ -1,17 +1,3 @@ -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35888/cve_2020_35888.rs:18:24 - | -LL | (*(ptr.wrapping_offset(i as isize))) = template.clone(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | offset here - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` - error: dropped an possibly-uninitialized value --> tests/ui/cve/cve_2020_35888/cve_2020_35888.rs:18:17 | @@ -28,5 +14,5 @@ LL | (*(ptr.wrapping_offset(i as isize))) = template.clone(); = help: assigning to a dereferenced pointer will cause previous value to be dropped, and try using `ptr::write` instead = note: `#[deny(rpl::drop_uninit_value)]` on by default -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs index 28772382..1e05cd6e 100644 --- a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs +++ b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs @@ -47,6 +47,8 @@ impl Slab { unsafe { elem_ptr = self.mem.offset(offset as isize); + //~^ ERROR: it is an undefined behavior to offset a pointer using an unchecked integer + //~| HELP: check whether it's in bound before offsetting last_elem_ptr = self.mem.offset(self.len as isize); //~^ HELP: this is because `self.len` exceeds the container's length by one //~| HELP: did you mean this diff --git a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr index 300038cb..e29a8143 100644 --- a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr +++ b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr @@ -1,18 +1,5 @@ error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35892_3/cve_2020_35892_3.rs:16:41 - | -LL | let elem_ptr = self.mem.offset(x as isize); - | -------- ^^^^^^^^^^^^^^^^^^ offset here - | | - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` - -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35892_3/cve_2020_35892_3.rs:28:31 + --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:28:31 | LL | unsafe { &(*(self.mem.offset(index as isize))) } | -------- ^^^^^^^^^^^^^^^^^^^^^^ offset here @@ -21,9 +8,11 @@ LL | unsafe { &(*(self.mem.offset(index as isize))) } | = help: check whether it's in bound before offsetting = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35892_3/cve_2020_35892_3.rs:49:33 + --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:49:33 | LL | elem_ptr = self.mem.offset(offset as isize); | -------- ^^^^^^^^^^^^^^^^^^^^^^^ offset here @@ -33,19 +22,8 @@ LL | elem_ptr = self.mem.offset(offset as isize); = help: check whether it's in bound before offsetting = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35892_3/cve_2020_35892_3.rs:50:38 - | -LL | last_elem_ptr = self.mem.offset(self.len as isize); - | -------- ^^^^^^^^^^^^^^^^^^^^^^^^^ offset here - | | - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - error: pointer out of bound - --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:53:25 + --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:57:25 | LL | last_elem_ptr = self.mem.offset(self.len as isize); | ------------------------- @@ -57,14 +35,14 @@ LL | last_elem = ptr::read(last_elem_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^ pointer read here | help: this is because `self.len` exceeds the container's length by one - --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:48:45 + --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:52:45 | LL | last_elem_ptr = self.mem.offset(self.len as isize); | ^^^^^^^^ = note: `#[deny(rpl::offset_by_one)]` on by default error: it usually isn't necessary to apply #[inline] to generic functions - --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:35:5 + --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:37:5 | LL | #[inline] | --------- `#[inline]` here @@ -79,5 +57,5 @@ LL | | } = note: `-D rpl::generic-function-marked-inline` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(rpl::generic_function_marked_inline)]` -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/cve/cve_2021_25904/cve_2021_25904.inline.stderr b/tests/ui/cve/cve_2021_25904/cve_2021_25904.inline.stderr index 6ff74575..637e1d0b 100644 --- a/tests/ui/cve/cve_2021_25904/cve_2021_25904.inline.stderr +++ b/tests/ui/cve/cve_2021_25904/cve_2021_25904.inline.stderr @@ -1,29 +1,3 @@ -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2021_25904/cve_2021_25904.rs:213:24 - | -LL | self.comp_info.iter() - | ^^^^^^ - | | - | offset here - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` - -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2021_25904/cve_2021_25904.rs:450:41 - | -LL | let mut f_iter = fmt.format.iter(); - | ^^^^^^ - | | - | offset here - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - error: it is unsound to trust pointers from passed-in iterators in a public safe function --> tests/ui/cve/cve_2021_25904/cve_2021_25904.rs:443:50 | @@ -39,5 +13,5 @@ LL | let ss = unsafe { slice::from_raw_parts(rr, hb * s_linesize = help: consider marking the function as unsafe = note: `#[deny(rpl::unvalidated_slice_from_raw_parts)]` on by default -error: aborting due to 3 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/cve/cve_2021_25905/minimal.inline.stderr b/tests/ui/cve/cve_2021_25905/minimal.inline.stderr deleted file mode 100644 index b5bb106c..00000000 --- a/tests/ui/cve/cve_2021_25905/minimal.inline.stderr +++ /dev/null @@ -1,64 +0,0 @@ -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2021_25905/minimal.rs:20:38 - | -LL | buf.as_mut_ptr().offset(b as isize), - | ------------ ^^^^^^^^^^^^^^^^^^ offset here - | | - | pointer used here -... -LL | cases!(Vec::new()); - | ------------------ in this macro invocation - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` - = note: this error originates in the macro `cases` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2021_25905/minimal.rs:20:38 - | -LL | buf.as_mut_ptr().offset(b as isize), - | ------------ ^^^^^^^^^^^^^^^^^^ offset here - | | - | pointer used here -... -LL | cases!(vec![1, 2, 3]); - | --------------------- in this macro invocation - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: this error originates in the macro `cases` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2021_25905/minimal.rs:20:38 - | -LL | buf.as_mut_ptr().offset(b as isize), - | ------------ ^^^^^^^^^^^^^^^^^^ offset here - | | - | pointer used here -... -LL | cases!(Vec::with_capacity(0)); - | ----------------------------- in this macro invocation - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: this error originates in the macro `cases` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2021_25905/minimal.rs:20:38 - | -LL | buf.as_mut_ptr().offset(b as isize), - | ------------ ^^^^^^^^^^^^^^^^^^ offset here - | | - | pointer used here -... -LL | cases!(Vec::with_capacity(1)); - | ----------------------------- in this macro invocation - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: this error originates in the macro `cases` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 4 previous errors - diff --git a/tests/ui/cve/cve_2021_29935/simplified.inline.stderr b/tests/ui/cve/cve_2021_29935/simplified.inline.stderr index e9b80cac..a65a08f6 100644 --- a/tests/ui/cve/cve_2021_29935/simplified.inline.stderr +++ b/tests/ui/cve/cve_2021_29935/simplified.inline.stderr @@ -6,19 +6,6 @@ LL | for (i, prefix) in self.prefixes.iter().enumerate() { | = note: `-D rpl::cast-slice-from-raw-parts` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(rpl::cast_slice_from_raw_parts)]` -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2021_29935/simplified.rs:239:50 - | -LL | for (i, prefix) in self.prefixes.iter().enumerate() { - | ^^^^^^ - | | - | offset here - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` error: aborting due to 1 previous error diff --git a/tests/ui/cve/cve_2021_29941_2/cve_2021_29941_2.inline.stderr b/tests/ui/cve/cve_2021_29941_2/cve_2021_29941_2.inline.stderr index 4c9f50e6..30577920 100644 --- a/tests/ui/cve/cve_2021_29941_2/cve_2021_29941_2.inline.stderr +++ b/tests/ui/cve/cve_2021_29941_2/cve_2021_29941_2.inline.stderr @@ -10,20 +10,6 @@ LL | vec.set_len(len); = help: incorrect implementation of `std::iter::ExactSizeIterator::len` must not cause safety issues, and consider using `std::iter::TrustedLen` instead if it's stabilized = note: `#[deny(rpl::trust_exact_size_iterator)]` on by default -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2021_29941_2/cve_2021_29941_2.rs:30:17 - | -LL | let ptr = vec.as_mut_ptr(); - | ------------ pointer used here -... -LL | ptr.add(a as usize).write(i as u32); - | ^^^^^^^^^^^^^^^ offset here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` - error: it is unsound to trust return value of `std::iter::ExactSizeIterator::len` and pass it to an unsafe function like `std::vec::Vec::set_len`, which may leak uninitialized memory --> tests/ui/cve/cve_2021_29941_2/cve_2021_29941_2.rs:58:13 | @@ -57,5 +43,5 @@ LL | vec.set_len(len); | = help: incorrect implementation of `std::iter::ExactSizeIterator::len` must not cause safety issues, and consider using `std::iter::TrustedLen` instead if it's stabilized -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/cve_2020_35886/cve_2020_35886.regular.stderr b/tests/ui/cve_2020_35886/cve_2020_35886.regular.stderr deleted file mode 100644 index 37335bf7..00000000 --- a/tests/ui/cve_2020_35886/cve_2020_35886.regular.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35886/cve_2020_35886.rs:36:18 - | -LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_ref() }.unwrap() - | --------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | offset here - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` - -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve_2020_35886/cve_2020_35886.rs:43:18 - | -LL | unsafe { self.ptr.wrapping_offset(idx as isize).as_mut() }.unwrap() - | --------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | offset here - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - -error: aborting due to 2 previous errors - From e8f7e17dee2ecf8864984be3ac5759031dfab309 Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Wed, 30 Jul 2025 21:49:49 +0800 Subject: [PATCH 12/17] Update comments and documents --- README.md | 21 +++++++++++++++++++-- crates/rpl_match/src/ty.rs | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 21d09486..593fa7c3 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,25 @@ The toolchain of RPL, which is a custom configuration of Rust compiler, enables 3. Run RPL analysis on your Rust project: - - `RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl` (using built-in RPL pattern definitions based on inline MIR) - - `RUSTFLAGS="-Zinline-mir=false" RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl` (using built-in RPL pattern definitions based on MIR) or `RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl -- -Zinline-mir=false` + - check using built-in RPL pattern definitions based on inline MIR: + + ```sh + RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl + ``` + + - check using built-in RPL pattern definitions based on MIR: + + ```sh + RUSTFLAGS="-Zinline-mir=false" RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl + ``` + + or + + ```sh + RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl -- -Zinline-mir=false + ``` + + you can also store the environment variable `RPL_PATS` for convenience. ## RPL Book diff --git a/crates/rpl_match/src/ty.rs b/crates/rpl_match/src/ty.rs index a03f72d9..9d313856 100644 --- a/crates/rpl_match/src/ty.rs +++ b/crates/rpl_match/src/ty.rs @@ -23,7 +23,7 @@ use crate::{AdtMatch, Candidates, MatchAdtCtxt}; /// FIXME: this generic parameter is not as convenient as intended, as `self.try_cmp_as(other, tcx, /// typing_env)` does not provide a way to specify `T` pub trait TryCmpAs<'tcx, T>: Copy { - /// Compare two `Const` values, returning `Some(Ordering)` if they can be compared. + /// Compare two `Const` values of `T`, returning `Some(Ordering)` if they can be compared. fn try_cmp_as(self, other: Self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>) -> Option; } From eea192b405f6146c2e2c79621d1aae0b14e94230 Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Wed, 30 Jul 2025 22:24:23 +0800 Subject: [PATCH 13/17] Use built-in patterns if not set --- .cargo/config.toml | 3 - .github/workflows/main.yml | 3 +- README.md | 6 +- crates/rpl_interface/src/callbacks.rs | 25 ++++--- crates/rpl_meta/src/cli.rs | 70 +++++++++++++++++++ src/driver.rs | 21 +++--- tests/ui/clippy/eager_transmute.normal.stderr | 20 +++--- .../cve_2020_35892_3/cve_2020_35892_3.stderr | 22 +++--- 8 files changed, 123 insertions(+), 47 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index ad844d9c..222e2779 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -18,8 +18,5 @@ binary-dep-depinfo = true [profile.dev] split-debuginfo = "unpacked" -[env] -RPL_PATS = "docs/patterns-pest/" - # [profile.dev.package.lintcheck] # rustflags = ["--remap-path-prefix", "=lintcheck"] diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9eac4c7c..756c6f5f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,12 +30,11 @@ jobs: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false - run: cargo fmt --check + - run: cargo test --all - run: cargo test --all env: RPL_PATS: docs/patterns-pest - run: cargo clippy -- -D warnings - run: cargo install --path . - run: cargo rpl --workspace --all-targets - env: - RPL_PATS: docs/patterns-pest # - uses: actions-rust-lang/audit@v1.2.4 diff --git a/README.md b/README.md index 593fa7c3..e512e09c 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,11 @@ The toolchain of RPL, which is a custom configuration of Rust compiler, enables RPL_PATS=/path/to/RPL/docs/patterns-pest cargo +nightly-2025-02-14 rpl -- -Zinline-mir=false ``` - you can also store the environment variable `RPL_PATS` for convenience. + You can also store the environment variable `RPL_PATS` for convenience. + + Without setting `RPL_PATS`, built-in RPL pattern definitions are used. + + TIP: You can view all available lints with `cargo rpl -- -W help`. ## RPL Book diff --git a/crates/rpl_interface/src/callbacks.rs b/crates/rpl_interface/src/callbacks.rs index 33c263a2..9925cb39 100644 --- a/crates/rpl_interface/src/callbacks.rs +++ b/crates/rpl_interface/src/callbacks.rs @@ -5,7 +5,7 @@ use rpl_context::PatternCtxt; use rpl_driver::{ERROR_FOUND, ErrorFound}; #[cfg(feature = "timing")] use rpl_driver::{TIMING, Timing}; -use rpl_meta::cli::collect_file_from_string_args; +use rpl_meta::cli::{collect_default_patterns, collect_file_from_string_args}; // use rpl_middle::ty::RplConfig; use rustc_interface::interface; use rustc_middle::ty::TyCtxt; @@ -66,11 +66,11 @@ impl rustc_driver::Callbacks for DefaultCallbacks {} pub struct RplCallbacks { rpl_args_var: Option, - pattern_paths: Vec, + pattern_paths: Option>, } impl RplCallbacks { - pub fn new(rpl_args_var: Option, pattern_paths: Vec) -> Self { + pub fn new(rpl_args_var: Option, pattern_paths: Option>) -> Self { Self { rpl_args_var, pattern_paths, @@ -110,9 +110,13 @@ impl rustc_driver::Callbacks for RplCallbacks { let mctx_arena = MCTX_ARENA.get_or_init(rpl_meta::arena::Arena::default); let patterns_and_paths = PATTERNS.get_or_init(|| { - collect_file_from_string_args(&self.pattern_paths, || { - EarlyDiagCtxt::new(config.opts.error_format).early_fatal(ErrorFound) - }) + self.pattern_paths + .as_ref() + .map_or_else(collect_default_patterns, |pattern_paths| { + collect_file_from_string_args(&pattern_paths, || { + EarlyDiagCtxt::new(config.opts.error_format).early_fatal(ErrorFound) + }) + }) }); // let dcx = compiler.sess.dcx(); let mut error_counter = 0; @@ -184,8 +188,13 @@ impl rustc_driver::Callbacks for RplCallbacks { let start = std::time::Instant::now(); let mctx_arena = MCTX_ARENA.get_or_init(rpl_meta::arena::Arena::default); - let patterns_and_paths = PATTERNS - .get_or_init(|| collect_file_from_string_args(&self.pattern_paths, || tcx.dcx().emit_fatal(ErrorFound))); + let patterns_and_paths = PATTERNS.get_or_init(|| { + self.pattern_paths + .as_ref() + .map_or_else(collect_default_patterns, |pattern_paths| { + collect_file_from_string_args(&pattern_paths, || tcx.dcx().emit_fatal(ErrorFound)) + }) + }); // let dcx = compiler.sess.dcx(); let mut error_counter = 0; diff --git a/crates/rpl_meta/src/cli.rs b/crates/rpl_meta/src/cli.rs index a548c03c..fa7380aa 100644 --- a/crates/rpl_meta/src/cli.rs +++ b/crates/rpl_meta/src/cli.rs @@ -7,6 +7,76 @@ use std::sync::Arc; use crate::RPLMetaError; +pub fn collect_default_patterns() -> Vec<(PathBuf, String)> { + /// Please pass a path related to docs/patterns-pest + macro_rules! default_pattern { + ($path:literal) => { + ( + PathBuf::from(concat!("/rpl/docs/patterns-pest", $path)), + include_str!(concat!("../../../docs/patterns-pest/", $path)).to_owned(), + ) + }; + } + + macro_rules! default_patterns { + ($($name:literal),* $(,)?) => { + vec![$( + default_pattern!($name), + )*] + }; + } + + default_patterns!( + // Clippy lints + "clippy/cast-slice-different-sizes.rpl", + "clippy/mut-from-ref.rpl", + "clippy/transmute-int-to-non-zero.rpl", + "clippy/unsound-collection-transmute.rpl", + "clippy/cast-slice-from-raw-parts.rpl", + "clippy/not-unsafe-ptr-arg-deref.rpl", + "clippy/transmute-null-to-fn.rpl", + "clippy/wrong-transmute.rpl", + "clippy/eager-transmute.rpl", + "clippy/size-of-in-element-count.rpl", + "clippy/transmuting-null.rpl", + "clippy/zst-offset.rpl", + "clippy/from-raw-with-void-ptr.rpl", + "clippy/swap-ptr-to-ref.rpl", + "clippy/uninit-assumed-init.rpl", + "clippy/mem-replace-with-uninit.rpl", + "clippy/uninit-vec.rpl", + // CVE patterns + "cve/CVE-2018-20992.rpl", + "cve/CVE-2020-25016.rpl", + "cve/CVE-2020-35877.rpl", + "cve/CVE-2020-35892-3.rpl", + "cve/CVE-2021-25904.rpl", + "cve/CVE-2022-23639.rpl", + "cve/CVE-2018-21000.rpl", + "cve/CVE-2020-35860.rpl", + "cve/CVE-2020-35881.rpl", + "cve/CVE-2020-35898-9.rpl", + "cve/CVE-2021-25905.rpl", + "cve/CVE-2024-27284.rpl", + "cve/CVE-2019-15548.rpl", + "cve/CVE-2020-35862.rpl", + "cve/CVE-2020-35887.rpl", + "cve/CVE-2020-35901-2.rpl", + "cve/CVE-2021-27376.rpl", + "cve/CVE-2019-16138.rpl", + "cve/CVE-2020-35873.rpl", + "cve/CVE-2020-35888.rpl", + "cve/CVE-2020-35907.rpl", + "cve/CVE-2021-29941-2.rpl", + // Common patterns based on Rust's UB + "ub/allow-unchecked.rpl", + "ub/private-or-generic-function-marked-inline.rpl", + "ub/transmute-to-bool.rpl", + "ub/manually-drop.rpl", + "ub/transmute-int-to-ptr.rpl", + ) +} + pub fn collect_file_from_string_args(args: &[String], handler: impl Fn() -> !) -> Vec<(PathBuf, String)> { let mut res = vec![]; for arg in args { diff --git a/src/driver.rs b/src/driver.rs index 011247b3..6139b1bd 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -2,6 +2,7 @@ #![allow(rustc::untranslatable_diagnostic)] #![feature(rustc_private)] #![feature(let_chains)] +#![feature(os_str_display)] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] // warn on rustc internal lints @@ -220,11 +221,13 @@ pub fn main() { let mut args: Vec = orig_args.clone(); pass_sysroot_env_if_given(&mut args, sys_root_env); - let pattern_paths = env::var("RPL_PATS").unwrap_or_else(|_| { - early_dcx.early_fatal( - "RPL_PATS is not set properly. Pass pattern path to RPL by setting the `RPL_PATS` environment variable.", - ) - }); + let pattern_paths = match env::var("RPL_PATS") { + Ok(val) => Some(val.split(':').map(ToString::to_string).collect()), + Err(env::VarError::NotPresent) => None, + Err(env::VarError::NotUnicode(var)) => { + early_dcx.early_fatal(format!("RPL_PATS is not valid unicode: {}", var.display())) + }, + }; let mut no_deps = false; let rpl_args_var = env::var(rpl_interface::RPL_ARGS_ENV).ok(); @@ -258,13 +261,7 @@ pub fn main() { /* rustc_driver::RunCompiler::new(&args, &mut RplCallbacks::new(rpl_args_var)) .set_using_internal_features(using_internal_features) .run() */ - rustc_driver::run_compiler( - &args, - &mut RplCallbacks::new( - rpl_args_var, - pattern_paths.split(':').map(ToString::to_string).collect(), - ), - ) + rustc_driver::run_compiler(&args, &mut RplCallbacks::new(rpl_args_var, pattern_paths)) } else { rustc_driver::run_compiler(&args, &mut RustcCallbacks::new(rpl_args_var)) } diff --git a/tests/ui/clippy/eager_transmute.normal.stderr b/tests/ui/clippy/eager_transmute.normal.stderr index eeecf560..9b85c1b6 100644 --- a/tests/ui/clippy/eager_transmute.normal.stderr +++ b/tests/ui/clippy/eager_transmute.normal.stderr @@ -120,6 +120,16 @@ error: this transmute is always evaluated eagerly, even if the condition is fals LL | (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: transmute from a `u8` to a `NonZero` + --> tests/ui/clippy/eager_transmute.rs:131:62 + | +LL | let _: Option> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(v1)` + | + = help: consider using `NonZero::new_unchecked` instead, or use `NonZero::new` if you want to handle the zero case safely + = note: `-D rpl::transmute-int-to-non-zero` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::transmute_int_to_non_zero)]` + error: this transmute is always evaluated eagerly, even if the condition is false --> tests/ui/clippy/eager_transmute.rs:131:62 | @@ -174,15 +184,5 @@ LL | (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::tr | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: transmute from a `u8` to a `NonZero` - --> tests/ui/clippy/eager_transmute.rs:131:62 - | -LL | let _: Option> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) }); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(v1)` - | - = help: consider using `NonZero::new_unchecked` instead, or use `NonZero::new` if you want to handle the zero case safely - = note: `-D rpl::transmute-int-to-non-zero` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::transmute_int_to_non_zero)]` - error: aborting due to 29 previous errors diff --git a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr index e29a8143..cb321071 100644 --- a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr +++ b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr @@ -11,17 +11,6 @@ LL | unsafe { &(*(self.mem.offset(index as isize))) } = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:49:33 - | -LL | elem_ptr = self.mem.offset(offset as isize); - | -------- ^^^^^^^^^^^^^^^^^^^^^^^ offset here - | | - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - error: pointer out of bound --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:57:25 | @@ -41,6 +30,17 @@ LL | last_elem_ptr = self.mem.offset(self.len as isize); | ^^^^^^^^ = note: `#[deny(rpl::offset_by_one)]` on by default +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:49:33 + | +LL | elem_ptr = self.mem.offset(offset as isize); + | -------- ^^^^^^^^^^^^^^^^^^^^^^^ offset here + | | + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + error: it usually isn't necessary to apply #[inline] to generic functions --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:37:5 | From b3c69714de397c30880f15daa8d152ac5c32aca3 Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Wed, 30 Jul 2025 22:26:01 +0800 Subject: [PATCH 14/17] Make clippy happy --- crates/rpl_context/src/pat/error.rs | 10 +++++----- crates/rpl_interface/src/callbacks.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/rpl_context/src/pat/error.rs b/crates/rpl_context/src/pat/error.rs index c7ed8df9..5a0098ee 100644 --- a/crates/rpl_context/src/pat/error.rs +++ b/crates/rpl_context/src/pat/error.rs @@ -96,7 +96,7 @@ impl DynamicError { )], helps: Vec::new(), suggestions: Vec::new(), - lint: &DYNAMIC, + lint: DYNAMIC, } } fn missing_primary_message_error(attr: &rustc_hir::Attribute) -> Self { @@ -106,7 +106,7 @@ impl DynamicError { notes: Vec::new(), helps: Vec::new(), suggestions: Vec::new(), - lint: &DYNAMIC, + lint: DYNAMIC, } } fn item_to_value_str(item: &rustc_ast::MetaItemInner) -> Result> { @@ -119,7 +119,7 @@ impl DynamicError { notes: Vec::new(), helps: Vec::new(), suggestions: Vec::new(), - lint: &DYNAMIC, + lint: DYNAMIC, } .into() }) @@ -131,7 +131,7 @@ impl DynamicError { notes: Vec::new(), helps: Vec::new(), suggestions: Vec::new(), - lint: &DYNAMIC, + lint: DYNAMIC, } .into() } @@ -189,7 +189,7 @@ impl DynamicError { notes, helps, suggestions: Vec::new(), - lint: &DYNAMIC, + lint: DYNAMIC, } .into()) } diff --git a/crates/rpl_interface/src/callbacks.rs b/crates/rpl_interface/src/callbacks.rs index 9925cb39..4f88dd68 100644 --- a/crates/rpl_interface/src/callbacks.rs +++ b/crates/rpl_interface/src/callbacks.rs @@ -113,7 +113,7 @@ impl rustc_driver::Callbacks for RplCallbacks { self.pattern_paths .as_ref() .map_or_else(collect_default_patterns, |pattern_paths| { - collect_file_from_string_args(&pattern_paths, || { + collect_file_from_string_args(pattern_paths, || { EarlyDiagCtxt::new(config.opts.error_format).early_fatal(ErrorFound) }) }) @@ -192,7 +192,7 @@ impl rustc_driver::Callbacks for RplCallbacks { self.pattern_paths .as_ref() .map_or_else(collect_default_patterns, |pattern_paths| { - collect_file_from_string_args(&pattern_paths, || tcx.dcx().emit_fatal(ErrorFound)) + collect_file_from_string_args(pattern_paths, || tcx.dcx().emit_fatal(ErrorFound)) }) }); From 80ebb71422de4a8096d67eebfc6cffa9f68c8d0b Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Wed, 30 Jul 2025 23:44:18 +0800 Subject: [PATCH 15/17] Fix order of built-in patterns --- crates/rpl_meta/src/cli.rs | 52 +++++++++---------- tests/ui/clippy/eager_transmute.normal.stderr | 20 +++---- .../cve_2020_35892_3/cve_2020_35892_3.stderr | 22 ++++---- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/crates/rpl_meta/src/cli.rs b/crates/rpl_meta/src/cli.rs index fa7380aa..49c51dc2 100644 --- a/crates/rpl_meta/src/cli.rs +++ b/crates/rpl_meta/src/cli.rs @@ -29,51 +29,51 @@ pub fn collect_default_patterns() -> Vec<(PathBuf, String)> { default_patterns!( // Clippy lints "clippy/cast-slice-different-sizes.rpl", - "clippy/mut-from-ref.rpl", - "clippy/transmute-int-to-non-zero.rpl", - "clippy/unsound-collection-transmute.rpl", "clippy/cast-slice-from-raw-parts.rpl", - "clippy/not-unsafe-ptr-arg-deref.rpl", - "clippy/transmute-null-to-fn.rpl", - "clippy/wrong-transmute.rpl", "clippy/eager-transmute.rpl", - "clippy/size-of-in-element-count.rpl", - "clippy/transmuting-null.rpl", - "clippy/zst-offset.rpl", "clippy/from-raw-with-void-ptr.rpl", + "clippy/mem-replace-with-uninit.rpl", + "clippy/mut-from-ref.rpl", + "clippy/not-unsafe-ptr-arg-deref.rpl", + "clippy/size-of-in-element-count.rpl", "clippy/swap-ptr-to-ref.rpl", + "clippy/transmute-int-to-non-zero.rpl", + "clippy/transmute-null-to-fn.rpl", + "clippy/transmuting-null.rpl", "clippy/uninit-assumed-init.rpl", - "clippy/mem-replace-with-uninit.rpl", "clippy/uninit-vec.rpl", + "clippy/unsound-collection-transmute.rpl", + "clippy/wrong-transmute.rpl", + "clippy/zst-offset.rpl", // CVE patterns "cve/CVE-2018-20992.rpl", - "cve/CVE-2020-25016.rpl", - "cve/CVE-2020-35877.rpl", - "cve/CVE-2020-35892-3.rpl", - "cve/CVE-2021-25904.rpl", - "cve/CVE-2022-23639.rpl", "cve/CVE-2018-21000.rpl", - "cve/CVE-2020-35860.rpl", - "cve/CVE-2020-35881.rpl", - "cve/CVE-2020-35898-9.rpl", - "cve/CVE-2021-25905.rpl", - "cve/CVE-2024-27284.rpl", "cve/CVE-2019-15548.rpl", - "cve/CVE-2020-35862.rpl", - "cve/CVE-2020-35887.rpl", - "cve/CVE-2020-35901-2.rpl", - "cve/CVE-2021-27376.rpl", "cve/CVE-2019-16138.rpl", + "cve/CVE-2020-25016.rpl", + "cve/CVE-2020-35860.rpl", + "cve/CVE-2020-35862.rpl", "cve/CVE-2020-35873.rpl", + "cve/CVE-2020-35877.rpl", + "cve/CVE-2020-35881.rpl", + "cve/CVE-2020-35887.rpl", "cve/CVE-2020-35888.rpl", + "cve/CVE-2020-35892-3.rpl", + "cve/CVE-2020-35898-9.rpl", + "cve/CVE-2020-35901-2.rpl", "cve/CVE-2020-35907.rpl", + "cve/CVE-2021-25904.rpl", + "cve/CVE-2021-25905.rpl", + "cve/CVE-2021-27376.rpl", "cve/CVE-2021-29941-2.rpl", + "cve/CVE-2022-23639.rpl", + "cve/CVE-2024-27284.rpl", // Common patterns based on Rust's UB "ub/allow-unchecked.rpl", - "ub/private-or-generic-function-marked-inline.rpl", - "ub/transmute-to-bool.rpl", "ub/manually-drop.rpl", + "ub/private-or-generic-function-marked-inline.rpl", "ub/transmute-int-to-ptr.rpl", + "ub/transmute-to-bool.rpl", ) } diff --git a/tests/ui/clippy/eager_transmute.normal.stderr b/tests/ui/clippy/eager_transmute.normal.stderr index 9b85c1b6..eeecf560 100644 --- a/tests/ui/clippy/eager_transmute.normal.stderr +++ b/tests/ui/clippy/eager_transmute.normal.stderr @@ -120,16 +120,6 @@ error: this transmute is always evaluated eagerly, even if the condition is fals LL | (op < 4).then_some(std::mem::transmute::<_, Opcode>(op)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: transmute from a `u8` to a `NonZero` - --> tests/ui/clippy/eager_transmute.rs:131:62 - | -LL | let _: Option> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) }); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(v1)` - | - = help: consider using `NonZero::new_unchecked` instead, or use `NonZero::new` if you want to handle the zero case safely - = note: `-D rpl::transmute-int-to-non-zero` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(rpl::transmute_int_to_non_zero)]` - error: this transmute is always evaluated eagerly, even if the condition is false --> tests/ui/clippy/eager_transmute.rs:131:62 | @@ -184,5 +174,15 @@ LL | (v2 < NonZero::new(255u8).unwrap()).then_some(unsafe { std::mem::tr | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +error: transmute from a `u8` to a `NonZero` + --> tests/ui/clippy/eager_transmute.rs:131:62 + | +LL | let _: Option> = (v1 > 0).then_some(unsafe { std::mem::transmute(v1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZero::new_unchecked(v1)` + | + = help: consider using `NonZero::new_unchecked` instead, or use `NonZero::new` if you want to handle the zero case safely + = note: `-D rpl::transmute-int-to-non-zero` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(rpl::transmute_int_to_non_zero)]` + error: aborting due to 29 previous errors diff --git a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr index cb321071..e29a8143 100644 --- a/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr +++ b/tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.stderr @@ -11,6 +11,17 @@ LL | unsafe { &(*(self.mem.offset(index as isize))) } = note: `-D rpl::unchecked-pointer-offset` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(rpl::unchecked_pointer_offset)]` +error: it is an undefined behavior to offset a pointer using an unchecked integer + --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:49:33 + | +LL | elem_ptr = self.mem.offset(offset as isize); + | -------- ^^^^^^^^^^^^^^^^^^^^^^^ offset here + | | + | pointer used here + | + = help: check whether it's in bound before offsetting + = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset + error: pointer out of bound --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:57:25 | @@ -30,17 +41,6 @@ LL | last_elem_ptr = self.mem.offset(self.len as isize); | ^^^^^^^^ = note: `#[deny(rpl::offset_by_one)]` on by default -error: it is an undefined behavior to offset a pointer using an unchecked integer - --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:49:33 - | -LL | elem_ptr = self.mem.offset(offset as isize); - | -------- ^^^^^^^^^^^^^^^^^^^^^^^ offset here - | | - | pointer used here - | - = help: check whether it's in bound before offsetting - = note: See the safety section in https://doc.rust-lang.org/std/primitive.pointer.html#method.offset - error: it usually isn't necessary to apply #[inline] to generic functions --> tests/ui/cve/cve_2020_35892_3/cve_2020_35892_3.rs:37:5 | From bbad5f9d4a00bed7c1c3e0e47777b1457de9d4a5 Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Wed, 30 Jul 2025 23:45:07 +0800 Subject: [PATCH 16/17] Fix forgotten slashes --- crates/rpl_meta/src/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rpl_meta/src/cli.rs b/crates/rpl_meta/src/cli.rs index 49c51dc2..3982681a 100644 --- a/crates/rpl_meta/src/cli.rs +++ b/crates/rpl_meta/src/cli.rs @@ -12,7 +12,7 @@ pub fn collect_default_patterns() -> Vec<(PathBuf, String)> { macro_rules! default_pattern { ($path:literal) => { ( - PathBuf::from(concat!("/rpl/docs/patterns-pest", $path)), + PathBuf::from(concat!("/rpl/docs/patterns-pest/", $path)), include_str!(concat!("../../../docs/patterns-pest/", $path)).to_owned(), ) }; From 2280d52aa7700652547e1ede5560458ea76c514a Mon Sep 17 00:00:00 2001 From: TheVeryDarkness <3266343194@qq.com> Date: Thu, 31 Jul 2025 00:26:36 +0800 Subject: [PATCH 17/17] Update comments --- crates/rpl_meta/src/cli.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/rpl_meta/src/cli.rs b/crates/rpl_meta/src/cli.rs index 3982681a..595af566 100644 --- a/crates/rpl_meta/src/cli.rs +++ b/crates/rpl_meta/src/cli.rs @@ -7,8 +7,18 @@ use std::sync::Arc; use crate::RPLMetaError; +/// Collect default patterns (paths and contents) from the repository. +/// +/// The patterns are embedded in the binary, and their paths are *absolute paths*, +/// **as if** the root of the repository is `/rpl/`. Their contents are +/// collected at compile time, and won't change unless re-compiled. +/// +/// This is used to provide a set of default patterns that can be used +/// by the user without setting up anything. pub fn collect_default_patterns() -> Vec<(PathBuf, String)> { - /// Please pass a path related to docs/patterns-pest + /// This macro will return a tuple of the path and the content of the file. + /// + /// Please pass a path related to `docs/patterns-pest`. macro_rules! default_pattern { ($path:literal) => { (