Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix large for-loop collection #1071

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

stayhero
Copy link

When a large Range is given in a for-loop (e.g. 1..100000000000000), it may crash the Ruby process or at least eats a lot of memory and takes a significant amount of time to render the template.

I added the following testcase to template_test.rb:

    t = Template.parse("{% for a in (1..100000000000000) %} {% for a in (1..10) %} foo {% endfor %} {% endfor %}")
    t.resource_limits.render_score_limit = 50
    assert_equal "Liquid error: Memory limits exceeded", t.render
    assert t.resource_limits.reached?

The render_score_limit does not help here because it's more of a Ruby performance problem in for.rb, where the collection = collection.to_a if collection.is_a?(Range) happens.

A fix may be to check if the given Range is larger than the given render_score_limit. I'm actually not sure if there may be a better fix (e.g. checking the resource limits somewhere else) but at least it works for the given test case.

A large collection in a for loop results in a crash or large memory
usage during render.
If a large collection is given to a for block, this may crash the render
process of the template. Setting of resource_limits does not help
because these are tested at a later stage.

This can be prevented by adding a simple check to the Liquid::For block
which makes sure that a collection size does never exceed the set
render_score_limit.
@ghost ghost added the cla-needed label Mar 11, 2019
@stayhero
Copy link
Author

stayhero commented Mar 12, 2019

BTW: You can checkout this branch to show the behavior for a large given collection without the fix:

https://github.com/stayhero/liquid/tree/large_forloop_failing_test?files=1

Copy link
Contributor

@pushrax pushrax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch!

Long-term I think it would make sense to iterate ranges more efficiently without constructing the intermediate array. Some range math can be done for the common cases to handle reverse and offsets. This patch makes sense as a solution for now though.

Resource limit gets triggered without a nested loop in the test for a
large collection range.
@ghost ghost removed the cla-needed label Mar 14, 2019
@stayhero
Copy link
Author

stayhero commented Apr 3, 2019

@pushrax Anything missing to merge the PR?

@fw42
Copy link
Contributor

fw42 commented Apr 26, 2019

@pushrax: ping

@gjtorikian
Copy link
Contributor

👋 Friendly bump to see what it would take to merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants