From fde95f8243696fdb33bbe01bbfd36e707bb65a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EB=8C=80=EC=9A=B0=20=28Daewoo=20Choi=29?= Date: Wed, 6 May 2026 16:57:35 +0900 Subject: [PATCH] HTTP/2: make ngx_http_huff_encode() output path alignment-safe In ngx_http_huff_encode(), the original output path used ngx_http_huff_encode_buf(dst, buf), which may expand to wide stores through (uint64_t *) or (uint32_t *) casts on the destination byte buffer. On an aarch64 cross-compiled environment, this was observed to corrupt some HTTP/2 response headers such as Date. This change preserves the existing HPACK Huffman encoding semantics and only changes how encoded bytes are written to dst: - keep the existing code/len table lookup logic - keep encoded length calculation unchanged - keep RFC 7541 padding behavior unchanged - replace architecture-sensitive wide-store output with byte-wise writes The new implementation accumulates bits in buf and emits bytes sequentially through u_char *dst, avoiding alignment-sensitive stores on the destination buffer and improving portability across cross-toolchains and architectures. --- src/http/ngx_http_huff_encode.c | 115 ++++++++++---------------------- 1 file changed, 36 insertions(+), 79 deletions(-) diff --git a/src/http/ngx_http_huff_encode.c b/src/http/ngx_http_huff_encode.c index c03b153da..39f368f80 100644 --- a/src/http/ngx_http_huff_encode.c +++ b/src/http/ngx_http_huff_encode.c @@ -156,99 +156,56 @@ static ngx_http_huff_encode_code_t ngx_http_huff_encode_table_lc[256] = }; -#if (NGX_PTR_SIZE == 8) - -#if (NGX_HAVE_LITTLE_ENDIAN) - -#if (NGX_HAVE_GCC_BSWAP64) -#define ngx_http_huff_encode_buf(dst, buf) \ - (*(uint64_t *) (dst) = __builtin_bswap64(buf)) -#else -#define ngx_http_huff_encode_buf(dst, buf) \ - ((dst)[0] = (u_char) ((buf) >> 56), \ - (dst)[1] = (u_char) ((buf) >> 48), \ - (dst)[2] = (u_char) ((buf) >> 40), \ - (dst)[3] = (u_char) ((buf) >> 32), \ - (dst)[4] = (u_char) ((buf) >> 24), \ - (dst)[5] = (u_char) ((buf) >> 16), \ - (dst)[6] = (u_char) ((buf) >> 8), \ - (dst)[7] = (u_char) (buf)) -#endif - -#else /* !NGX_HAVE_LITTLE_ENDIAN */ -#define ngx_http_huff_encode_buf(dst, buf) \ - (*(uint64_t *) (dst) = (buf)) -#endif - -#else /* NGX_PTR_SIZE == 4 */ - -#define ngx_http_huff_encode_buf(dst, buf) \ - (*(uint32_t *) (dst) = htonl(buf)) - -#endif - - size_t ngx_http_huff_encode(u_char *src, size_t len, u_char *dst, ngx_uint_t lower) { - u_char *end; - size_t hlen; - ngx_uint_t buf, pending, code; - ngx_http_huff_encode_code_t *table, *next; + u_char ch; + size_t hlt, pending; + ngx_uint_t i; + uint64_t buf; + ngx_http_huff_encode_code_t *table, *code; table = lower ? ngx_http_huff_encode_table_lc : ngx_http_huff_encode_table; - hlen = 0; + + hlt = 0; + for (i = 0; i < len; i++) { + ch = src[i]; + hlt += table[ch].len; + } + + hlt = (hlt + 7) / 8; + + if (dst == NULL) { + return hlt; + } + buf = 0; pending = 0; - end = src + len; + for (i = 0; i < len; i++) { + ch = src[i]; + code = &table[ch]; - while (src != end) { - next = &table[*src++]; + buf <<= code->len; + buf |= code->code; + pending += code->len; - code = next->code; - pending += next->len; - - /* accumulate bits */ - if (pending < sizeof(buf) * 8) { - buf |= code << (sizeof(buf) * 8 - pending); - continue; + /* + * Emit encoded output one byte at a time to avoid + * alignment-sensitive wide stores on the destination buffer. + */ + while (pending >= 8) { + pending -= 8; + *dst++ = (u_char) (buf >> pending); } - - if (hlen + sizeof(buf) >= len) { - return 0; - } - - pending -= sizeof(buf) * 8; - - buf |= code >> pending; - - ngx_http_huff_encode_buf(&dst[hlen], buf); - - hlen += sizeof(buf); - - buf = pending ? code << (sizeof(buf) * 8 - pending) : 0; } - if (pending == 0) { - return hlen; + if (pending > 0) { + buf <<= (8 - pending); + buf |= (uint64_t) (0xff >> pending); + *dst++ = (u_char) buf; } - buf |= (ngx_uint_t) -1 >> pending; - - pending = ngx_align(pending, 8); - - if (hlen + pending / 8 >= len) { - return 0; - } - - buf >>= sizeof(buf) * 8 - pending; - - do { - pending -= 8; - dst[hlen++] = (u_char) (buf >> pending); - } while (pending); - - return hlen; + return hlt; }