GetNextSegment with empty Remaining causing fuzzes

Posted on July 07, 2024

Optimizely CMS offers you to create partial routers. This concept allows you display content differently depending on the routed content in the URL. Of course, a more in-dept explanation can be found at the following URL: https://docs.developers.optimizely.com/content-management-system/docs/example-of-news-partial-routing. All in all, this concept also existed in the previous version of the CMS, but it differed a bit than it is currently.

See, when we migrated a customer from .NET Framework to .NET Core, AKA CMS 11 to 12, we encountered a memory leak that we couldn't really put our hand on at first sight. The solution heavily used those partial routers. Our issue also often occurred on DXP and not on our development machine, which was even weirder. Until we remembered that the application is automatically warmed up when running under DXP. This led us to find the source of the problem, the partial routers, but more specifically on the part when we get the next value of the URL segment.

As explained in the CMS 12 documentation, you can use from an UrlResolverContext the method GetNextSegment to get that information. It returns a Segment, which contains what's "next" and the "remaining path". Our code, since it was originally developed for CMS 11, used at the time the old API, a method "GetNextValue", probably from a similar service which I don't reminder the name and was looping around it until Optimizely code returned an empty segment. It all makes sense right? Here's the decompiled code from CMS 11:

As you can see, CMS 11 was simply returning an empty "next" and "remaining" SegmentPair if the remaining path was empty. Our code was relying on this information, well, specifically the property "Remaining" of the segment pair. I'm sure at this point you're probably realizing why we were having memory leaks. Indeed, under CMS 12, this piece of code changed and caused infinite loops in ours, concatenating string to infinity and beyond. The change itself is very inexplicable I would say, even myself don't understand why Optimizely decided to make it that way, it feels more like an unwanted error than anything else. Here's a snapshot of the decompiled code, this will speak for itself:

It, hum, well, reassign the remaining path if it's empty, but also have the same check just after, a condition, which at this point, will never be met, that simply does what we would expect.

We informed Optimizely of this quirk, though unfortunately didn't ended up being fixed. Our code now has different checks and also a failsafe to avoid looping indefinitely. I hope that with this post can save you time diagnosing similar problems on your end.