Skip to content

Commit 8db71ae

Browse files
committed
Implement null dereference check
1 parent 92350aa commit 8db71ae

File tree

25 files changed

+384
-90
lines changed

25 files changed

+384
-90
lines changed

changelog/dmd.nullderefcheck.dd

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
On null dereference check is now available
2+
3+
A new check is implemented for when a null dereference occurs.
4+
5+
This can be enabled by using ``-check=nullderef=on``
6+
What happens may be customized by the ``-checkaction`` switch and by setting a new handler in ``core.exception``.
7+
8+
Due to issues in dmd's backend, not all pointer dereferences are guaranteed to get a check.

compiler/src/dmd/astenums.d

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,14 @@ enum CHECKACTION : ubyte
472472
context, /// call D assert with the error context on failure
473473
}
474474

475+
/// What should happen when a dereference on null happens
476+
enum CHECKACTIONDEREF : ubyte
477+
{
478+
D, /// call D hook function
479+
Assert, /// lower to an assert
480+
Ignore /// ignore
481+
}
482+
475483
extern (C++) struct structalign_t
476484
{
477485
private:

compiler/src/dmd/backend/codebuilder.d

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,10 +399,7 @@ assert(op != BADINS);
399399
@trusted
400400
code* last()
401401
{
402-
// g++ and clang++ complain about offsetof() because of the code::code() constructor.
403-
// return (code *)((char *)pTail - offsetof(code, next));
404-
// So do our own.
405-
return cast(code*)(cast(void*)pTail - (cast(void*)&(*pTail).next - cast(void*)*pTail));
402+
return cast(code*)(cast(void*)pTail - code.next.offsetof);
406403
}
407404

408405
/*************************************

compiler/src/dmd/backend/drtlsym.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ Symbol* getRtlsym(RTLSYM i) @trusted
8989
case RTLSYM.DARRAYP: symbolz(ps,FL.func,FREGSAVED,"_d_arrayboundsp", SFLexit, t); break;
9090
case RTLSYM.DARRAY_SLICEP: symbolz(ps,FL.func,FREGSAVED,"_d_arraybounds_slicep", SFLexit, t); break;
9191
case RTLSYM.DARRAY_INDEXP: symbolz(ps,FL.func,FREGSAVED,"_d_arraybounds_indexp", SFLexit, t); break;
92+
case RTLSYM.DNULLP: symbolz(ps,FL.func,FREGSAVED,"_d_nullpointerp", SFLexit, t); break;
9293
case RTLSYM.DINVARIANT: symbolz(ps,FL.func,FREGSAVED,"_D2rt10invariant_12_d_invariantFC6ObjectZv", 0, tsdlib); break;
9394
case RTLSYM.MEMCPY: symbolz(ps,FL.func,FREGSAVED,"memcpy", 0, t); break;
9495
case RTLSYM.MEMSET8: symbolz(ps,FL.func,FREGSAVED,"memset", 0, t); break;

compiler/src/dmd/backend/rtlsym.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ enum RTLSYM
3333
DARRAYP,
3434
DARRAY_SLICEP,
3535
DARRAY_INDEXP,
36+
DNULLP,
3637
DINVARIANT,
3738
MEMCPY,
3839
MEMSET8,

compiler/src/dmd/cli.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,7 @@ struct CLIUsage
11901190
=invariant[=[on|off]] Class/struct invariants
11911191
=out[=[on|off]] Out contracts
11921192
=switch[=[on|off]] Final switch failure checking
1193+
=nullderef[=[on|off]] Null dereference error
11931194
=on Enable all assertion checking
11941195
(default for non-release builds)
11951196
=off Disable all assertion checking

compiler/src/dmd/dfa/fast/report.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ struct DFAReporter
5757
var.var.ident.toChars);
5858
}
5959

60-
void onEndOfScope(FuncDeclaration fd, ref Loc loc)
60+
void onEndOfScope(FuncDeclaration fd, ref const(Loc) loc)
6161
{
6262
// this is where we validate escapes, for a specific location
6363

compiler/src/dmd/dfa/fast/statement.d

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ final:
582582

583583
dfaCommon.currentDFAScope.haveJumped = true;
584584
dfaCommon.currentDFAScope.haveReturned = true;
585-
analyzer.reporter.onEndOfScope(dfaCommon.currentFunction, exp.loc);
585+
analyzer.reporter.onEndOfScope(dfaCommon.currentFunction, st.loc);
586586
break;
587587

588588
case STMT.Compound:
@@ -859,6 +859,7 @@ final:
859859
dfaCommon.currentDFAScope.inConditional = true;
860860
dfaCommon.setScopeAsLoopyLabel;
861861

862+
if (theCondition !is null)
862863
{
863864
dfaCommon.printStructure((ref OutBuffer ob,
864865
scope PrintPrefixType prefix) => ob.writestring("For condition:\n"));
@@ -873,6 +874,8 @@ final:
873874

874875
expWalker.seeAssert(lrCondition, theCondition.loc, true);
875876
}
877+
else
878+
dfaCommon.currentDFAScope.isLoopyLabelKnownToHaveRun = true;
876879

877880
dfaCommon.printStructure((ref OutBuffer ob,
878881
scope PrintPrefixType prefix) => ob.writestring("For body:\n"));

compiler/src/dmd/frontend.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8266,6 +8266,7 @@ struct Param final
82668266
CHECKENABLE useIn;
82678267
CHECKENABLE useOut;
82688268
CHECKENABLE useArrayBounds;
8269+
CHECKENABLE useNullCheck;
82698270
CHECKENABLE useAssert;
82708271
CHECKENABLE useSwitchError;
82718272
CHECKENABLE boundscheck;
@@ -8349,6 +8350,7 @@ struct Param final
83498350
useIn((CHECKENABLE)0u),
83508351
useOut((CHECKENABLE)0u),
83518352
useArrayBounds((CHECKENABLE)0u),
8353+
useNullCheck((CHECKENABLE)0u),
83528354
useAssert((CHECKENABLE)0u),
83538355
useSwitchError((CHECKENABLE)0u),
83548356
boundscheck((CHECKENABLE)0u),
@@ -8389,7 +8391,7 @@ struct Param final
83898391
timeTraceFile()
83908392
{
83918393
}
8392-
Param(bool obj, bool readStdin = false, bool multiobj = false, bool trace = false, bool tracegc = false, bool vcg_ast = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting useWarnings = (DiagnosticReporting)2u, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = true, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool useGC = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = true, bool rewriteNoExceptionToSeq = false, CppStdRevision cplusplus = (CppStdRevision)201103u, Help help = Help(), Verbose v = Verbose(), Edition edition = (Edition)2023u, void* editionFiles = nullptr, FeatureState useDIP25 = (FeatureState)2u, FeatureState useDIP1000 = (FeatureState)0u, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)0u, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)0u, FeatureState safer = (FeatureState)0u, FeatureState noSharedAccess = (FeatureState)0u, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)0u, FeatureState systemVariables = (FeatureState)0u, bool useFastDFA = false, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, CLIIdentifierTable dIdentifierTable = (CLIIdentifierTable)0u, CLIIdentifierTable cIdentifierTable = (CLIIdentifierTable)0u, _d_dynamicArray< const char > argv0 = {}, Array<const char* > modFileAliasStrings = Array<const char* >(), Array<ImportPathInfo > imppath = Array<ImportPathInfo >(), Array<const char* > fileImppath = Array<const char* >(), _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), bool debugEnabled = false, bool run = false, Array<const char* > runargs = Array<const char* >(), Array<const char* > cppswitches = Array<const char* >(), const char* cpp = nullptr, Array<const char* > objfiles = Array<const char* >(), Array<const char* > linkswitches = Array<const char* >(), Array<bool > linkswitchIsForCC = Array<bool >(), Array<const char* > libfiles = Array<const char* >(), Array<const char* > dllfiles = Array<const char* >(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}, bool fullyQualifiedObjectFiles = false, bool timeTrace = false, uint32_t timeTraceGranularityUs = 500u, const char* timeTraceFile = nullptr) :
8394+
Param(bool obj, bool readStdin = false, bool multiobj = false, bool trace = false, bool tracegc = false, bool vcg_ast = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting useWarnings = (DiagnosticReporting)2u, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = true, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool useGC = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = true, bool rewriteNoExceptionToSeq = false, CppStdRevision cplusplus = (CppStdRevision)201103u, Help help = Help(), Verbose v = Verbose(), Edition edition = (Edition)2023u, void* editionFiles = nullptr, FeatureState useDIP25 = (FeatureState)2u, FeatureState useDIP1000 = (FeatureState)0u, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)0u, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)0u, FeatureState safer = (FeatureState)0u, FeatureState noSharedAccess = (FeatureState)0u, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)0u, FeatureState systemVariables = (FeatureState)0u, bool useFastDFA = false, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useNullCheck = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, CLIIdentifierTable dIdentifierTable = (CLIIdentifierTable)0u, CLIIdentifierTable cIdentifierTable = (CLIIdentifierTable)0u, _d_dynamicArray< const char > argv0 = {}, Array<const char* > modFileAliasStrings = Array<const char* >(), Array<ImportPathInfo > imppath = Array<ImportPathInfo >(), Array<const char* > fileImppath = Array<const char* >(), _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), bool debugEnabled = false, bool run = false, Array<const char* > runargs = Array<const char* >(), Array<const char* > cppswitches = Array<const char* >(), const char* cpp = nullptr, Array<const char* > objfiles = Array<const char* >(), Array<const char* > linkswitches = Array<const char* >(), Array<bool > linkswitchIsForCC = Array<bool >(), Array<const char* > libfiles = Array<const char* >(), Array<const char* > dllfiles = Array<const char* >(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}, bool fullyQualifiedObjectFiles = false, bool timeTrace = false, uint32_t timeTraceGranularityUs = 500u, const char* timeTraceFile = nullptr) :
83938395
obj(obj),
83948396
readStdin(readStdin),
83958397
multiobj(multiobj),
@@ -8441,6 +8443,7 @@ struct Param final
84418443
useIn(useIn),
84428444
useOut(useOut),
84438445
useArrayBounds(useArrayBounds),
8446+
useNullCheck(useNullCheck),
84448447
useAssert(useAssert),
84458448
useSwitchError(useSwitchError),
84468449
boundscheck(boundscheck),

compiler/src/dmd/funcsem.d

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4065,3 +4065,34 @@ bool arrayBoundsCheck(FuncDeclaration fd)
40654065
assert(0);
40664066
}
40674067
}
4068+
4069+
/**********************
4070+
* Check to see if null dereference checking code has to be generated
4071+
*
4072+
* Params:
4073+
* fd = function for which code is to be generated
4074+
* Returns:
4075+
* true if do null dereference checking for the given function
4076+
*/
4077+
bool nullDerefCheck(FuncDeclaration fd)
4078+
{
4079+
final switch (global.params.useNullCheck)
4080+
{
4081+
case CHECKENABLE.off:
4082+
return false;
4083+
case CHECKENABLE.on:
4084+
return true;
4085+
case CHECKENABLE.safeonly:
4086+
{
4087+
if (fd)
4088+
{
4089+
Type t = fd.type;
4090+
if (t.ty == Tfunction && (cast(TypeFunction)t).trust == TRUST.safe)
4091+
return true;
4092+
}
4093+
return false;
4094+
}
4095+
case CHECKENABLE._default:
4096+
assert(0);
4097+
}
4098+
}

0 commit comments

Comments
 (0)