Skip to main content
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Nolan Erck
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Nolan Erck

Strange ___IMPLICITARRYSTRUCTVAR Behavior In ColdFusion

By
Published in Comments (5)

While James England and I were working on Dig Deep Fitness, we came across a rather mysterious ColdFusion behavior. The local memory leak detection mechanism started reporting the existence of an unexpected variable called, ___IMPLICITARRYSTRUCTVAR0. I'd never seen this before; but Google pointed me to something Adam Cameron mentioned it on an old Adobe ColdFusion forum post from 2012. Apparently implicit struct and array notation has been creating these hidden variables since ColdFusion 9.

Demonstration of Variable Behavior

First, let's take a look at what ColdFusion is doing. This behavior only seems to manifest when the implicit struct or array notation is used 1) inside a Function execution and 2) within a ternary operator. As such, we're going to define an immediately-invoked function expression (IIFE) which executes two nonsense ternary operators. Then, we're going to output the variables scope.

<cfscript>

	(() => {

		var arrayValue = false
			? "yay"
			: []
		;
		var structValue = false
			? "yay"
			: {}
		;

		for ( key in variables.keyArray() ) {

			// Note: wrapping output in array because dumping-out a struct will hide any
			// key that contains the substring "___IMPLICITARRYSTRUCTVAR".
			writeDump([ key, variables[ key ] ]);

		}

	})();

</cfscript>

After the two ternary operators are executed, I output the keys in the variables scope. When I do this, I'm wrapping the key-value pairs in an array since ColdFusion will actively hide any struct entry in which the key contains the substring, ___IMPLICITARRYSTRUCTVAR. This is probably why I've never noticed this before.

When we run this Adobe ColdFusion 2023 code, we get the following output:

A CFDump of the variables scope shows several hidden variables.

Notice that ColdFusion has created two hidden variables relating to the implicit data structure notation: ___IMPLICITARRYSTRUCTVAR0 and ___IMPLICITARRYSTRUCTVAR1. These variables contain the result of the implicit data structure expression execution.

These Variables Are Not Thread Safe

These implicit struct and implicit array variables are stored in the variables scope. When this is done within a ColdFusion template or a ColdFusion custom tag, this is safe because the variables scope only lasts as long as the template execution (more or less). But, when this is done within a persisted ColdFusion component (CFC), it can become a problem.

When a ColdFusion component is instantiated and cached for the lifetime of a ColdFusion application, the variables scope of said component becomes a shared memory space that can be accessed by parallel requests and parallel threads. This can create a dangerous environment if you don't take precautions. And, it can create an even more dangerous environment when you don't even know that a shared variable is being created behind the scenes (especially one that is actively hidden from CFDump and WriteDump() output).

Let's take a look at how the dangerous behavior can manifest. We're going to use the asynchronous iteration features of ColdFusion to launch a number of parallel threads. These threads will then use implicit struct notation to define and mutate a local variable:

<cfscript>

	arrayNew( 1 )
		// Create a collection that is large(ish).
		.resize( 1000 )
		.set( 1, 1000, 1 )
		// Iterate over the collection using PARALLEL THREADS.
		.each(
			() => {

				var value = false
					? "yay"
					: { count: 0 } // CAUTION: Creates a shared variable.
				;

				// Increment LOCAL value.
				value.count++;

			},
			true, // Parallel iteration.
			20
		)
	;

	// Output the private page variables.
	for ( key in variables.keyArray() ) {

		writeDump([ key, variables[ key ] ]);

	}

</cfscript>

As you can see, the value variable is declared as local variable to the iteration operator, which should make it safe to subsequently mutate. However, when we run this Adobe ColdFusion 2023 code, we get the following error:

An Adobe ColdFusion error that reads: Error Occurred While Processing Request. coldfusion.runtime.UndefinedElementException: Element COUNT is undefined in VALUE.

As some point, during the parallel iteration, we get the error Element COUNT is undefined in VALUE. This is when we go to increment the value.count key, which should be local to the iterator function and therefore safe to mutate.

What I assume is happening is that the results of the ternary operator are being mechanised as series of assignments:

  1. Create empty struct using ___IMPLICITARRYSTRUCTVAR0 value, stored in the variables scope.

  2. Append count:0 entry to ___IMPLICITARRYSTRUCTVAR0.

  3. Assign ___IMPLICITARRYSTRUCTVAR0 to value variable, stored in the local scope.

Any time you have a multi-step process that touches shared memory, the process is inherently unsafe for parallel computing unless locking precautions are applied. Of course, we didn't know that this was happening; so, we didn't expect this code be unsafe.

Aside: even the ++ and -- operators are unsafe to use when it comes to a shared memory space.

What's likely happening is that just before one of the iterations goes to increment the .count key, a parallel iteration thread has just overwritten the shared ___IMPLICITARRYSTRUCTVAR0 variable, thereby clobbering the .count key for the other thread.

To work around this bug, all you have to do is move the implicit struct expression out of the ternary operator. One way to do that is to replace the ternary operator with an if/else control flow:

<cfscript>

	arrayNew( 1 )
		// Create a collection that is large(ish).
		.resize( 1000 )
		.set( 1, 1000, 1 )
		// Iterate over the collection using PARALLEL THREADS.
		.each(
			() => {

				// By moving the implicit struct OUT of the ternary operator and into an
				// if/else block, we remove the implicit variable creation.
				if ( false ) {

					var value = "yay";

				} else {

					var value = { count: 0 };
				}

				// Increment LOCAL value.
				value.count++;

			},
			true,
			20
		)
	;

	// Output the private page variables.
	for ( key in variables.keyArray() ) {

		writeDump([ key, variables[ key ] ]);

	}

</cfscript>

This time, when we execute this Adobe ColdFusion 2023 code, it executes without error. And, the resultant page is blank since no ___IMPLICITARRYSTRUCTVAR0 variable is created in the variables scope.

Crazy stuff! I don't know if this is limited to the ternary operator; but, it's the only place that I've seen this behavior manifest itself.

Want to use code from this post? Check out the license.

Reader Comments

254 Comments

This is incredibly insightful and very well explained. When I get random errors I can't explain, I usually just throw my hands up and hope it resolves itself 🤣

But now you've given me another possible angle to consider. Fortunately, I don't do much parallel processing.

15,929 Comments

@Chris,

Sometimes, it's fun to dig deep into what is going wrong. Of course, I don't write about the times I failed to figure it out 😜

re: parallel processing, even if you're not doing much stuff with async iteration, multiple users making a request to the same page is still parallel processing. If this workflow were part of a cached CFC instance, for example, and multiple users were hitting a Function that had the implicit struct / ternary operator combination, you'd still be able to trigger the same edge case.

That's actually the scarier concept. If the issue were to always throw an error, that's not so bad. But, the real red flag is if there's any chance that one user's data might accidentally stored into a struct that is then made available to a different user, that could have very bad consequences (and would be essentially impossible to debug since you'd have no idea this was actually the cause).

I'm not saying that I could even reproduce that cross-user issue; but the fact that two threads affect each other in an unexpected way make me think it might be possible.

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel