Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit fc309f3

Browse files
committed
[[ Bug 22010 ]] Fix memory leak when binding LCB foreign handlers
This patch fixes a memory leak which can occur when binding to LCB foreign handlers. Previously, if a foreign handler was binding to a C or Objective-C function and was present in an external code library then a leak of the native code module handle wrapper could occur. This patch fixes the issue by holding any such handles in a per-module array keyed by the name of the external native code library being loaded.
1 parent b5e0e6b commit fc309f3

File tree

5 files changed

+101
-33
lines changed

5 files changed

+101
-33
lines changed

docs/notes/bugfix-22010.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Fix memory leak when binding some LCB foreign handlers

libscript/include/libscript/script.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,9 @@ void MCScriptSetModuleLicensed(MCScriptModuleRef self, bool p_licensed);
255255
// Gets the licensed state of a module
256256
bool MCScriptIsModuleLicensed(MCScriptModuleRef self);
257257

258+
// Attempt to load the named library using any context provided by the module
259+
bool MCScriptLoadModuleLibrary(MCScriptModuleRef self, MCStringRef p_library, MCSLibraryRef& r_library);
260+
258261
////////////////////////////////////////////////////////////////////////////////
259262

260263
// Create an instance of the given module. If the module is single-instance it

libscript/src/script-instance.cpp

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -805,28 +805,20 @@ __MCScriptResolveForeignFunctionBindingForC(MCScriptInstanceRef p_instance,
805805
{
806806
return false;
807807
}
808-
809-
/* TODO: This leaks a module handle if library is not empty (builtin) */
808+
810809
MCSLibraryRef t_module;
811-
if (MCStringIsEmpty(*t_library))
810+
if (!MCScriptLoadModuleLibrary(MCScriptGetModuleOfInstance(p_instance),
811+
*t_library,
812+
t_module))
812813
{
813-
t_module = MCScriptGetLibrary();
814-
}
815-
else
816-
{
817-
if (!MCScriptLoadLibrary(MCScriptGetModuleOfInstance(p_instance),
818-
*t_library,
819-
t_module))
814+
if (r_bound == nil)
820815
{
821-
if (r_bound == nil)
822-
{
823-
return MCScriptThrowUnableToLoadForiegnLibraryError();
824-
}
816+
return MCScriptThrowUnableToLoadForiegnLibraryError();
817+
}
825818

826-
*r_bound = false;
819+
*r_bound = false;
827820

828-
return true;
829-
}
821+
return true;
830822
}
831823

832824
/* Resolve the symbol from the module which we've loaded */
@@ -948,27 +940,19 @@ __MCScriptResolveForeignFunctionBindingForObjC(MCScriptInstanceRef p_instance,
948940
return false;
949941
}
950942

951-
/* TODO: This leaks a module handle if library is not empty (builtin) */
952943
MCSLibraryRef t_module;
953-
if (MCStringIsEmpty(*t_library))
954-
{
955-
t_module = MCScriptGetLibrary();
956-
}
957-
else
944+
if (!MCScriptLoadModuleLibrary(MCScriptGetModuleOfInstance(p_instance),
945+
*t_library,
946+
t_module))
958947
{
959-
if (!MCScriptLoadLibrary(MCScriptGetModuleOfInstance(p_instance),
960-
*t_library,
961-
t_module))
948+
if (r_bound == nil)
962949
{
963-
if (r_bound == nil)
964-
{
965-
return MCScriptThrowUnableToLoadForiegnLibraryError();
966-
}
950+
return MCScriptThrowUnableToLoadForiegnLibraryError();
951+
}
967952

968-
*r_bound = false;
953+
*r_bound = false;
969954

970-
return true;
971-
}
955+
return true;
972956
}
973957

974958
MCScriptThreadAffinity t_thread_affinity;

libscript/src/script-module.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,13 @@ void MCScriptDestroyModule(MCScriptModuleRef self)
284284
break;
285285
}
286286

287+
// Free any loaded libraries
288+
if (self->libraries != nullptr)
289+
{
290+
MCValueRelease(self->libraries);
291+
}
292+
293+
// Free the compiled module representation
287294
MCPickleRelease(kMCScriptModulePickleInfo, self);
288295
}
289296

@@ -909,6 +916,74 @@ bool MCScriptEnsureModuleIsUsable(MCScriptModuleRef self)
909916
return false;
910917
}
911918

919+
bool
920+
MCScriptLoadModuleLibrary(MCScriptModuleRef self,
921+
MCStringRef p_library_name_string,
922+
MCSLibraryRef& r_library)
923+
{
924+
/* If the string is empty, then use the library containing libscript (i.e.
925+
* 'builtin'). */
926+
if (MCStringIsEmpty(p_library_name_string))
927+
{
928+
r_library = MCScriptGetLibrary();
929+
return true;
930+
}
931+
932+
/* Create a nameref for the library so we can lookup up in the module's
933+
* library list. */
934+
MCNewAutoNameRef t_library_name;
935+
if (!MCNameCreate(p_library_name_string,
936+
&t_library_name))
937+
{
938+
return false;
939+
}
940+
941+
/* If there is a libraries array, then check whether the library has been
942+
* loaded before, and if so use that library. Otherwise create the libraries
943+
* array. */
944+
if (self->libraries != nullptr)
945+
{
946+
if (MCArrayFetchValue(self->libraries,
947+
true,
948+
*t_library_name,
949+
(MCValueRef&)r_library))
950+
{
951+
return true;
952+
}
953+
}
954+
else
955+
{
956+
if (!MCArrayCreateMutable(self->libraries))
957+
{
958+
return false;
959+
}
960+
}
961+
962+
/* Attempt to load the library */
963+
MCSAutoLibraryRef t_library;
964+
if (!MCScriptLoadLibrary(self,
965+
p_library_name_string,
966+
&t_library))
967+
{
968+
return false;
969+
}
970+
971+
/* Store the library in the libraries array for the module. */
972+
if (!MCArrayStoreValue(self->libraries,
973+
true,
974+
*t_library_name,
975+
*t_library))
976+
{
977+
return false;
978+
}
979+
980+
/* Return the library (unretained - as the module holds a reference through
981+
* its array). */
982+
r_library = *t_library;
983+
984+
return true;
985+
}
986+
912987
////////////////////////////////////////////////////////////////////////////////
913988

914989
static bool

libscript/src/script-private.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,11 @@ struct MCScriptModule: public MCScriptObject
512512

513513
// This is the ordinal mapping array (if any) -- not pickled
514514
void **builtins;
515+
516+
// This is a map from library name used in the module to MCSLibraryRef
517+
// all foreign handler definitions which have the same library string share
518+
// a single MCSLibraryRef -- not pickled
519+
MCArrayRef libraries;
515520
};
516521

517522
bool MCScriptWriteRawModule(MCStreamRef stream, MCScriptModule *module);

0 commit comments

Comments
 (0)