|
| 1 | +From 08d00ef18394e7cf5b1860224af821c6000e5858 Mon Sep 17 00:00:00 2001 |
| 2 | +From: itchyny <itchyny@cybozu.co.jp> |
| 3 | +Date: Mon, 13 Apr 2026 08:53:26 +0900 |
| 4 | +Subject: [PATCH] Randomize hash seed to mitigate hash collision DoS attacks |
| 5 | + |
| 6 | +The hash function used a fixed seed, allowing attackers to craft colliding keys |
| 7 | +and cause O(n^2) object parsing performance. Initialize the seed from a random |
| 8 | +source at process startup to prevent the attack. This fixes CVE-2026-40164. |
| 9 | + |
| 10 | +Co-authored-by: Asaf Meizner <asafmeizner@gmail.com> |
| 11 | +Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> |
| 12 | +Upstream-reference: https://github.com/jqlang/jq/commit/0c7d133c3c7e37c00b6d46b658a02244fdd3c784.patch |
| 13 | +--- |
| 14 | + configure.ac | 2 ++ |
| 15 | + src/jv.c | 34 ++++++++++++++++++++++++++++++++-- |
| 16 | + 2 files changed, 34 insertions(+), 2 deletions(-) |
| 17 | + |
| 18 | +diff --git a/configure.ac b/configure.ac |
| 19 | +index 118e084..7f12b52 100644 |
| 20 | +--- a/configure.ac |
| 21 | ++++ b/configure.ac |
| 22 | +@@ -149,6 +149,8 @@ AC_CHECK_MEMBER([struct tm.tm_gmtoff], [AC_DEFINE([HAVE_TM_TM_GMT_OFF],1,[Define |
| 23 | + AC_CHECK_MEMBER([struct tm.__tm_gmtoff], [AC_DEFINE([HAVE_TM___TM_GMT_OFF],1,[Define to 1 if the system has the __tm_gmt_off field in struct tm])], |
| 24 | + [], [[#include <time.h>]]) |
| 25 | + AC_FIND_FUNC([setlocale], [c], [#include <locale.h>], [0,0]) |
| 26 | ++AC_FIND_FUNC([arc4random], [c], [#include <stdlib.h>], []) |
| 27 | ++AC_FIND_FUNC([getentropy], [c], [#include <unistd.h>], [0, 0]) |
| 28 | + |
| 29 | + dnl Figure out if we have the pthread functions we actually need |
| 30 | + AC_FIND_FUNC_NO_LIBS([pthread_key_create], [], [#include <pthread.h>], [NULL, NULL]) |
| 31 | +diff --git a/src/jv.c b/src/jv.c |
| 32 | +index 73387d8..08ded35 100644 |
| 33 | +--- a/src/jv.c |
| 34 | ++++ b/src/jv.c |
| 35 | +@@ -40,6 +40,10 @@ |
| 36 | + #include <limits.h> |
| 37 | + #include <math.h> |
| 38 | + #include <float.h> |
| 39 | ++#include <time.h> |
| 40 | ++#include <unistd.h> |
| 41 | ++#include <fcntl.h> |
| 42 | ++#include <pthread.h> |
| 43 | + |
| 44 | + #include "jv_alloc.h" |
| 45 | + #include "jv.h" |
| 46 | +@@ -1183,7 +1187,33 @@ static jv jvp_string_append(jv string, const char* data, uint32_t len) { |
| 47 | + } |
| 48 | + } |
| 49 | + |
| 50 | +-static const uint32_t HASH_SEED = 0x432A9843; |
| 51 | ++static uint32_t hash_seed; |
| 52 | ++static pthread_once_t hash_seed_once = PTHREAD_ONCE_INIT; |
| 53 | ++ |
| 54 | ++static void jvp_hash_seed_init(void) { |
| 55 | ++ uint32_t seed; |
| 56 | ++#if defined(HAVE_ARC4RANDOM) |
| 57 | ++ seed = arc4random(); |
| 58 | ++#elif defined(HAVE_GETENTROPY) |
| 59 | ++ if (getentropy(&seed, sizeof(seed)) != 0) |
| 60 | ++ seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); |
| 61 | ++#else |
| 62 | ++ int fd = open("/dev/urandom", O_RDONLY); |
| 63 | ++ if (fd >= 0) { |
| 64 | ++ if (read(fd, &seed, sizeof(seed)) != 4) |
| 65 | ++ seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); |
| 66 | ++ close(fd); |
| 67 | ++ } else { |
| 68 | ++ seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); |
| 69 | ++ } |
| 70 | ++#endif |
| 71 | ++ hash_seed = seed; |
| 72 | ++} |
| 73 | ++ |
| 74 | ++static uint32_t jvp_hash_seed(void) { |
| 75 | ++ pthread_once(&hash_seed_once, jvp_hash_seed_init); |
| 76 | ++ return hash_seed; |
| 77 | ++} |
| 78 | + |
| 79 | + static uint32_t rotl32 (uint32_t x, int8_t r){ |
| 80 | + return (x << r) | (x >> (32 - r)); |
| 81 | +@@ -1202,7 +1232,7 @@ static uint32_t jvp_string_hash(jv jstr) { |
| 82 | + int len = (int)jvp_string_length(str); |
| 83 | + const int nblocks = len / 4; |
| 84 | + |
| 85 | +- uint32_t h1 = HASH_SEED; |
| 86 | ++ uint32_t h1 = jvp_hash_seed(); |
| 87 | + |
| 88 | + const uint32_t c1 = 0xcc9e2d51; |
| 89 | + const uint32_t c2 = 0x1b873593; |
| 90 | +-- |
| 91 | +2.45.4 |
| 92 | + |
0 commit comments