Skip to main content

rapx/analysis/api_dependency/
fuzzable.rs

1#[cfg(not(rapx_ge_100))]
2use rustc_hir::LangItem;
3#[cfg(rapx_ge_100)]
4use rustc_hir::attrs::lang_items::LangItem;
5use rustc_middle::ty::{self, Ty, TyCtxt, TyKind};
6use rustc_span::sym;
7
8fn is_fuzzable_std_ty<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, depth: usize) -> bool {
9    match ty.kind() {
10        ty::Adt(def, args) => {
11            if tcx.is_lang_item(def.did(), LangItem::String) {
12                return true;
13            }
14            if tcx.is_diagnostic_item(sym::Vec, def.did())
15                && is_fuzzable_ty(args.type_at(0), tcx, depth + 1)
16            {
17                return true;
18            }
19            if tcx.is_diagnostic_item(sym::Arc, def.did())
20                && is_fuzzable_ty(args.type_at(0), tcx, depth + 1)
21            {
22                return true;
23            }
24            false
25        }
26        _ => false,
27    }
28}
29
30fn is_non_fuzzable_std_ty<'tcx>(ty: Ty<'tcx>, _tcx: TyCtxt<'tcx>) -> bool {
31    let name = format!("{}", ty);
32    match name.as_str() {
33        "core::alloc::LayoutError" => return true,
34        _ => {}
35    }
36    false
37}
38
39const MAX_DEPTH: usize = 64;
40pub fn is_fuzzable_ty<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, depth: usize) -> bool {
41    if depth > MAX_DEPTH {
42        return false;
43    }
44
45    if is_fuzzable_std_ty(ty, tcx, depth + 1) {
46        return true;
47    }
48
49    if is_non_fuzzable_std_ty(ty, tcx) {
50        return false;
51    }
52
53    match ty.kind() {
54        // Basical data type
55        TyKind::Bool
56        | TyKind::Char
57        | TyKind::Int(_)
58        | TyKind::Uint(_)
59        | TyKind::Float(_)
60        | TyKind::Str => true,
61
62        // Infer
63        TyKind::Infer(
64            ty::InferTy::IntVar(_)
65            | ty::InferTy::FreshIntTy(_)
66            | ty::InferTy::FloatVar(_)
67            | ty::InferTy::FreshFloatTy(_),
68        ) => true,
69
70        // Reference, Array, Slice
71        TyKind::Ref(_, inner_ty, _) | TyKind::Slice(inner_ty) => {
72            is_fuzzable_ty(inner_ty.peel_refs(), tcx, depth + 1)
73        }
74
75        TyKind::Array(inner_ty, const_) => {
76            if const_.try_to_value().is_none() {
77                return false;
78            }
79            is_fuzzable_ty(inner_ty.peel_refs(), tcx, depth + 1)
80        }
81
82        // Tuple
83        TyKind::Tuple(tys) => tys
84            .iter()
85            .all(|inner_ty| is_fuzzable_ty(inner_ty.peel_refs(), tcx, depth + 1)),
86
87        // ADT
88        TyKind::Adt(adt_def, args) => {
89            if adt_def.is_union() {
90                return false;
91            }
92
93            if adt_def.is_variant_list_non_exhaustive() {
94                return false;
95            }
96
97            // if adt contain region, then we consider it non-fuzzable
98            if args.iter().any(|arg| arg.as_region().is_some()) {
99                return false;
100            }
101
102            // if any field is not public or not fuzzable, then we consider it non-fuzzable
103            if !adt_def.all_fields().all(|field| {
104                #[cfg(not(rapx_ge_99))]
105                let field_ty = field.ty(tcx, args);
106                #[cfg(rapx_ge_99)]
107                let field_ty = field.ty(tcx, args).skip_norm_wip();
108                field.vis.is_public() && is_fuzzable_ty(field_ty, tcx, depth + 1)
109            }) {
110                return false;
111            }
112
113            // empty enum cannot be instantiated
114            if adt_def.is_enum() && adt_def.variants().is_empty() {
115                return false;
116            }
117
118            true
119        }
120
121        // Other types are not fuzzable by default
122        _ => false,
123    }
124}