BUG: FIX: VBScript Can Corrupt Data Stored in Scripting Dictionary ObjectID: Q216279
|
If you assign Request.Form or QueryString items in a Scripting Dictionary object cached in Session scope, you will corrupt your data. Corrupted data will manifest either as empty fields (that is, blank individual dictionary items) or as items containing garbage text.
VBScript does not use IDispatch to fetch the Response object's default property. This results in an object reference being assigned to an Item in the Dictionary (even when the assignment in your script does not use the Set statement). When this object reference is then cached with Session scope, a related bug in Active Server Pages (ASP) doesn't catch and block the object reference (see Q216825 "BUG: Assignment of Multi-valued Objects in Request Object Causes Corruption in Scripting Dictionary" for details).
The solution is to ensure that only string values are stored in Dictionaries. The most reliable way to ensure only strings are used is to explicitly reference the Item property of all elements in a Request object. Special consideration must be given to multivalued named items as described above (see the code in the MORE INFORMATION section below for an example, and reference Q216825 "BUG: Assignment of Multi-valued Objects in Request Object Causes Corruption in Scripting Dictionary".
Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article. This bug was corrected in Microsoft Internet Explorer 5.
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>
<html>
<head>
<title>Dictionary Corruption Reproduction</title>
</head>
<body>
<h1>Dictionary Corruption Reproduction</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 reproduction</h2>
By default the workaround check box is blank. This means that this reproduction 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 Visual InterDev 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 click 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 repro. 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
Active Server Pages (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 structure 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> </p>
</body>
</html>
For additional information, please see the following article in the Microsoft Knowledge Base:
Q216825 BUG: Assignment of Multi-valued Objects in Request Object Causes Corruption in Scripting Dictionary
Additional query words: ASP Session Request Dictionary Scripting VBScript
Keywords : kbASP kbASP400fix kbASPObj kbIE400bug kbVBScript301bug kbGrpASP kbIE500fix
Version : WINDOWS:3.0,4.0,95,98; winnt:4.0
Platform : WINDOWS winnt
Issue type : kbbug
Last Reviewed: April 8, 1999