summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTelukula Jeevan Kumar Sahu <j-sahu@ti.com>2026-03-02 20:16:34 +0530
committerKhem Raj <raj.khem@gmail.com>2026-03-02 19:26:02 -0800
commitbb5f304e15ae959d91a34ee236b2e4cd93e02031 (patch)
tree58cffb175c876ac2232660ffca7643416e4b9b06
parentc3e16d369d6ec0b954a4d0c13eaeff54e61412b8 (diff)
downloadmeta-openembedded-bb5f304e15ae959d91a34ee236b2e4cd93e02031.tar.gz
nodejs: fix NEON llhttp ctzll undefined behavior
The NEON SIMD fast path in the bundled llhttp calls __builtin_ctzll(match_mask) without checking if match_mask is zero. When all 16 bytes in a NEON register are valid header value characters, match_mask is 0. Calling __builtin_ctzll(0) is undefined behavior. GCC at -O2 exploits this by optimizing "if (match_len != 16)" to always-true, causing HTTP 400 Bad Request for any header value longer than 16 characters on ARM targets with NEON enabled. Fix by explicitly checking for match_mask == 0 and setting match_len = 16. This bug affects both aarch64 and armv7 NEON targets. The code this patch modifies is generated, so the patch itself isn't suitable for upstream submission, as the root cause of the error is in the generator itself. The fix has been merged upstream[1] in llparse 7.3.1 and is included in llhttp 9.3.1. This patch can be dropped when nodejs updates its bundled llhttp to >= 9.3.1. [1]: https://github.com/nodejs/llparse/pull/83 Signed-off-by: Telukula Jeevan Kumar Sahu <j-sahu@ti.com> Signed-off-by: Khem Raj <raj.khem@gmail.com>
-rw-r--r--meta-oe/recipes-devtools/nodejs/nodejs/0001-llhttp-fix-NEON-header-value-__builtin_ctzll-undefin.patch60
-rw-r--r--meta-oe/recipes-devtools/nodejs/nodejs_22.22.0.bb1
2 files changed, 61 insertions, 0 deletions
diff --git a/meta-oe/recipes-devtools/nodejs/nodejs/0001-llhttp-fix-NEON-header-value-__builtin_ctzll-undefin.patch b/meta-oe/recipes-devtools/nodejs/nodejs/0001-llhttp-fix-NEON-header-value-__builtin_ctzll-undefin.patch
new file mode 100644
index 0000000000..683dddcf04
--- /dev/null
+++ b/meta-oe/recipes-devtools/nodejs/nodejs/0001-llhttp-fix-NEON-header-value-__builtin_ctzll-undefin.patch
@@ -0,0 +1,60 @@
1From a63a5faea54055973bf5f0a514444532563cc20d Mon Sep 17 00:00:00 2001
2From: Telukula Jeevan Kumar Sahu <j-sahu@ti.com>
3Date: Fri, 27 Feb 2026 20:58:43 +0530
4Subject: [PATCH] llhttp: fix NEON header value __builtin_ctzll undefined
5 behavior
6
7When all 16 bytes match the allowed range, match_mask becomes 0 after
8the bitwise NOT. Calling __builtin_ctzll(0) is undefined behavior per
9the C standard.
10
11The code expects match_len == 16 when all bytes match (so the branch
12is skipped and p += 16 continues the loop), but this relied on
13ctzll(0) returning 64, which is not guaranteed.
14
15GCC at -O2 exploits this UB by deducing that __builtin_ctzll() result
16is always in range [0, 63], and after >> 2 always in [0, 15], which
17is never equal to 16. The compiler then optimizes
18"if (match_len != 16)" to always-true, causing every valid 16-byte
19chunk to be falsely rejected as containing an invalid character.
20
21This manifests as HTTP 400 Bad Request (HPE_INVALID_HEADER_TOKEN) for
22any HTTP header value longer than 16 characters on ARM targets with
23NEON enabled.
24
25Fix by explicitly checking for match_mask == 0 and setting
26match_len = 16, avoiding the undefined behavior entirely. This bug
27affects both aarch64 and armv7 NEON targets.
28
29The fix has been merged upstream in llparse 7.3.1 [1] and is included
30in llhttp 9.3.1. This patch can be dropped when nodejs updates its
31bundled llhttp to >= 9.3.1.
32
33[1]: https://github.com/nodejs/llparse/pull/83
34
35Upstream-Status: Inappropriate
36Signed-off-by: Telukula Jeevan Kumar Sahu <j-sahu@ti.com>
37---
38 deps/llhttp/src/llhttp.c | 6 +++++-
39 1 file changed, 5 insertions(+), 1 deletion(-)
40
41diff --git a/deps/llhttp/src/llhttp.c b/deps/llhttp/src/llhttp.c
42index 14b731e..b0a46c6 100644
43--- a/deps/llhttp/src/llhttp.c
44+++ b/deps/llhttp/src/llhttp.c
45@@ -2651,7 +2651,11 @@ static llparse_state_t llhttp__internal__run(
46 mask = vorrq_u8(mask, single);
47 narrow = vshrn_n_u16(vreinterpretq_u16_u8(mask), 4);
48 match_mask = ~vget_lane_u64(vreinterpret_u64_u8(narrow), 0);
49- match_len = __builtin_ctzll(match_mask) >> 2;
50+ if (match_mask == 0) {
51+ match_len = 16;
52+ } else {
53+ match_len = __builtin_ctzll(match_mask) >> 2;
54+ }
55 if (match_len != 16) {
56 p += match_len;
57 goto s_n_llhttp__internal__n_header_value_otherwise;
58--
592.34.1
60
diff --git a/meta-oe/recipes-devtools/nodejs/nodejs_22.22.0.bb b/meta-oe/recipes-devtools/nodejs/nodejs_22.22.0.bb
index 05fa608047..d08c5d8318 100644
--- a/meta-oe/recipes-devtools/nodejs/nodejs_22.22.0.bb
+++ b/meta-oe/recipes-devtools/nodejs/nodejs_22.22.0.bb
@@ -33,6 +33,7 @@ SRC_URI = "https://nodejs.org/dist/v${PV}/node-v${PV}.tar.xz \
33 file://0001-build-remove-redundant-mXX-flags-for-V8.patch \ 33 file://0001-build-remove-redundant-mXX-flags-for-V8.patch \
34 file://0001-fix-arm-Neon-intrinsics-types.patch \ 34 file://0001-fix-arm-Neon-intrinsics-types.patch \
35 file://0001-detect-aarch64-Neon-correctly.patch \ 35 file://0001-detect-aarch64-Neon-correctly.patch \
36 file://0001-llhttp-fix-NEON-header-value-__builtin_ctzll-undefin.patch \
36 file://run-ptest \ 37 file://run-ptest \
37 " 38 "
38SRC_URI:append:class-target = " \ 39SRC_URI:append:class-target = " \