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

Valid IPv6 input may cause 'ipsubnet' filter to attempt to create 2 ^ 128 values #132

Closed
sebastien-rosset opened this issue Jan 24, 2022 · 0 comments · Fixed by #146
Closed

Comments

@sebastien-rosset
Copy link
Contributor

sebastien-rosset commented Jan 24, 2022

SUMMARY

The "2600:1f1c:1b3:8f00::/56" | ipsubnet(120, 0) filter never returns. Internally, the filter invokes the netaddr.subnet function, which attempts to create 18446744073709551616 values.

The ipsubnet filter creates a IPNetwork with "2600:1f1c:1b3:8f00::/56" and prefix length 120.
The ipsubnet filter invokes the netaddr.subnet function at https://github.com/ansible-collections/ansible.utils/blob/main/plugins/filter/ipsubnet.py#L283
)

return str(list(value.subnet(query))[index])

In the netaddr module, the subnet() function calculates the max number of subnets:

width = self._module.width
max_subnets = 2 ** (width - self.prefixlen) // 2 ** (width - prefixlen)

If the network prefix is /56 and the prefix length is /120, then max_subnets = 18446744073709551616.

In the worst case, the network address is /0 and the prefix length is 128, then max_subnets = 2 ^ 128 // 2 ^ 0, which is 2 ^ 128. If the count argument is set to 0, this will cause unbounded CPU usage and memory allocation.
So netaddr.subnet never returns.

ISSUE TYPE
  • Bug Report
COMPONENT NAME

ipsubnet filter

ANSIBLE VERSION

I have pasted the output below, but the problem is reproduced when running unit tests in the main branch of ansible.utils module as of 01/24/2022.

ansible [core 2.12.1]
  config file = None
  configured module search path = ['/home/serosset/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/serosset/.local/lib/python3.8/site-packages/ansible
  ansible collection location = /home/serosset/.ansible/collections:/usr/share/ansible/collections
  executable location = /opt/rh/rh-python38/root/usr/local/bin/ansible
  python version = 3.8.11 (default, Sep  1 2021, 12:33:46) [GCC 9.3.1 20200408 (Red Hat 9.3.1-2)]
  jinja version = 3.0.3
  libyaml = True
COLLECTION VERSION

Because the problem is exposed when running the test_ipsubnet.py unit tests inside a docker image, the applicable collections and configuration is what's inside the docker container. I'm not sure how to get that information. But see summary, I think the problem can be root caused to a specific line in the ipsubnet function in the main branch.

ansible-test units --python=3.10 --docker -vvvv tests/unit/plugins/filter/test_ipsubnet.py
CONFIGURATION

OS / ENVIRONMENT
STEPS TO REPRODUCE

Checkout the code from #131, then run the following command:

# ansible-test units --python=3.10 --docker -vvvv tests/unit/plugins/filter/test_ipsubnet.py

The following filter never completes: "2600:1f1c:1b3:8f00::/56" | ipsubnet(120, 0)

The problem gets worse when the network prefix has a smaller value and the first argument of ipsubnet has a higher value. In the worst case, this would cause 2 ^ 128 iterations.

EXPECTED RESULTS

"2600:1f1c:1b3:8f00::/56" | ipsubnet(120, 0) should return 2600:1f1c:1b3:8f00::/120
"2600:1f1c:1b3:8f00::/56" | ipsubnet(120, 4) should return 2600:1f1c:1b3:8f00::400/120

This could be calculated in constant time. Instead, the ipsubnet filter attempts to create all possible subnets, then get the subnet at index X. However, the list of all subnets could be 2 ^ 128 in the worst case.

ACTUAL RESULTS

The ipsubnet filter never returns. It iterates through a very large number of elements and never returns.


This was referenced Nov 3, 2022
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 a pull request may close this issue.

1 participant