1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(DMDSRC _safe.d)
9  */
10 
11 module ddmd.safe;
12 
13 import core.stdc.stdio;
14 
15 import ddmd.aggregate;
16 import ddmd.dclass;
17 import ddmd.declaration;
18 import ddmd.dscope;
19 import ddmd.expression;
20 import ddmd.mtype;
21 import ddmd.target;
22 import ddmd.tokens;
23 
24 
25 /*************************************************************
26  * Check for unsafe access in @safe code:
27  * 1. read overlapped pointers
28  * 2. write misaligned pointers
29  * 3. write overlapped storage classes
30  * Print error if unsafe.
31  * Params:
32  *      sc = scope
33  *      e = expression to check
34  *      readonly = if access is read-only
35  *      printmsg = print error message if true
36  * Returns:
37  *      true if error
38  */
39 
40 bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg)
41 {
42     if (e.op != TOKdotvar)
43         return false;
44     DotVarExp dve = cast(DotVarExp)e;
45     if (VarDeclaration v = dve.var.isVarDeclaration())
46     {
47         if (sc.intypeof || !sc.func || !sc.func.isSafeBypassingInference())
48             return false;
49 
50         auto ad = v.toParent2().isAggregateDeclaration();
51         if (!ad)
52             return false;
53 
54         if (v.overlapped && v.type.hasPointers() && sc.func.setUnsafe())
55         {
56             if (printmsg)
57                 e.error("field %s.%s cannot access pointers in @safe code that overlap other fields",
58                     ad.toChars(), v.toChars());
59             return true;
60         }
61 
62         if (readonly || !e.type.isMutable())
63             return false;
64 
65         if (v.type.hasPointers() && v.type.toBasetype().ty != Tstruct)
66         {
67             if ((ad.type.alignment() < Target.ptrsize ||
68                  (v.offset & (Target.ptrsize - 1))) &&
69                 sc.func.setUnsafe())
70             {
71                 if (printmsg)
72                     e.error("field %s.%s cannot modify misaligned pointers in @safe code",
73                         ad.toChars(), v.toChars());
74                 return true;
75             }
76         }
77 
78         if (v.overlapUnsafe && sc.func.setUnsafe())
79         {
80              if (printmsg)
81                  e.error("field %s.%s cannot modify fields in @safe code that overlap fields with other storage classes",
82                     ad.toChars(), v.toChars());
83              return true;
84         }
85     }
86     return false;
87 }
88 
89 
90 /**********************************************
91  * Determine if it is @safe to cast e from tfrom to tto.
92  * Params:
93  *      e = expression to be cast
94  *      tfrom = type of e
95  *      tto = type to cast e to
96  * Returns:
97  *      true if @safe
98  */
99 bool isSafeCast(Expression e, Type tfrom, Type tto)
100 {
101     // Implicit conversions are always safe
102     if (tfrom.implicitConvTo(tto))
103         return true;
104 
105     if (!tto.hasPointers())
106         return true;
107 
108     auto tfromb = tfrom.toBasetype();
109     auto ttob = tto.toBasetype();
110 
111     if (ttob.ty == Tclass && tfrom.ty == Tclass)
112     {
113         ClassDeclaration cdfrom = tfrom.isClassHandle();
114         ClassDeclaration cdto = ttob.isClassHandle();
115 
116         int offset;
117         if (!cdfrom.isBaseOf(cdto, &offset))
118             return false;
119 
120         if (cdfrom.isCPPinterface() || cdto.isCPPinterface())
121             return false;
122 
123         if (!MODimplicitConv(tfrom.mod, ttob.mod))
124             return false;
125         return true;
126     }
127 
128     if (ttob.ty == Tarray && tfrom.ty == Tsarray) // Bugzilla 12502
129         tfrom = tfrom.nextOf().arrayOf();
130 
131     if (ttob.ty == Tarray   && tfrom.ty == Tarray ||
132         ttob.ty == Tpointer && tfrom.ty == Tpointer)
133     {
134         Type ttobn = ttob.nextOf().toBasetype();
135         Type tfromn = tfrom.nextOf().toBasetype();
136 
137         /* From void[] to anything mutable is unsafe because:
138          *  int*[] api;
139          *  void[] av = api;
140          *  int[] ai = cast(int[]) av;
141          *  ai[0] = 7;
142          *  *api[0] crash!
143          */
144         if (tfromn.ty == Tvoid && ttobn.isMutable())
145         {
146             if (ttob.ty == Tarray && e.op == TOKarrayliteral)
147                 return true;
148             return false;
149         }
150 
151         // If the struct is opaque we don't know about the struct members then the cast becomes unsafe
152         if (ttobn.ty == Tstruct && !(cast(TypeStruct)ttobn).sym.members ||
153             tfromn.ty == Tstruct && !(cast(TypeStruct)tfromn).sym.members)
154             return false;
155 
156         const frompointers = tfromn.hasPointers();
157         const topointers = ttobn.hasPointers();
158 
159         if (frompointers && !topointers && ttobn.isMutable())
160             return false;
161 
162         if (!frompointers && topointers)
163             return false;
164 
165         if (!topointers &&
166             ttobn.ty != Tfunction && tfromn.ty != Tfunction &&
167             (ttob.ty == Tarray || ttobn.size() <= tfromn.size()) &&
168             MODimplicitConv(tfromn.mod, ttobn.mod))
169         {
170             return true;
171         }
172     }
173     return false;
174 }
175