BUG: Assignment of Multi-Valued Objects in Request Object Causes Corruption in Scripting Dictionary

ID: Q216825


The information in this article applies to:


SYMPTOMS

If items in the Request.Form or Request.QueryString collections that contain multiple values are assigned to a Scripting Dictionary object stored in the Session object, data corruption may occur. Corrupted data will manifest either as empty fields (that is, blank individual dictionary items) or items that contain garbage text.

A common example of this is a checkbox; checkbox controls can have the same name so that multiple values of a single named item can be returned to the server.


CAUSE

When the assignment of a multi-valued item in the Request.QueryString or Request.Form collections to an item in the Scripting Dictionary object takes place without explicitly using the Item property of the object within the collection, a reference to the actual object is stored in the Scripting Dictionary instead of the intended value. This means that when the item stored in the Scripting Dictionary is referenced again, the Scripting Dictionary will refrence an object in the Request.QueryString or Request.Form collections instead of the intended string or integer values.


RESOLUTION

As described in the following article in the Microsoft Knowledge Base, the best workaround is to be sure that the Item property is used whenever the Request item is assigned to a Session-cached Scripting Dictionary object:

Q216279 BUG: VBScript Can Corrupt Data Stored in Scripting Dictionary Object
The Item property will then return a semi-colon delimited string of text values. However, there is a twist when it comes to using these strings on the client. The most efficient technique uses the VBScript Split() function to move the strings into array elements. See the code in the More Information section below for details.


STATUS

Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article.


MORE INFORMATION

To reproduce this problem add the following to your Global.asa file


<OBJECT 
	RUNAT=Server 
	SCOPE=Session 
	PROGID="Scripting.Dictionary" 
	ID=objDict>
</OBJECT>
<SCRIPT LANGUAGE=VBScript RUNAT=Server>

Sub Session_OnStart
	Set Session("objDict") = objDict
	Session("objDict").Add "name", "Enter your name"     
	Session("objDict").Add "phone",""
End Sub

</SCRIPT> 

Then add the following to a file named Index.asp:

<html>

<head>

<title>Dictionary Corruption Repro</title>

</head>

<body>
<h1>Dictionary Corruption Repro</h1>

<%
If Request.Form("workAround")="on" Then
	fWorkAround="CHECKED"
Else
	fWorkAround=""
End If

If Not isEmpty(Request.Form("update")) Then
	If fWorkAround="CHECKED" Then
		Session("objDict").Item("name")=Request.Form("txtName").Item
		Session("objDict").Item("phone")=Request.Form("phone").Item
	Else
		Session("objDict").Item("name")=Request.Form("txtName")
		Session("objDict").Item("phone")=Request.Form("phone")
	End If
End If
 
Response.Write("Cached name: " & Session("objDict").Item("name") & "<BR>")
if Session("objDict").Item("phone")="" Then
	varPhone=Array("","")
Else
	varPhone=split(Session("objDict").Item("phone"),", ")
	Response.Write("Cached Phone: " & varPhone(0) & "<BR>")
	Response.Write("Cached Phone: " & varPhone(1) & "<BR>")
end if
%>

<BR>
SessionID: <%=Session.SessionID%>
<P>

<form id="testForm" method="post" action="<%=Request.ServerVariables("SCRIPT_NAME")%>">
  Name <input name="txtName" maxlength=30 size=30 value="<%=Session("objDict").Item("name")%>" ><BR>
  Phone 1 <input name="phone" value="<%=varPhone(0)%>"><BR>
  Phone 2 <input name="phone" value="<%=varPhone(1)%>"><BR>
  Use Array workaround <input type="checkbox" name="workAround" <%=fWorkAround%>><BR>
  <input type="submit" value="Update" name="update" title="Call this page with updated information.">
  <input type="submit" value="Refresh" name="refresh" title="Call this page with existing cached information.">
</form>

<hr width="600" align="left">

<div style="width:600">
	<h2>Instructions for repro</h2> 
	By default the workaround checkbox is blank. This means that this repro will use 
	the Request object to populate the Dictionary object.
	<p>
	
	This will result in corrupted data in the Dictionary object. The most reliable way 
	to demonstrate this unintended result requires the following steps:
	<ol>
	<li> Open this file in Visual InterDev, then View in Browser.
	<li> Fill in this form and press the Update button. 
	<li> Then return to VID and modify this index.asp file by adding a space between 
	the lines of script. 
	<li> View in Browser again (don't save and manually refresh the page in Internet Explorer; you 
	need to make the data in the Request object stale -- if you hit refresh, you're 
	reusing the original Request object).
	</ol>
	
	You will see no (or corrupted) data in the form (meaning the Dictionary has been corrupted).
	<p>
	Now fill out the form again; only this time check the Workaround box. Repeat the same 
	steps as above. This time, the data in the Dictionary is intact.
	<p>
	<h2>Explanation</h2>
	This workaround does two things. First, it uses the Item property of the Request 
	object when assigning scalars/strings from the Form to the Dictionary. This corrects a 
	problem in VBScript that will Set an object reference in the Dictionary item even 
	though no Set command was used in this reproduction. This object reference is stored in 
	the Dictionary because VBScript is not making the required IDispatch call to fetch 
	the default (string) property of the Request object, the Item property. This 
	workaround also has the added bonus of making the code a tiny bit faster because 
	one less IDispatch call is made.
	<p>
	The second workaround uses a twist on the first workaround. As with scalar values, 
	our second workaround assigns the Item property to Dictionary object for Request 
	objects that use the <b>IStringList</b> interface to return multivalued named items 
	such (as the multiple phone numbers in our repro). Using the Item property results 
	in a string value in the Phone Dictionary item. To fetch these multivalued named 
	items from the Dictionary use the VBScript split() function to expand the string 
	into an array and then use that array in the User Interface script as necessary.
	<p>
	Again, the key is to <b>store string values only</b> in the Dictionary object.
	<p>
	Note: these bugs apply only to VBScript.
	<p>
	<h2>Postscript</h2>
	This bug in VBScript has prompted a fix in ASP.dll as well. It is bad form to store 
	ASP intrinsics in Session or Application scope. ASP has a function called 
	CheckForTombstone() that checks for stale data structures in Session or Application 
	objects and returns an error if found. An example of stale data structure is a 
	Request object from a prior form post. If you need to cache this old data in Session 
	scope, store the data, not the stucture that brought the data up to the server.
	<p>
	CheckForTombstone() has now been added to the IStringList interface to ensure that 
	intrinsic objects are not stored there. This fix will also preclude the problem 
	with VBScript described above.
</div>
<p>

<hr width="600" align="left">

<p>&nbsp;</p>

</body>
</html> 


REFERENCES

Q156223 BUG: VBScript can corrupt data stored in Scripting Dictionary Object

Additional query words: ASP Session Request Dictionary Scripting VBScript


Keywords          : 
Version           : WINDOWS:3.0,4.0,95,98; winnt:4.0
Platform          : WINDOWS winnt 
Issue type        : kbbug 

Last Reviewed: March 1, 1999