Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Fix Issue 22218 - Dynamic casts across binary boundaries can easily fail #3543

Merged
merged 8 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions src/rt/cast_.d
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ extern (C):
nothrow:
pure:

// Needed because ClassInfo.opEquals(Object) does a dynamic cast,
// but we are trying to implement dynamic cast.
extern (D) private bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) @safe
{
if (a is b)
return true;
// take care of potential duplicates across binaries
return a.name == b.name;
}

/******************************************
* Given a pointer:
* If it is an Object, return that Object.
Expand Down Expand Up @@ -80,19 +90,19 @@ void* _d_dynamic_cast(Object o, ClassInfo c)

int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t offset) @safe
{
if (oc is c)
if (areClassInfosEqual(oc, c))
return true;

do
{
if (oc.base is c)
if (oc.base && areClassInfosEqual(oc.base, c))
return true;

// Bugzilla 2013: Use depth-first search to calculate offset
// from the derived (oc) to the base (c).
foreach (iface; oc.interfaces)
{
if (iface.classinfo is c || _d_isbaseof2(iface.classinfo, c, offset))
if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2(iface.classinfo, c, offset))
{
offset += iface.offset;
return true;
Expand All @@ -107,17 +117,17 @@ int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t o

int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @safe
{
if (oc is c)
if (areClassInfosEqual(oc, c))
return true;

do
{
if (oc.base is c)
if (oc.base && areClassInfosEqual(oc.base, c))
return true;

foreach (iface; oc.interfaces)
{
if (iface.classinfo is c || _d_isbaseof(iface.classinfo, c))
if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof(iface.classinfo, c))
return true;
}

Expand Down
11 changes: 10 additions & 1 deletion test/shared/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ LINK_SHARED:=1

include ../common.mak

TESTS:=link load linkD linkDR loadDR host finalize
TESTS:=link load linkD linkDR loadDR host finalize dynamiccast
TESTS+=link_linkdep load_linkdep link_loaddep load_loaddep load_13414

EXPORT_DYNAMIC=$(if $(findstring $(OS),linux freebsd dragonflybsd),-L--export-dynamic,)
Expand All @@ -13,9 +13,12 @@ all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS)))

$(ROOT)/loadDR.done $(ROOT)/host.done: RUN_ARGS:=$(DRUNTIMESO)

$(ROOT)/dynamiccast.done: CLEANUP:=rm dynamiccast_endmain dynamiccast_endbar

$(ROOT)/%.done: $(ROOT)/%
@echo Testing $*
$(QUIET)$(TIMELIMIT)$< $(RUN_ARGS)
$(CLEANUP)
@touch $@

$(ROOT)/link: $(SRC)/link.d $(ROOT)/lib.so $(DRUNTIMESO)
Expand All @@ -39,6 +42,12 @@ $(ROOT)/load $(ROOT)/finalize: $(ROOT)/%: $(SRC)/%.d $(ROOT)/lib.so $(DRUNTIMESO
$(ROOT)/load_13414: $(ROOT)/%: $(SRC)/%.d $(ROOT)/lib_13414.so $(DRUNTIMESO)
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< $(LINKDL)

$(ROOT)/dynamiccast: $(SRC)/dynamiccast.d $(ROOT)/dynamiccast.so $(DRUNTIMESO)
$(QUIET)$(DMD) $(DFLAGS) -of$@ $(SRC)/dynamiccast.d $(LINKDL)

$(ROOT)/dynamiccast.so: $(SRC)/dynamiccast.d $(DRUNTIMESO)
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< -version=DLL -fPIC -shared $(LINKDL)

$(ROOT)/linkD: $(SRC)/linkD.c $(ROOT)/lib.so $(DRUNTIMESO)
$(QUIET)$(CC) $(CFLAGS) -o $@ $< $(ROOT)/lib.so $(LDL) -pthread

Expand Down
98 changes: 98 additions & 0 deletions test/shared/src/dynamiccast.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
class C : Exception
{
this() { super(""); }
}

version (DLL)
{
version (Windows)
{
import core.sys.windows.dll;
mixin SimpleDllMain;
}

pragma(mangle, "foo")
export Object foo(Object o)
{
assert(cast(C) o);
return new C;
}

pragma(mangle, "bar")
export void bar(void function() f)
{
import core.stdc.stdio : fopen, fclose;
bool caught;
try
f();
catch (C e)
caught = true;
assert(caught);

// verify we've actually got to the end, because for some reason we can
// end up exiting with code 0 when throwing an exception
fclose(fopen("dynamiccast_endbar", "w"));
throw new C;
}
}
else
{
T getFunc(T)(const(char)* sym, string thisExePath)
{
import core.runtime : Runtime;

version (Windows)
{
import core.sys.windows.winbase : GetProcAddress;
return cast(T) Runtime.loadLibrary("dynamiccast.dll")
.GetProcAddress(sym);
}
else version (Posix)
{
import core.sys.posix.dlfcn : dlsym;
import core.stdc.string : strrchr;

auto name = thisExePath ~ '\0';
const pathlen = strrchr(name.ptr, '/') - name.ptr + 1;
name = name[0 .. pathlen] ~ "dynamiccast.so";
return cast(T) Runtime.loadLibrary(name)
.dlsym(sym);
}
else static assert(0);
}

version (DigitalMars) version (Win64) version = DMD_Win64;

void main(string[] args)
{
import core.stdc.stdio : fopen, fclose, remove;

remove("dynamiccast_endmain");
remove("dynamiccast_endbar");

C c = new C;

auto o = getFunc!(Object function(Object))("foo", args[0])(c);
assert(cast(C) o);

version (DMD_Win64)
{
// FIXME: apparent crash & needs more work, see https://github.com/dlang/druntime/pull/2874
fclose(fopen("dynamiccast_endbar", "w"));
}
else
{
bool caught;
try
getFunc!(void function(void function()))("bar", args[0])(
{ throw new C; });
catch (C e)
caught = true;
assert(caught);
}

// verify we've actually got to the end, because for some reason we can
// end up exiting with code 0 when throwing an exception
fclose(fopen("dynamiccast_endmain", "w"));
}
}
10 changes: 9 additions & 1 deletion test/shared/win64.mak
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ DMD=dmd
MODEL=64
DRUNTIMELIB=druntime64.lib

test: loadlibwin dllrefcount dllgc
test: loadlibwin dllrefcount dllgc dynamiccast

dllrefcount:
$(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) test\shared\src\dllrefcount.d
Expand All @@ -21,3 +21,11 @@ dllgc:
$(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) -ofloaddllgc.exe test\shared\src\dllgc.d
loaddllgc.exe
del loaddllgc.exe loaddllgc.obj dllgc.dll dllgc.obj

dynamiccast:
$(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) -version=DLL -shared -ofdynamiccast.dll test\shared\src\dynamiccast.d
$(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) -ofdynamiccast.exe test\shared\src\dynamiccast.d
dynamiccast.exe
cmd /c "if not exist dynamiccast_endbar exit 1"
cmd /c "if not exist dynamiccast_endmain exit 1"
del dynamiccast.exe dynamiccast.dll dynamiccast.obj dynamiccast_endbar dynamiccast_endmain