1
1
#![ feature( rustc_private) ]
2
2
#![ warn( unused_extern_crates) ]
3
+ #![ recursion_limit = "256" ]
3
4
4
5
extern crate rustc_data_structures;
5
6
extern crate rustc_hir;
6
7
extern crate rustc_index;
7
8
extern crate rustc_middle;
8
9
extern crate rustc_span;
9
10
10
- use clippy_utils:: { diagnostics:: span_lint_and_help, match_def_path, ty:: match_type } ;
11
+ use clippy_utils:: { diagnostics:: span_lint_and_help, match_def_path, ty:: implements_trait , get_trait_def_id } ;
11
12
use rustc_data_structures:: fx:: FxHashMap ;
12
13
use rustc_hir:: { def:: Res , Expr , ExprKind , QPath , TyKind } ;
13
14
use rustc_index:: vec:: Idx ;
14
15
use rustc_lint:: { LateContext , LateLintPass } ;
15
16
use rustc_middle:: ty:: { AdtDef , AdtKind , TyKind as MiddleTyKind } ;
16
17
use rustc_span:: { def_id:: DefId , Span } ;
17
- use solana_lints:: { paths, utils :: visit_expr_no_bodies } ;
18
+ use solana_lints:: paths;
18
19
19
20
use if_chain:: if_chain;
20
21
@@ -61,31 +62,58 @@ struct TypeCosplay {
61
62
deser_types : FxHashMap < AdtKind , Vec < ( DefId , Span ) > > ,
62
63
}
63
64
65
+ // get type X
66
+ // check if implements Discriminator
67
+ // check corresponding function call type:
68
+ // if !try_deserialize
69
+ // emit lint with warning to use try_deserialize (because any types deriving Discriminator should since it guaranteed to check discrim)
70
+
64
71
impl < ' tcx > LateLintPass < ' tcx > for TypeCosplay {
65
72
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
66
73
if_chain ! {
67
- if !expr. span. from_expansion( ) ;
68
- if let ExprKind :: Call ( fnc_expr, args_exprs) = expr. kind;
69
- if is_deserialize_function( cx, fnc_expr) ;
74
+ if let ExprKind :: Call ( fnc_expr, _args_exprs) = expr. kind;
75
+ // TODO: is the following if statement really needed?? don't think it's ever used.
76
+ // all it does is check if AccountInfo.data is referenced...but what bytes we deser
77
+ // from shouldn't impact if this is a type-cosplay issue or not.
70
78
// walk each argument expression and see if the data field is referenced
71
- if args_exprs. iter( ) . any( |arg| {
72
- visit_expr_no_bodies( arg, |expr| contains_data_field_reference( cx, expr) )
73
- } ) ;
79
+ // if args_exprs.iter().any(|arg| {
80
+ // visit_expr_no_bodies(arg, |expr| contains_data_field_reference(cx, expr))
81
+ // });
74
82
// get the type that the function was called on, ie X in X::deser()
75
83
if let ExprKind :: Path ( qpath) = & fnc_expr. kind;
76
84
if let QPath :: TypeRelative ( ty, _) = qpath;
77
85
if let TyKind :: Path ( ty_qpath) = & ty. kind;
78
86
let res = cx. typeck_results( ) . qpath_res( ty_qpath, ty. hir_id) ;
79
87
if let Res :: Def ( _, def_id) = res;
88
+ let middle_ty = cx. tcx. type_of( def_id) ;
89
+ if let Some ( trait_did) = get_trait_def_id( cx, & paths:: ANCHOR_DISCRIMINATOR_TRAIT ) ;
80
90
then {
81
- let middle_ty = cx. tcx. type_of( def_id) ;
82
- if let MiddleTyKind :: Adt ( adt_def, _) = middle_ty. kind( ) {
83
- let adt_kind = adt_def. adt_kind( ) ;
84
- let def_id = adt_def. did( ) ;
85
- if let Some ( vec) = self . deser_types. get_mut( & adt_kind) {
86
- vec. push( ( def_id, ty. span) ) ;
87
- } else {
88
- self . deser_types. insert( adt_kind, vec![ ( def_id, ty. span) ] ) ;
91
+ if implements_trait( cx, middle_ty, trait_did, & [ ] ) {
92
+ if let Some ( def_id) = cx. typeck_results( ) . type_dependent_def_id( fnc_expr. hir_id) {
93
+ if !match_def_path( cx, def_id, & paths:: ANCHOR_TRY_DESERIALIZE ) {
94
+ span_lint_and_help(
95
+ cx,
96
+ TYPE_COSPLAY ,
97
+ fnc_expr. span,
98
+ & format!( "{} type implements the anchor_lang::Discriminator trait. If you are using #[account] to derive Discriminator, use try_deserialize() instead." ,
99
+ middle_ty) ,
100
+ None ,
101
+ "otherwise, make sure you are accounting for this type's discriminator in your deserialization function"
102
+ ) ;
103
+ }
104
+ }
105
+ } else {
106
+ // currently only checks borsh::try_from_slice()
107
+ if is_deserialize_function( cx, fnc_expr) {
108
+ if let MiddleTyKind :: Adt ( adt_def, _) = middle_ty. kind( ) {
109
+ let adt_kind = adt_def. adt_kind( ) ;
110
+ let def_id = adt_def. did( ) ;
111
+ if let Some ( vec) = self . deser_types. get_mut( & adt_kind) {
112
+ vec. push( ( def_id, ty. span) ) ;
113
+ } else {
114
+ self . deser_types. insert( adt_kind, vec![ ( def_id, ty. span) ] ) ;
115
+ }
116
+ }
89
117
}
90
118
}
91
119
}
@@ -126,19 +154,19 @@ fn is_deserialize_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
126
154
}
127
155
}
128
156
129
- fn contains_data_field_reference ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
130
- if_chain ! {
131
- if let ExprKind :: Field ( obj_expr, ident) = expr. kind;
132
- if ident. as_str( ) == "data" ;
133
- let ty = cx. typeck_results( ) . expr_ty( obj_expr) ;
134
- if match_type( cx, ty, & paths:: SOLANA_PROGRAM_ACCOUNT_INFO ) ;
135
- then {
136
- true
137
- } else {
138
- false
139
- }
140
- }
141
- }
157
+ // fn contains_data_field_reference(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
158
+ // if_chain! {
159
+ // if let ExprKind::Field(obj_expr, ident) = expr.kind;
160
+ // if ident.as_str() == "data";
161
+ // let ty = cx.typeck_results().expr_ty(obj_expr);
162
+ // if match_type(cx, ty, &paths::SOLANA_PROGRAM_ACCOUNT_INFO);
163
+ // then {
164
+ // true
165
+ // } else {
166
+ // false
167
+ // }
168
+ // }
169
+ // }
142
170
143
171
fn check_enums ( cx : & LateContext < ' _ > , enums : & Vec < ( DefId , Span ) > ) {
144
172
#[ allow( clippy:: comparison_chain) ]
@@ -188,7 +216,7 @@ fn has_discriminant(cx: &LateContext, adt: AdtDef, num_struct_types: usize, span
188
216
"type does not have a proper discriminant. It may be indistinguishable when deserialized." ,
189
217
None ,
190
218
"add an enum with at least as many variants as there are struct definitions"
191
- )
219
+ ) ;
192
220
}
193
221
}
194
222
}
@@ -208,6 +236,11 @@ fn insecure_3() {
208
236
dylint_testing:: ui_test_example ( env ! ( "CARGO_PKG_NAME" ) , "insecure-3" ) ;
209
237
}
210
238
239
+ #[ test]
240
+ fn insecure_anchor ( ) {
241
+ dylint_testing:: ui_test_example ( env ! ( "CARGO_PKG_NAME" ) , "insecure-anchor" ) ;
242
+ }
243
+
211
244
#[ test]
212
245
fn secure ( ) {
213
246
dylint_testing:: ui_test_example ( env ! ( "CARGO_PKG_NAME" ) , "secure" ) ;
0 commit comments