Skip to content

Commit b959c12

Browse files
committed
Fix potential memory corruption in TPythonVariantType.DispInvoke (freeing WideStrings twice)
1 parent 1ed6e46 commit b959c12

File tree

1 file changed

+100
-69
lines changed

1 file changed

+100
-69
lines changed

PythonForDelphi/Components/Sources/Core/VarPyth.pas

+100-69
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ TNamedParamDesc = record
126126

127127
{$IFDEF DELPHIXE2_OR_HIGHER}
128128
{$DEFINE USESYSTEMDISPINVOKE} //Delphi 2010 DispInvoke is buggy
129+
{$DEFINE PATCHEDSYSTEMDISPINVOKE} //To correct memory leaks
129130
{$ENDIF}
130131
{.$IF DEFINED(FPC_FULLVERSION) and (FPC_FULLVERSION >= 20500)}
131132
{.$DEFINE USESYSTEMDISPINVOKE}
@@ -944,17 +945,40 @@ procedure TPythonVariantType.DispInvoke(Dest: PVarData;
944945
var Source: TVarData; CallDesc: PCallDesc; Params: Pointer);
945946
{$ENDIF}
946947
{$IFDEF USESYSTEMDISPINVOKE}
947-
{$IFDEF DELPHIXE2_OR_HIGHER}
948-
// Modified to correct memory leak QC102387
948+
{$IFDEF PATCHEDSYSTEMDISPINVOKE}
949+
// Modified to correct memory leak QC102387 / RSP-23093
950+
procedure PatchedFinalizeDispatchInvokeArgs(CallDesc: PCallDesc; const Args: TVarDataArray; OrderLTR : Boolean);
951+
const
952+
atByRef = $80;
953+
var
954+
I: Integer;
955+
ArgType: Byte;
956+
PVarParm: PVarData;
957+
VType: TVarType;
958+
begin
959+
for I := 0 to CallDesc^.ArgCount-1 do
960+
begin
961+
ArgType := CallDesc^.ArgTypes[I];
962+
963+
if OrderLTR then
964+
PVarParm := @Args[I]
965+
else
966+
PVarParm := @Args[CallDesc^.ArgCount-I-1];
967+
968+
VType := PVarParm.VType;
969+
970+
// Only ByVal Variant or Array parameters have been copied and need to be released
971+
// Strings have been released via the use of the TStringRefList parameter to GetDispatchInvokeArgs
972+
// !!Modified to prevent memory leaks!! RSP-23093
973+
if ((ArgType and atByRef) <> atByRef) and ((ArgType = varVariant) or ((VType and varArray) = varArray)) then
974+
VarClear(PVariant(PVarParm)^);
975+
end;
976+
end;
977+
949978
procedure PatchedDispInvoke(Dest: PVarData;
950979
const Source: TVarData; CallDesc: PCallDesc; Params: Pointer);
951980
type
952-
PParamRec = ^TParamRec;
953-
TParamRec = array[0..3] of LongInt;
954-
TStringDesc = record
955-
BStr: WideString;
956-
PStr: PAnsiString;
957-
end;
981+
PStringRefList = ^TStringRefList;
958982
const
959983
CDoMethod = $01;
960984
CPropertyGet = $02;
@@ -964,67 +988,75 @@ TStringDesc = record
964988
LIdent: string;
965989
LTemp: TVarData;
966990
VarParams : TVarDataArray;
967-
Strings: TStringRefList;
991+
Strings: array of TStringRef;
992+
PIdent: PByte;
968993
begin
969994
// Grab the identifier
970995
LArgCount := CallDesc^.ArgCount;
971-
LIdent := FixupIdent(string(AnsiString(PAnsiChar(@CallDesc^.ArgTypes[LArgCount]))));
972-
973-
FillChar(Strings, SizeOf(Strings), 0);
974-
VarParams := GetDispatchInvokeArgs(CallDesc, Params, Strings, true);
975-
976-
// What type of invoke is this?
977-
case CallDesc^.CallType of
978-
CDoMethod:
979-
// procedure with N arguments
980-
if Dest = nil then
981-
begin
982-
if not DoProcedure(Source, LIdent, VarParams) then
996+
PIdent := @CallDesc^.ArgTypes[LArgCount];
997+
LIdent := FixupIdent( UTF8ToString(MarshaledAString(PIdent)) );
998+
if LArgCount > 0 then begin
999+
SetLength(Strings, LArgCount);
1000+
FillChar(Strings[0], SizeOf(TStringRef)*LArgCount, 0);
1001+
VarParams := GetDispatchInvokeArgs(CallDesc, Params, PStringRefList(Strings)^, true);
1002+
end;
1003+
try
1004+
// What type of invoke is this?
1005+
case CallDesc^.CallType of
1006+
CDoMethod:
1007+
// procedure with N arguments
1008+
if Dest = nil then
9831009
begin
1010+
if not DoProcedure(Source, LIdent, VarParams) then
1011+
begin
1012+
1013+
// ok maybe its a function but first we must make room for a result
1014+
VarDataInit(LTemp);
1015+
try
1016+
1017+
// notate that the destination shouldn't be bothered with
1018+
// functions can still return stuff, we just do this so they
1019+
// can tell that they don't need to if they don't want to
1020+
SetClearVarToEmptyParam(LTemp);
1021+
1022+
// ok lets try for that function
1023+
if not DoFunction(LTemp, Source, LIdent, VarParams) then
1024+
RaiseDispError;
1025+
finally
1026+
VarDataClear(LTemp);
1027+
end;
1028+
end
1029+
end
9841030

985-
// ok maybe its a function but first we must make room for a result
986-
VarDataInit(LTemp);
987-
try
988-
989-
// notate that the destination shouldn't be bothered with
990-
// functions can still return stuff, we just do this so they
991-
// can tell that they don't need to if they don't want to
992-
SetClearVarToEmptyParam(LTemp);
993-
994-
// ok lets try for that function
995-
if not DoFunction(LTemp, Source, LIdent, VarParams) then
996-
RaiseDispError;
997-
finally
998-
VarDataClear(LTemp);
999-
end;
1031+
// property get or function with 0 argument
1032+
else if LArgCount = 0 then
1033+
begin
1034+
if not GetProperty(Dest^, Source, LIdent) and
1035+
not DoFunction(Dest^, Source, LIdent, VarParams) then
1036+
RaiseDispError;
10001037
end
1001-
end
10021038

1003-
// property get or function with 0 argument
1004-
else if LArgCount = 0 then
1005-
begin
1006-
if not GetProperty(Dest^, Source, LIdent) and
1007-
not DoFunction(Dest^, Source, LIdent, VarParams) then
1039+
// function with N arguments
1040+
else if not DoFunction(Dest^, Source, LIdent, VarParams) then
10081041
RaiseDispError;
1009-
end
10101042

1011-
// function with N arguments
1012-
else if not DoFunction(Dest^, Source, LIdent, VarParams) then
1013-
RaiseDispError;
1043+
CPropertyGet:
1044+
if not ((Dest <> nil) and // there must be a dest
1045+
(LArgCount = 0) and // only no args
1046+
GetProperty(Dest^, Source, LIdent)) then // get op be valid
1047+
RaiseDispError;
10141048

1015-
CPropertyGet:
1016-
if not ((Dest <> nil) and // there must be a dest
1017-
(LArgCount = 0) and // only no args
1018-
GetProperty(Dest^, Source, LIdent)) then // get op be valid
1019-
RaiseDispError;
1049+
CPropertySet:
1050+
if not ((Dest = nil) and // there can't be a dest
1051+
(LArgCount = 1) and // can only be one arg
1052+
SetProperty(Source, LIdent, VarParams[0])) then // set op be valid
1053+
RaiseDispError;
1054+
else
1055+
RaiseDispError;
1056+
end;
10201057

1021-
CPropertySet:
1022-
if not ((Dest = nil) and // there can't be a dest
1023-
(LArgCount = 1) and // can only be one arg
1024-
SetProperty(Source, LIdent, VarParams[0])) then // set op be valid
1025-
RaiseDispError;
1026-
else
1027-
RaiseDispError;
1058+
finally
1059+
PatchedFinalizeDispatchInvokeArgs(CallDesc, VarParams, true);
10281060
end;
10291061

10301062
for I := 0 to Length(Strings) - 1 do
@@ -1033,13 +1065,12 @@ TStringDesc = record
10331065
Break;
10341066
if Strings[I].Ansi <> nil then
10351067
Strings[I].Ansi^ := AnsiString(Strings[I].Wide)
1036-
else if Strings[I].Unicode <> nil then
1037-
Strings[I].Unicode^ := UnicodeString(Strings[I].Wide)
1068+
else
1069+
if Strings[I].Unicode <> nil then
1070+
Strings[I].Unicode^ := UnicodeString(Strings[I].Wide)
10381071
end;
1039-
for I := Low(VarParams) to High(VarParams) do
1040-
VarDataClear(VarParams[I]);
10411072
end;
1042-
{$ENDIF DELPHIXE2_OR_HIGHER}
1073+
{$ENDIF PATCHEDSYSTEMDISPINVOKE}
10431074

10441075
procedure GetNamedParams;
10451076
var
@@ -1066,17 +1097,17 @@ TStringDesc = record
10661097
if (CallDesc^.CallType = CPropertyGet) and (CallDesc^.ArgCount = 1) then begin
10671098
NewCallDesc := CallDesc^;
10681099
NewCallDesc.CallType := CDoMethod;
1069-
{$IFDEF DELPHIXE2_OR_HIGHER}
1100+
{$IFDEF PATCHEDSYSTEMDISPINVOKE}
10701101
PatchedDispInvoke(Dest, Source, @NewCallDesc, Params);
1071-
{$ELSE DELPHIXE2_OR_HIGHER}
1102+
{$ELSE PATCHEDSYSTEMDISPINVOKE}
10721103
inherited DispInvoke(Dest, Source, @NewCallDesc, Params);
1073-
{$ENDIF DELPHIXE2_OR_HIGHER}
1104+
{$ENDIF PATCHEDSYSTEMDISPINVOKE}
10741105
end else
1075-
{$IFDEF DELPHIXE2_OR_HIGHER}
1106+
{$IFDEF PATCHEDSYSTEMDISPINVOKE}
10761107
PatchedDispInvoke(Dest, Source, CallDesc, Params);
1077-
{$ELSE DELPHIXE2_OR_HIGHER}
1108+
{$ELSE PATCHEDSYSTEMDISPINVOKE}
10781109
inherited;
1079-
{$ENDIF DELPHIXE2_OR_HIGHER}
1110+
{$ENDIF PATCHEDSYSTEMDISPINVOKE}
10801111
finally
10811112
if CallDesc^.NamedArgCount > 0 then SetLength(fNamedParams, 0);
10821113
end;

0 commit comments

Comments
 (0)