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