seccion - ColdFusion VARIABLES Condición de carrera?
race condition vulnerability (1)
El problema fue el uso del ámbito VARIABLES dentro de la función DisplayContent del componente PageManager (que se instanciaba como parte de la Aplicación, por lo tanto compartido). El alcance VARIABLES se compartiría en este caso, lo que daría lugar a condiciones de carrera.
Aparte de duplicar esa función en otro componente y crear una instancia en el ámbito VARIABLES de index.cfm, también podría pasar del alcance VARIABLES en la función DisplayContent al alcance LOCAL (suponiendo que no necesita acceder a estas variables fuera del función). Ambas formas evitaron que la condición de carrera volviera a aparecer cuando se realizaba una prueba de esfuerzo con jmeter.
Me gustaría obtener ayuda para identificar por qué este código en particular, en circunstancias excepcionales, produce una condición de carrera. Encontré una solución, que también describiré, pero realmente quiero entenderla.
Tenemos un sistema basado en CMS compuesto por muchos módulos basados libremente en un modelo de caja de fusibles. Todo se ejecuta en un solo index.cfm.
En nuestro Index.cfm, estamos creando un par de instancias de Componentes, algunos están bsaed en la instancia APPLICATION.PortalApp creada en Application.cfc. No incluyo ese código porque no es del todo relevante:
<cfset REQUEST.ActionHandler = CreateObject("Component", "Components.ActionHandler").init(APPLICATION.PortalApp.Config) />
<cfset VARIABLES.Modules = CreateObject("Component", "Components.Modules").init(APPLICATION.PortalApp.Config, REQUEST.ActionHandler.GetModuleList(), REQUEST.ActionHandler.GetSuppressOutput(), REQUEST.ActionHandler.GetRoleList(), REQUEST.ActionHandler.GetAccessList(), REQUEST.ActionHandler.GetMasterRoleList()) />
Después de instanciar estos objetos, obtenemos el contenido de los módulos en la página (en función de su ''panel'': superior, izquierdo, medio, derecho) llamando a un componente de PageManager que se instancia como parte de la aplicación, Application.PortalApp.
<cfsavecontent variable="Variables.Portal_Content.Top"><cfset APPLICATION.PortalApp.PageManager.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 0 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Left"><cfset APPLICATION.PortalApp.PageManager.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 1 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Middle"><cfset APPLICATION.PortalApp.PageManager.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 2 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Right"><cfset APPLICATION.PortalApp.PageManager.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 3 ) /></cfsavecontent>
PageManager.DisplayContent básicamente hace un bucle sobre los módulos y los envuelve en un contenedor. Sin embargo, en algunos puntos, hay una condición de carrera y la función crea cráteres y no muestra ningún módulo. Parece estar basado en VARIABLES. Los módulos se vuelven corruptos, pero eso está en el ámbito VARIABLES que no se comparte.
Para solucionarlo, cambiamos el código a lo siguiente:
<!--- If we do not use VARIABLES scope and create a ContentManager, race condition can cause empty modules --->
<cfset VARIABLES.CurrPageMgr = CreateObject("Component", "Components.ContentManager").init() />
<cfsavecontent variable="Variables.Portal_Content.Top"><cfset VARIABLES.CurrPageMgr.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 0 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Left"><cfset VARIABLES.CurrPageMgr.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 1 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Middle"><cfset VARIABLES.CurrPageMgr.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 2 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Right"><cfset VARIABLES.CurrPageMgr.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 3 ) /></cfsavecontent>
DisplayContent de ContentManager es exactamente el mismo texto de función que el PageManager.DisplayContent con la excepción en ContentManager existente solo en el ámbito de VARIABLES y el PageManager existente como parte de la APLICACIÓN.
Esto fue muy difícil de reproducir después de recibir informes al respecto. Básicamente, comencé una sesión de jmeter martillando el servidor de desarrollo lo más duro posible con un conjunto de puntos de interrupción basado en una condición que sabía que dispararía con VARIABLES. El módulo se corrompió. Esa fue la única forma de reproducirlo.
Tampoco estoy 100% seguro de que esta solución funcione, pero hasta ahora jmeter no ha disparado la condición con ella en su lugar.
Editar: por solicitud, función DisplayContent:
<cffunction name="displayContent" access="public" output="true">
<cfargument name="SessionData" required="yes" type="Struct" />
<cfargument name="ActionHandler" required="yes" type="ActionHandler" />
<cfargument name="Modules" required="yes" type="Modules" />
<cfargument name="Pane" required="yes" type="numeric" />
<cfswitch expression="#Arguments.Pane#">
<cfcase value="1"><cfset Variables.blnPane = ARGUMENTS.Modules.getLeft()></cfcase>
<cfcase value="2"><cfset Variables.blnPane = ARGUMENTS.Modules.getCenter()></cfcase>
<cfcase value="3"><cfset Variables.blnPane = ARGUMENTS.Modules.getRight()></cfcase>
<cfdefaultcase><cfset Variables.blnPane = ARGUMENTS.Modules.getTop()></cfdefaultcase>
</cfswitch>
<cfif VARIABLES.blnPane>
<cfset VARIABLES.qryPaneModules = ARGUMENTS.Modules.GetModulesInPane(Arguments.Pane)>
<cfset VARIABLES.aryModulesInPane = ArrayNew(1)>
<cfloop query="VARIABLES.qryPaneModules">
<cfset VARIABLES.blnResult = ArrayAppend(aryModulesInPane,VARIABLES.qryPaneModules.MOD_SYS_NR)>
</cfloop>
<cfset VARIABLES.Template = "../CustomTags/Portalv#ARGUMENTS.SessionData.intPortalVersion#/DisplayModuleAlternate.cfm">
<cfif Arguments.ActionHandler.GetDocumentType() EQ 3>
<cfset VARIABLES.Template = "../CustomTags/Portalv#ARGUMENTS.SessionData.intPortalVersion#/DisplayXMLModule.cfm">
</cfif>
<cfif VARIABLES.qryPaneModules.recordcount GT 0>
<cfloop index="VARIABLES.modLoop" from="1" to="#ArrayLen(VARIABLES.aryModulesInPane)#">
<cfparam name="VARIABLES.aryModulesInPane[VARIABLES.modLoop]" default="0">
<cfset VARIABLES.objModuleInfo = ARGUMENTS.Modules.GetModuleInfo( VARIABLES.aryModulesInPane[VARIABLES.modLoop], ARGUMENTS.Modules.GetModules() ) />
<cfif NOT IsNumeric(VARIABLES.objModuleInfo.intModuleID)>
<cfset VARIABLES.objModuleInfo.intModuleID = 0 >
</cfif>
<cfmodule template="#VARIABLES.Template#"
ModuleID="#VARIABLES.objModuleInfo.intModuleID#"
ModuleName="#VARIABLES.objModuleInfo.strModuleName#"
SecurityLevel="#VARIABLES.objModuleInfo.intRoleTypeID#"
ModuleDSN="#VARIABLES.objModuleInfo.strModDBDSN#"
ModuleUserName="#VARIABLES.objModuleInfo.strModDBUserID#"
ModulePassword="#VARIABLES.objModuleInfo.strModDBPassword#"
AlternateFunctionID="#VARIABLES.objModuleInfo.intAlternateFunctionID#"
AlternateFunctionName="#VARIABLES.objModuleInfo.strAlternateFunctionName#"
InstructionFileID="#VARIABLES.objModuleInfo.intManualID#"
ModuleOps="#VARIABLES.objModuleInfo.blnModuleOps#"
ModuleSource="#VARIABLES.objModuleInfo.strModuleSource#"
ItemID="#VARIABLES.objModuleInfo.intModItemID#"
AutoLoginID="#VARIABLES.objModuleInfo.intAutoLoginCategoryID#"
IPS_objPortalSessionData="#ARGUMENTS.SessionData#"
ModuleList="#ARGUMENTS.ActionHandler.GetModuleList_SingleRecord(VARIABLES.objModuleInfo.intModuleID)#"
ModulesComponent="#ARGUMENTS..Modules#"
ControlHeaderIR = "#VARIABLES.objModuleInfo.blnControlHeaderIR#"
BorderIR = "#VARIABLES.objModuleInfo.blnBorderIR#"
IPS_strPortalRoot = "#VARIABLES.IPS_strPortalRoot#"
IPS_strPortalURL = "#VARIABLES.IPS_strPortalURL#"
Wrapper = "#Arguments.ActionHandler.getWrapper()#"
Definition = "#VARIABLES.objModuleInfo.strModuleDef#"
Category = "#VARIABLES.objModuleInfo.strModuleCat#"
aryModulesInPane = "#VARIABLES.aryModulesInPane#"
blnLockIR = "#VARIABLES.objModuleInfo.blnLockIR#"
strMessageTE = "#VARIABLES.objModuleInfo.strMessageTE#" >
</cfloop>
</cfif>
</cfif>
</cffunction>