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

Commit a8df891

Browse files
authored
Merge pull request #3543 from kinke/dynamic_cast
Fix Issue 22218 - Dynamic casts across binary boundaries can easily fail
2 parents 5a77d7f + 68efbb3 commit a8df891

File tree

4 files changed

+133
-8
lines changed

4 files changed

+133
-8
lines changed

src/rt/cast_.d

+16-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ extern (C):
1919
nothrow:
2020
pure:
2121

22+
// Needed because ClassInfo.opEquals(Object) does a dynamic cast,
23+
// but we are trying to implement dynamic cast.
24+
extern (D) private bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) @safe
25+
{
26+
if (a is b)
27+
return true;
28+
// take care of potential duplicates across binaries
29+
return a.name == b.name;
30+
}
31+
2232
/******************************************
2333
* Given a pointer:
2434
* If it is an Object, return that Object.
@@ -80,19 +90,19 @@ void* _d_dynamic_cast(Object o, ClassInfo c)
8090

8191
int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t offset) @safe
8292
{
83-
if (oc is c)
93+
if (areClassInfosEqual(oc, c))
8494
return true;
8595

8696
do
8797
{
88-
if (oc.base is c)
98+
if (oc.base && areClassInfosEqual(oc.base, c))
8999
return true;
90100

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

108118
int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @safe
109119
{
110-
if (oc is c)
120+
if (areClassInfosEqual(oc, c))
111121
return true;
112122

113123
do
114124
{
115-
if (oc.base is c)
125+
if (oc.base && areClassInfosEqual(oc.base, c))
116126
return true;
117127

118128
foreach (iface; oc.interfaces)
119129
{
120-
if (iface.classinfo is c || _d_isbaseof(iface.classinfo, c))
130+
if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof(iface.classinfo, c))
121131
return true;
122132
}
123133

test/shared/Makefile

+10-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ LINK_SHARED:=1
22

33
include ../common.mak
44

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

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

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

16+
$(ROOT)/dynamiccast.done: CLEANUP:=rm dynamiccast_endmain dynamiccast_endbar
17+
1618
$(ROOT)/%.done: $(ROOT)/%
1719
@echo Testing $*
1820
$(QUIET)$(TIMELIMIT)$< $(RUN_ARGS)
21+
$(CLEANUP)
1922
@touch $@
2023

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

45+
$(ROOT)/dynamiccast: $(SRC)/dynamiccast.d $(ROOT)/dynamiccast.so $(DRUNTIMESO)
46+
$(QUIET)$(DMD) $(DFLAGS) -of$@ $(SRC)/dynamiccast.d $(LINKDL)
47+
48+
$(ROOT)/dynamiccast.so: $(SRC)/dynamiccast.d $(DRUNTIMESO)
49+
$(QUIET)$(DMD) $(DFLAGS) -of$@ $< -version=DLL -fPIC -shared $(LINKDL)
50+
4251
$(ROOT)/linkD: $(SRC)/linkD.c $(ROOT)/lib.so $(DRUNTIMESO)
4352
$(QUIET)$(CC) $(CFLAGS) -o $@ $< $(ROOT)/lib.so $(LDL) -pthread
4453

test/shared/src/dynamiccast.d

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
class C : Exception
2+
{
3+
this() { super(""); }
4+
}
5+
6+
version (DLL)
7+
{
8+
version (Windows)
9+
{
10+
import core.sys.windows.dll;
11+
mixin SimpleDllMain;
12+
}
13+
14+
pragma(mangle, "foo")
15+
export Object foo(Object o)
16+
{
17+
assert(cast(C) o);
18+
return new C;
19+
}
20+
21+
pragma(mangle, "bar")
22+
export void bar(void function() f)
23+
{
24+
import core.stdc.stdio : fopen, fclose;
25+
bool caught;
26+
try
27+
f();
28+
catch (C e)
29+
caught = true;
30+
assert(caught);
31+
32+
// verify we've actually got to the end, because for some reason we can
33+
// end up exiting with code 0 when throwing an exception
34+
fclose(fopen("dynamiccast_endbar", "w"));
35+
throw new C;
36+
}
37+
}
38+
else
39+
{
40+
T getFunc(T)(const(char)* sym, string thisExePath)
41+
{
42+
import core.runtime : Runtime;
43+
44+
version (Windows)
45+
{
46+
import core.sys.windows.winbase : GetProcAddress;
47+
return cast(T) Runtime.loadLibrary("dynamiccast.dll")
48+
.GetProcAddress(sym);
49+
}
50+
else version (Posix)
51+
{
52+
import core.sys.posix.dlfcn : dlsym;
53+
import core.stdc.string : strrchr;
54+
55+
auto name = thisExePath ~ '\0';
56+
const pathlen = strrchr(name.ptr, '/') - name.ptr + 1;
57+
name = name[0 .. pathlen] ~ "dynamiccast.so";
58+
return cast(T) Runtime.loadLibrary(name)
59+
.dlsym(sym);
60+
}
61+
else static assert(0);
62+
}
63+
64+
version (DigitalMars) version (Win64) version = DMD_Win64;
65+
66+
void main(string[] args)
67+
{
68+
import core.stdc.stdio : fopen, fclose, remove;
69+
70+
remove("dynamiccast_endmain");
71+
remove("dynamiccast_endbar");
72+
73+
C c = new C;
74+
75+
auto o = getFunc!(Object function(Object))("foo", args[0])(c);
76+
assert(cast(C) o);
77+
78+
version (DMD_Win64)
79+
{
80+
// FIXME: apparent crash & needs more work, see https://github.com/dlang/druntime/pull/2874
81+
fclose(fopen("dynamiccast_endbar", "w"));
82+
}
83+
else
84+
{
85+
bool caught;
86+
try
87+
getFunc!(void function(void function()))("bar", args[0])(
88+
{ throw new C; });
89+
catch (C e)
90+
caught = true;
91+
assert(caught);
92+
}
93+
94+
// verify we've actually got to the end, because for some reason we can
95+
// end up exiting with code 0 when throwing an exception
96+
fclose(fopen("dynamiccast_endmain", "w"));
97+
}
98+
}

test/shared/win64.mak

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ DMD=dmd
44
MODEL=64
55
DRUNTIMELIB=druntime64.lib
66

7-
test: loadlibwin dllrefcount dllgc
7+
test: loadlibwin dllrefcount dllgc dynamiccast
88

99
dllrefcount:
1010
$(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) test\shared\src\dllrefcount.d
@@ -21,3 +21,11 @@ dllgc:
2121
$(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) -ofloaddllgc.exe test\shared\src\dllgc.d
2222
loaddllgc.exe
2323
del loaddllgc.exe loaddllgc.obj dllgc.dll dllgc.obj
24+
25+
dynamiccast:
26+
$(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) -version=DLL -shared -ofdynamiccast.dll test\shared\src\dynamiccast.d
27+
$(DMD) -g -m$(MODEL) -conf= -Isrc -defaultlib=$(DRUNTIMELIB) -ofdynamiccast.exe test\shared\src\dynamiccast.d
28+
dynamiccast.exe
29+
cmd /c "if not exist dynamiccast_endbar exit 1"
30+
cmd /c "if not exist dynamiccast_endmain exit 1"
31+
del dynamiccast.exe dynamiccast.dll dynamiccast.obj dynamiccast_endbar dynamiccast_endmain

0 commit comments

Comments
 (0)