{"id":"GHSA-p9jm-q85p-7mcp","summary":"Netty: RedisArrayAggregator max-elements failure leaves retained partial aggregate state","details":"## Summary\n\n`RedisArrayAggregator` clears retained partial aggregate state when the `maxNestedArrayDepth` limit is exceeded, but it does not clear the same state when the sibling `maxElements` limit is exceeded. A peer can start a valid RESP array, send a bulk-string child, then send a nested array header longer than the configured `maxElements`. Netty throws a decoder exception, but the existing partial aggregate remains retained in the handler.\n\nIf the application leaves the channel alive after the exception, later messages are still consumed into the pre-error aggregate. The supplied PoV proves both the retained `ByteBuf` reference and the stale parser state continuation.\n\n## Technical Details\n\n`RedisArrayAggregator.decode(...)` retains non-array messages before adding them to `depths.peek().children`. In `decodeRedisArrayHeader(...)`, the `header.length() \u003e maxElements` branch throws immediately:\n\n```java\nif (header.length() \u003e maxElements) {\n    throw new CodecException(\"this codec doesn't support longer length than \" + maxElements);\n}\n```\n\nThe immediately following nested-depth branch clears retained aggregate state before throwing:\n\n```java\nif (depths.size() \u003e= maxNestedArrayDepth) {\n    releaseAndClearDepths();\n    throw new CodecException(\"max nested array depth exceeded: \" + maxNestedArrayDepth);\n}\n```\n\nThe missing cleanup in the first branch leaves retained children and aggregate state reachable after the exception.\n\n## PoC\n\nPlace the supplied `RedisArrayAggregatorIncompleteCleanupPovTest.java` under:\n\n`codec-redis/src/test/java/io/netty/handler/codec/redis/`\n\nRun:\n\n```fish\n./mvnw -pl codec-redis -am -Dtest=RedisArrayAggregatorIncompleteCleanupPovTest -Dsurefire.failIfNoSpecifiedTests=false -DskipNativeTests -DskipAutobahnTests test\n```\n\nThe test suite includes:\n\n- serialized RESP trigger through `RedisDecoder`, `RedisBulkStringAggregator`, and `RedisArrayAggregator`;\n- direct refcount proof that max-elements overflow does not release the retained child immediately;\n- post-exception continuation proof that the stale aggregate consumes a later message;\n- nested-depth controls that clear the same partial aggregate state.\n\nAll five tests pass on current `4.2`, `4.2.15.Final`, and `4.1.135.Final`.\n\n## Impact\n\nFor Redis codec pipelines that continue after codec exceptions, an unauthenticated peer can keep attacker-controlled aggregate state alive across a security-limit exception. This can pin retained pooled buffers until channel close/removal or until a later message completes the stale aggregate.\n\n`RedisBulkStringAggregator` permits bulk strings up to `RedisConstants.REDIS_MESSAGE_MAX_LENGTH` (`512MB`), so the retained child can be large in deployments that aggregate untrusted Redis streams.\n\nApplications that always close the channel or remove the handler on decoder exceptions will trigger existing cleanup; the issue is the missing immediate cleanup on the max-elements failure path while the handler remains installed.\n\n## Suggested Fix\n\nCall `releaseAndClearDepths()` before throwing from the max-elements branch. Consider applying the same cleanup to all unrecoverable `decodeRedisArrayHeader(...)` error exits that can occur while `depths` is non-empty.\n\n## Affected Package/Versions\n\n`io.netty:netty-codec-redis`\n\nConfirmed on:\n\n- current `4.2` branch head `7bae566a93e69409697fe57fa807910ba5c9720e`\n- `4.2.15.Final` at `a41f7b289ce1`\n- `4.1.135.Final` at `f05f765d8146`\n\n## Advisory History\n\nThis differs from the public Redis codec advisories because it reproduces on their patched tags:\n\n- `GHSA-5w86-c3rq-vjj7`\n- `GHSA-3244-j874-rhc2` / `CVE-2026-44250`\n- `GHSA-6jv9-x5w9-2ccm` / `CVE-2026-48006`\n- `GHSA-6ghj-frrj-jjj3` / `CVE-2026-44890`\n\n## Why This Is Not Intended Behavior\n\nThe public API docs document `RedisArrayAggregator` as aggregating `RedisMessage` parts into `ArrayRedisMessage` and document a `CodecException` when an array header exceeds `maxElements`. They do not document preserving pre-exception partial aggregate state after that limit fires.\n\nThe adjacent nested-depth branch already calls `releaseAndClearDepths()` before throwing. The max-elements branch is the sibling aggregation-limit branch but throws without cleanup. Netty's later Redis lifecycle cleanup patch explicitly added release behavior for nested-array failure and handler removal, leaving the max-elements failure branch as a missed cleanup path.","aliases":["CVE-2026-56818"],"modified":"2026-08-07T17:41:12.448244Z","published":"2026-08-07T17:16:13Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-08-07T17:16:13Z","nvd_published_at":null,"cwe_ids":["CWE-401","CWE-703"],"severity":"MODERATE"},"references":[{"type":"WEB","url":"https://github.com/netty/netty/security/advisories/GHSA-p9jm-q85p-7mcp"},{"type":"WEB","url":"https://github.com/netty/netty/pull/17065"},{"type":"WEB","url":"https://github.com/netty/netty/commit/5b68c61f37aa4a3045cba624cbea239655c9003b"},{"type":"WEB","url":"https://github.com/netty/netty/commit/bb2ff68a1fb71cb4b0eb9a9e17b66c52aff680c6"},{"type":"PACKAGE","url":"https://github.com/netty/netty"}],"affected":[{"package":{"name":"io.netty:netty-codec-redis","ecosystem":"Maven","purl":"pkg:maven/io.netty/netty-codec-redis"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.1.136.Final"}]}],"versions":["4.1.0.Final","4.1.1.Final","4.1.10.Final","4.1.100.Final","4.1.101.Final","4.1.102.Final","4.1.103.Final","4.1.104.Final","4.1.105.Final","4.1.106.Final","4.1.107.Final","4.1.108.Final","4.1.109.Final","4.1.11.Final","4.1.110.Final","4.1.111.Final","4.1.112.Final","4.1.113.Final","4.1.114.Final","4.1.115.Final","4.1.116.Final","4.1.117.Final","4.1.118.Final","4.1.119.Final","4.1.12.Final","4.1.120.Final","4.1.121.Final","4.1.122.Final","4.1.123.Final","4.1.124.Final","4.1.125.Final","4.1.126.Final","4.1.127.Final","4.1.128.Final","4.1.129.Final","4.1.13.Final","4.1.130.Final","4.1.131.Final","4.1.132.Final","4.1.133.Final","4.1.134.Final","4.1.135.Final","4.1.14.Final","4.1.15.Final","4.1.16.Final","4.1.17.Final","4.1.18.Final","4.1.19.Final","4.1.2.Final","4.1.20.Final","4.1.21.Final","4.1.22.Final","4.1.23.Final","4.1.24.Final","4.1.25.Final","4.1.26.Final","4.1.27.Final","4.1.28.Final","4.1.29.Final","4.1.3.Final","4.1.30.Final","4.1.31.Final","4.1.32.Final","4.1.33.Final","4.1.34.Final","4.1.35.Final","4.1.36.Final","4.1.37.Final","4.1.38.Final","4.1.39.Final","4.1.4.Final","4.1.40.Final","4.1.41.Final","4.1.42.Final","4.1.43.Final","4.1.44.Final","4.1.45.Final","4.1.46.Final","4.1.47.Final","4.1.48.Final","4.1.49.Final","4.1.5.Final","4.1.50.Final","4.1.51.Final","4.1.52.Final","4.1.53.Final","4.1.54.Final","4.1.55.Final","4.1.56.Final","4.1.57.Final","4.1.58.Final","4.1.59.Final","4.1.6.Final","4.1.60.Final","4.1.61.Final","4.1.62.Final","4.1.63.Final","4.1.64.Final","4.1.65.Final","4.1.66.Final","4.1.67.Final","4.1.68.Final","4.1.69.Final","4.1.7.Final","4.1.70.Final","4.1.71.Final","4.1.72.Final","4.1.73.Final","4.1.74.Final","4.1.75.Final","4.1.76.Final","4.1.77.Final","4.1.78.Final","4.1.79.Final","4.1.8.Final","4.1.80.Final","4.1.81.Final","4.1.82.Final","4.1.83.Final","4.1.84.Final","4.1.85.Final","4.1.86.Final","4.1.87.Final","4.1.88.Final","4.1.89.Final","4.1.9.Final","4.1.90.Final","4.1.91.Final","4.1.92.Final","4.1.93.Final","4.1.94.Final","4.1.95.Final","4.1.96.Final","4.1.97.Final","4.1.98.Final","4.1.99.Final"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/08/GHSA-p9jm-q85p-7mcp/GHSA-p9jm-q85p-7mcp.json"}},{"package":{"name":"io.netty:netty-codec-redis","ecosystem":"Maven","purl":"pkg:maven/io.netty/netty-codec-redis"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.2.0-Final"},{"fixed":"4.2.16.Final"}]}],"versions":["4.2.0.Final","4.2.1.Final","4.2.10.Final","4.2.11.Final","4.2.12.Final","4.2.13.Final","4.2.14.Final","4.2.15.Final","4.2.2.Final","4.2.3.Final","4.2.4.Final","4.2.5.Final","4.2.6.Final","4.2.7.Final","4.2.8.Final","4.2.9.Final"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/08/GHSA-p9jm-q85p-7mcp/GHSA-p9jm-q85p-7mcp.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L"}]}