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