rapx/analysis/core/api_dependency/
fuzzable.rs

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