GHSA-ww6m-cw3f-q94g: Pip/nltk vulnerability

Published Sep 2, 2026
·
Updated

nltk.stem.PorterStemmer.stem() -- a ubiquitous public API applied to arbitrary, often untrusted, tokens -- runs in O(n^2) time on a token containing a long run of the letter 'y', letting a single ~20-50 KB token pin a CPU core (CWE-407).

Root cause

isconsonant(word, i) was made iterative (commit for #3633, GHSA/CWE-674) to fix an earlier unbounded-recursion RecursionError on 'y'10000. The iterative form walks backward over the whole run of 'y's on every call:

python while i > 0 and word[i] == 'y': negate = not negate i -= 1

measure() then calls isconsonant(stem, i) once for every position i of the stem. For a run of n 'y's that is sum{i} O(i) = O(n^2). The recursion fix therefore traded a CWE-674 RecursionError for a CWE-407 quadratic-time DoS.

Proof of concept

Measured (Python 3.13): stem('y'5000 + 'ness') = 2.6s, stem('y'10000 + 'ness') = 11.3s (2x input -> ~4.3x time = quadratic), stem('y'20000 + 'ness') > 20s. A pure run of 'y' with no matching suffix is fast because the stemmer rules that call measure do not fire; a real suffix such as 'ness' triggers measure on the long stem.

python from nltk.stem import PorterStemmer PorterStemmer().stem('y' 20000 + 'ness') # >20s of CPU

Impact

Stemming is routinely applied to untrusted text (search, indexing, NLP pipelines). A single unbroken ~20-50 KB token of 'y' characters (no whitespace, so it survives tokenization) causes multi-second-to-minutes CPU consumption per request. No confidentiality/integrity impact; single-process availability only.

Fix direction

Classify each character's consonant/vowel status in a single left-to-right O(n) pass (memoise the 'y' run parity) instead of re-walking the run on every isconsonant call, so measure and stemming are linear. This is a sibling of the corpus-reader quadratic advisories GHSA-vp2x-qp44-57v7 and GHSA-8mpw-7fpc-4gqj (CWE-407).

Affected Software

1 affected componentFixes available
pip/nltk<=3.10.2
3.10.3

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/nltk to a version that resolves this vulnerability.

    Fixed in 3.10.3

Event History

Sep 2, 2026
Advisory Published
via GitHub·02:36 PM
Data Sourced
via GitHub·02:36 PM
DescriptionWeaknessAffected Software

Frequently Asked Questions

1

Who is realistically exposed to this denial-of-service condition?

Applications that pass attacker-controlled or otherwise unbounded tokens to nltk.stem.PorterStemmer.stem() are exposed. A single token in the roughly 20–50 KB range can occupy a CPU core when it has a long run of "y" characters and a suffix that causes the relevant stemming rules to run.

2

What input is needed to trigger the expensive processing?

The token needs a long sequence of "y" characters followed by a suffix such as "ness". A token made only of "y" characters is reported to be fast because it does not trigger the rules that call _measure().

3

How can I assess the operational impact in my environment?

Test representative processing with inputs such as "y" repeated 5,000 times followed by "ness". Reported Python 3.13 timings were 2.6 seconds at 5,000 characters and 11.3 seconds at 10,000 characters, consistent with quadratic growth.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203