changeset 92:a6718f9fe942

If a module appears to redefine one of our own types, refuse to load it. Also clear out the class dict for all refused modules now, so that we don't get stale names on the next scan due to not having cleared the module on unload
author Chris Cannam
date Mon, 14 Jan 2019 16:19:44 +0000
parents c4510e5f7a17
children 161ed1fb016b
files PyExtensionManager.h PyPlugScanner.cpp
diffstat 2 files changed, 88 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/PyExtensionManager.h	Mon Jan 14 16:16:43 2019 +0000
+++ b/PyExtensionManager.h	Mon Jan 14 16:19:44 2019 +0000
@@ -1,3 +1,4 @@
+/* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
 /*
 
  * Vampy : This plugin is a wrapper around the Vamp plugin API.
@@ -64,9 +65,9 @@
 	void setPlugModuleNames(vector<string> pyPlugs);
 	void deleteModuleName(string plugKey);
 
+	static const char* m_exposedNames[];
+
 private:
-	static const char* m_exposedNames[];
-	
 	vector<string> m_plugModuleNames;
 	PyObject* m_pyGlobalNamespace;
 	PyObject* m_pyVampyNamespace;
--- a/PyPlugScanner.cpp	Mon Jan 14 16:16:43 2019 +0000
+++ b/PyPlugScanner.cpp	Mon Jan 14 16:19:44 2019 +0000
@@ -1,3 +1,4 @@
+/* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
 /*
 
  * Vampy : This plugin is a wrapper around the Vamp plugin API.
@@ -11,6 +12,8 @@
 
 
 #include "PyPlugScanner.h"
+#include "PyExtensionManager.h"
+#include "Debug.h"
 #include <algorithm>
 #include <cstdlib>
 //#include "vamp-hostsdk/PluginHostAdapter.h"
@@ -95,13 +98,12 @@
 					pluginKey=joinPath(m_path[i],script)+":"+classname;
 					pyClass = getScriptClass(m_path[i],classname);
 					if (pyClass == NULL) 
-					cerr << "Warning: Syntax error in VamPy plugin:  " 
+					cerr << "Warning: Syntax error or other problem in scanning VamPy plugin: " 
 					     << classname << ". Avoiding plugin." << endl;
 					else { 
 							pyPlugs.push_back(pluginKey);
 							m_pyClasses.push_back(pyClass);
-						}
-					//pyPlugs.push_back(pluginKey);
+					}
 				}
 		}		
 	}
@@ -142,7 +144,6 @@
 PyObject* 
 PyPlugScanner::getScriptClass(string path, string classname)
 {
-
 	//Add plugin path to active Python Path 
 	string pyCmd = "import sys\nsys.path.append('" + path + "')\n";
 	PyRun_SimpleString(pyCmd.c_str());
@@ -169,17 +170,91 @@
 	//Get the PluginClass from the module (borrowed reference)
 	PyObject *pyClass = PyDict_GetItemString(pyDict, classname.c_str());
 
-	//Check if class is present and a callable method is implemented
-	if (pyClass && PyCallable_Check(pyClass)) {
-
-	    return pyClass;
-	}	
-	else {
+	if (pyClass == Py_None) {
+		DSTREAM << "Vampy: class name " << classname
+			<< " is None in module; assuming it was scrubbed "
+			<< "following an earlier load failure" << endl;
+		return NULL;
+	}
+	
+	// Check if class is present and a callable method is implemented
+	if (!pyClass || !PyCallable_Check(pyClass)) {
 		cerr << "ERROR: callable plugin class could not be found in source: " << classname << endl 
 			<< "Hint: plugin source filename and plugin class name must be the same." << endl;
 		PyErr_Print(); 
 		return NULL;
 	}
+
+	bool acceptable = true;
+	
+        // Check that the module doesn't have any name collisions with
+        // our own symbols
+
+        int i = 0;
+        while (PyExtensionManager::m_exposedNames[i]) {
+
+		const char* name = PyExtensionManager::m_exposedNames[i];
+		i++;
+
+		PyObject *item = PyDict_GetItemString(pyDict, name);
+		if (!item) continue;
+
+		if (item == Py_None) {
+			DSTREAM << "Vampy: name " << name << " is None "
+				<< "in module " << classname
+				<< "; assuming it was cleared on unload"
+				<< endl;
+			continue;
+		}
+		
+		PyTypeObject *metatype = Py_TYPE(item);
+
+		if (!strcmp(name, "frame2RealTime")) {
+			if (metatype != &PyCFunction_Type) {
+				cerr << "ERROR: plugin " << classname
+				     << " redefines Vampy function name \""
+				     << name << "\" (metatype is \""
+				     << metatype->tp_name << "\")" << endl;
+				acceptable = false;
+				break;
+			} else {
+				continue;
+			}
+		}
+		
+		if (metatype != &PyType_Type) {
+			cerr << "ERROR: plugin " << classname
+			     << " uses Vampy reserved type name \"" << name
+			     << "\" for non-type (metatype is \""
+			     << metatype->tp_name << "\")" << endl;
+			acceptable = false;
+			break;
+		}
+
+		PyTypeObject *type = (PyTypeObject *)item;
+		if (type->tp_name == std::string("vampy.") + name) {
+			DSTREAM << "Vampy: acceptable Vampy type name "
+				<< type->tp_name << " found in module" << endl;
+		} else {
+			cerr << "ERROR: plugin " << classname
+			     << " redefines Vampy type \"" << name << "\"";
+			if (strcmp(type->tp_name, name)) {
+				cerr << " (as \"" << type->tp_name << "\")";
+			}
+			cerr << endl;
+			acceptable = false;
+			break;
+		}
+        }
+
+	if (acceptable) {
+		return pyClass;
+	} else {
+		PyObject *key = PyString_FromString(classname.c_str());
+		PyDict_SetItem(pyDict, key, Py_None);
+		Py_DECREF(key);
+		return NULL;
+	}
 }