Merge pull request #6090 from WalterBright/add-safe.d

refactor: move safe code to safe.d
This commit is contained in:
Andrei Alexandrescu 2016-08-27 21:17:18 -04:00 committed by GitHub
commit 2777fdad0e
4 changed files with 184 additions and 146 deletions

View file

@ -13,6 +13,7 @@ module ddmd.expression;
import core.stdc.stdarg;
import core.stdc.stdio;
import core.stdc.string;
import ddmd.access;
import ddmd.aggregate;
import ddmd.aliasthis;
@ -59,6 +60,7 @@ import ddmd.root.filename;
import ddmd.root.outbuffer;
import ddmd.root.rmem;
import ddmd.root.rootobject;
import ddmd.safe;
import ddmd.sideeffect;
import ddmd.target;
import ddmd.tokens;
@ -80,71 +82,6 @@ void emplaceExp(T : UnionExp)(T* p, Expression e)
memcpy(p, cast(void*)e, e.size);
}
/*************************************************************
* Check for unsafe access in @safe code:
* 1. read overlapped pointers
* 2. write misaligned pointers
* 3. write overlapped storage classes
* Print error if unsafe.
* Params:
* sc = scope
* e = expression to check
* readonly = if access is read-only
* printmsg = print error message if true
* Returns:
* true if error
*/
private bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg)
{
if (e.op != TOKdotvar)
return false;
DotVarExp dve = cast(DotVarExp)e;
if (VarDeclaration v = dve.var.isVarDeclaration())
{
if (sc.intypeof || !sc.func || !sc.func.isSafeBypassingInference())
return false;
auto ad = v.toParent2().isAggregateDeclaration();
if (!ad)
return false;
if (v.overlapped && v.type.hasPointers() && sc.func.setUnsafe())
{
if (printmsg)
e.error("field %s.%s cannot access pointers in @safe code that overlap other fields",
ad.toChars(), v.toChars());
return true;
}
if (readonly || !e.type.isMutable())
return false;
if (v.type.hasPointers() && v.type.toBasetype().ty != Tstruct)
{
if ((ad.type.alignment() < Target.ptrsize ||
(v.offset & (Target.ptrsize - 1))) &&
sc.func.setUnsafe())
{
if (printmsg)
e.error("field %s.%s cannot modify misaligned pointers in @safe code",
ad.toChars(), v.toChars());
return true;
}
}
if (v.overlapUnsafe && sc.func.setUnsafe())
{
if (printmsg)
e.error("field %s.%s cannot modify fields in @safe code that overlap fields with other storage classes",
ad.toChars(), v.toChars());
return true;
}
}
return false;
}
/*************************************************************
* Given var, we need to get the
* right 'this' pointer if var is in an outer class, but our
@ -11306,89 +11243,14 @@ extern (C++) final class CastExp : UnaExp
return ex;
// Check for unsafe casts
if (sc.func && !sc.intypeof)
if (sc.func && !sc.intypeof &&
!isSafeCast(ex, t1b, tob) &&
sc.func.setUnsafe())
{
// Disallow unsafe casts
// Implicit conversions are always safe
if (t1b.implicitConvTo(tob))
goto Lsafe;
if (!tob.hasPointers())
goto Lsafe;
if (tob.ty == Tclass && t1b.ty == Tclass)
{
ClassDeclaration cdfrom = t1b.isClassHandle();
ClassDeclaration cdto = tob.isClassHandle();
int offset;
if (!cdfrom.isBaseOf(cdto, &offset))
goto Lunsafe;
if (cdfrom.isCPPinterface() || cdto.isCPPinterface())
goto Lunsafe;
if (!MODimplicitConv(t1b.mod, tob.mod))
goto Lunsafe;
goto Lsafe;
}
if (tob.ty == Tarray && t1b.ty == Tsarray) // Bugzilla 12502
t1b = t1b.nextOf().arrayOf();
if (tob.ty == Tarray && t1b.ty == Tarray ||
tob.ty == Tpointer && t1b.ty == Tpointer)
{
Type tobn = tob.nextOf().toBasetype();
Type t1bn = t1b.nextOf().toBasetype();
/* From void[] to anything mutable is unsafe because:
* int*[] api;
* void[] av = api;
* int[] ai = cast(int[]) av;
* ai[0] = 7;
* *api[0] crash!
*/
if (t1bn.ty == Tvoid && tobn.isMutable())
{
if (tob.ty == Tarray && ex.op == TOKarrayliteral)
goto Lsafe;
goto Lunsafe;
}
// If the struct is opaque we don't know about the struct members then the cast becomes unsafe
if (tobn.ty == Tstruct && !(cast(TypeStruct)tobn).sym.members ||
t1bn.ty == Tstruct && !(cast(TypeStruct)t1bn).sym.members)
goto Lunsafe;
const t1pointers = t1bn.hasPointers();
const topointers = tobn.hasPointers();
if (t1pointers && !topointers && tobn.isMutable())
goto Lunsafe;
if (!t1pointers && topointers)
goto Lunsafe;
if (!topointers &&
tobn.ty != Tfunction && t1bn.ty != Tfunction &&
(tob.ty == Tarray || tobn.size() <= t1bn.size()) &&
MODimplicitConv(t1bn.mod, tobn.mod))
{
goto Lsafe;
}
}
Lunsafe:
if (sc.func.setUnsafe())
{
error("cast from %s to %s not allowed in safe code", e1.type.toChars(), to.toChars());
return new ErrorExp();
}
error("cast from %s to %s not allowed in safe code", e1.type.toChars(), to.toChars());
return new ErrorExp();
}
Lsafe:
return ex;
}

View file

@ -209,7 +209,7 @@ FRONT_SRCS=$(addsuffix .d,access aggregate aliasthis apply argtypes arrayop \
globals hdrgen id identifier impcnvtab imphint init inline intrange \
json lexer lib link mars mtype nogc nspace opover optimize parse sapply \
sideeffect statement staticassert target tokens traits utf visitor \
typinf utils statementsem)
typinf utils statementsem safe)
ifeq ($(D_OBJC),1)
FRONT_SRCS += objc.d

175
src/safe.d Normal file
View file

@ -0,0 +1,175 @@
/**
* Compiler implementation of the
* $(LINK2 http://www.dlang.org, D programming language).
*
* Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(DMDSRC _safe.d)
*/
module ddmd.safe;
import core.stdc.stdio;
import ddmd.aggregate;
import ddmd.dclass;
import ddmd.declaration;
import ddmd.dscope;
import ddmd.expression;
import ddmd.mtype;
import ddmd.target;
import ddmd.tokens;
/*************************************************************
* Check for unsafe access in @safe code:
* 1. read overlapped pointers
* 2. write misaligned pointers
* 3. write overlapped storage classes
* Print error if unsafe.
* Params:
* sc = scope
* e = expression to check
* readonly = if access is read-only
* printmsg = print error message if true
* Returns:
* true if error
*/
bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg)
{
if (e.op != TOKdotvar)
return false;
DotVarExp dve = cast(DotVarExp)e;
if (VarDeclaration v = dve.var.isVarDeclaration())
{
if (sc.intypeof || !sc.func || !sc.func.isSafeBypassingInference())
return false;
auto ad = v.toParent2().isAggregateDeclaration();
if (!ad)
return false;
if (v.overlapped && v.type.hasPointers() && sc.func.setUnsafe())
{
if (printmsg)
e.error("field %s.%s cannot access pointers in @safe code that overlap other fields",
ad.toChars(), v.toChars());
return true;
}
if (readonly || !e.type.isMutable())
return false;
if (v.type.hasPointers() && v.type.toBasetype().ty != Tstruct)
{
if ((ad.type.alignment() < Target.ptrsize ||
(v.offset & (Target.ptrsize - 1))) &&
sc.func.setUnsafe())
{
if (printmsg)
e.error("field %s.%s cannot modify misaligned pointers in @safe code",
ad.toChars(), v.toChars());
return true;
}
}
if (v.overlapUnsafe && sc.func.setUnsafe())
{
if (printmsg)
e.error("field %s.%s cannot modify fields in @safe code that overlap fields with other storage classes",
ad.toChars(), v.toChars());
return true;
}
}
return false;
}
/**********************************************
* Determine if it is @safe to cast e from tfrom to tto.
* Params:
* e = expression to be cast
* tfrom = type of e
* tto = type to cast e to
* Returns:
* true if @safe
*/
bool isSafeCast(Expression e, Type tfrom, Type tto)
{
// Implicit conversions are always safe
if (tfrom.implicitConvTo(tto))
return true;
if (!tto.hasPointers())
return true;
auto tfromb = tfrom.toBasetype();
auto ttob = tto.toBasetype();
if (ttob.ty == Tclass && tfrom.ty == Tclass)
{
ClassDeclaration cdfrom = tfrom.isClassHandle();
ClassDeclaration cdto = ttob.isClassHandle();
int offset;
if (!cdfrom.isBaseOf(cdto, &offset))
return false;
if (cdfrom.isCPPinterface() || cdto.isCPPinterface())
return false;
if (!MODimplicitConv(tfrom.mod, ttob.mod))
return false;
return true;
}
if (ttob.ty == Tarray && tfrom.ty == Tsarray) // Bugzilla 12502
tfrom = tfrom.nextOf().arrayOf();
if (ttob.ty == Tarray && tfrom.ty == Tarray ||
ttob.ty == Tpointer && tfrom.ty == Tpointer)
{
Type ttobn = ttob.nextOf().toBasetype();
Type tfromn = tfrom.nextOf().toBasetype();
/* From void[] to anything mutable is unsafe because:
* int*[] api;
* void[] av = api;
* int[] ai = cast(int[]) av;
* ai[0] = 7;
* *api[0] crash!
*/
if (tfromn.ty == Tvoid && ttobn.isMutable())
{
if (ttob.ty == Tarray && e.op == TOKarrayliteral)
return true;
return false;
}
// If the struct is opaque we don't know about the struct members then the cast becomes unsafe
if (ttobn.ty == Tstruct && !(cast(TypeStruct)ttobn).sym.members ||
tfromn.ty == Tstruct && !(cast(TypeStruct)tfromn).sym.members)
return false;
const frompointers = tfromn.hasPointers();
const topointers = ttobn.hasPointers();
if (frompointers && !topointers && ttobn.isMutable())
return false;
if (!frompointers && topointers)
return false;
if (!topointers &&
ttobn.ty != Tfunction && tfromn.ty != Tfunction &&
(ttob.ty == Tarray || ttobn.size() <= tfromn.size()) &&
MODimplicitConv(tfromn.mod, ttobn.mod))
{
return true;
}
}
return false;
}

View file

@ -151,6 +151,7 @@ FRONT_SRCS=access.d aggregate.d aliasthis.d apply.d argtypes.d arrayop.d \
impcnvtab.d init.d inline.d intrange.d json.d lexer.d lib.d link.d \
mars.d mtype.d nogc.d nspace.d objc_stubs.d opover.d optimize.d parse.d \
sapply.d sideeffect.d statement.d staticassert.d target.d tokens.d \
safe.d \
traits.d utf.d utils.d visitor.d libomf.d scanomf.d typinf.d \
libmscoff.d scanmscoff.d statementsem.d