What changed, and why it matters
This commit removes compiler directives that forced certain cryptographic data structures to be packed tightly without padding, and replaces some direct multi-byte memory reads with safer memcpy operations. On some processors, reading a multi-byte value from a memory address that is not aligned to that value's size can cause a crash or reduced performance. The change makes the code safer and more portable across different CPU architectures, but it does not appear to fix an exploitable remote vulnerability in the Monero network itself.
Treat as a hardening/robustness fix rather than an urgent security patch. Review whether any other direct casts to uint64_t* remain in the crypto tree on strict-alignment platforms, and consider running tests on ARM/RISC-V hardware or QEMU. No immediate network-wide action is required.
Security signals we found
Removal of #pragma pack(1) on structures accessed as uint64_t arrays
Replacement of direct unaligned pointer casts with memcpy
Addition of alignment static_assert for union hash_state
Change of keccak temp buffer to uint64_t[18] with explicit size/alignment checks
No explicit security advisory, CVE, or researcher attribution in commit
Evidence from the diff
The patch removes #pragma pack(push,1) from union hash_state and union cn_slow_hash_state, adds a static_assert that hash_state is aligned to uint64_t, and switches several direct uint32_t/uint64_t pointer dereferences to memcpy or to aligned uint64_t arrays. In keccak.c the local temp buffer is changed from uint8_t[144] to uint64_t[18] and input is copied into it before 64-bit XOR operations. In slow-hash.c the VARIANT1_INIT64 macro copies the nonce via memcpy before XORing. These changes avoid undefined behavior from unaligned loads and may prevent crashes on strict-alignment architectures (e.g., some ARM, RISC-V, or older MIPS).
Changed components
src/crypto/CryptonightR_JIT.csrc/crypto/hash-ops.hsrc/crypto/keccak.csrc/crypto/slow-hash.cInspect captured patch +24 / −28
diff --git a/src/crypto/CryptonightR_JIT.c b/src/crypto/CryptonightR_JIT.c
index b59af8c..968f454 100644
--- a/src/crypto/CryptonightR_JIT.c
+++ b/src/crypto/CryptonightR_JIT.c
@@ -102,7 +102,7 @@ int v4_generate_JIT_code(const struct V4_Instruction* code, v4_random_math_JIT_f
APPEND_CODE(p1, p2 - p1);
if (inst.opcode == ADD)
- *(uint32_t*)(JIT_code - 4) = inst.C;
+ memcpy(JIT_code - 4, &inst.C, sizeof(inst.C));
}
APPEND_CODE(epilogue, sizeof(epilogue));
diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h
index 4323fa7..9250e4f 100644
--- a/src/crypto/hash-ops.h
+++ b/src/crypto/hash-ops.h
@@ -39,6 +39,7 @@
#include <assert.h>
#include <stdbool.h>
+#include <stdalign.h>
#include <stddef.h>
#include <stdint.h>
@@ -65,13 +66,12 @@ static inline void place_length(uint8_t *buffer, size_t bufsize, size_t length)
}
POP_WARNINGS
-#pragma pack(push, 1)
union hash_state {
uint8_t b[200];
uint64_t w[25];
};
-#pragma pack(pop)
static_assert(sizeof(union hash_state) == 200, "Invalid structure size");
+static_assert(alignof(union hash_state) == alignof(uint64_t), "Invalid structure alignment");
void hash_permutation(union hash_state *state);
void hash_process(union hash_state *state, const uint8_t *buf, size_t count);
diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c
index 6616d35..62e574b 100644
--- a/src/crypto/keccak.c
+++ b/src/crypto/keccak.c
@@ -119,50 +119,49 @@ typedef uint64_t state_t[25];
void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
{
state_t st;
- uint8_t temp[144];
+ uint64_t temp[18];
size_t i, rsiz, rsizw;
- static_assert(HASH_DATA_AREA <= sizeof(temp), "Bad keccak preconditions");
- if (mdlen <= 0 || (mdlen >= 100 && sizeof(st) != (size_t)mdlen))
+ static_assert(HASH_DATA_AREA + sizeof(uint64_t) == sizeof(temp), "Bad keccak preconditions");
+ if (mdlen <= 0 || (mdlen >= 100 && sizeof(st) != (size_t)mdlen) || ((size_t)mdlen % sizeof(uint64_t)) != 0)
{
local_abort("Bad keccak use");
}
rsiz = sizeof(state_t) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen;
rsizw = rsiz / 8;
+ if (rsiz == 0 || rsiz > sizeof(temp) || rsizw * sizeof(uint64_t) > sizeof(temp))
+ {
+ local_abort("Bad keccak use");
+ }
memset(st, 0, sizeof(st));
for ( ; inlen >= rsiz; inlen -= rsiz, in += rsiz) {
+ memcpy(temp, in, rsiz);
for (i = 0; i < rsizw; i++) {
- uint64_t ina;
- memcpy(&ina, in + i * 8, 8);
- st[i] ^= swap64le(ina);
+ st[i] ^= SWAP64LE(temp[i]);
}
keccakf(st, KECCAK_ROUNDS);
}
// last block and padding
- if (inlen + 1 >= sizeof(temp) || inlen > rsiz || rsiz - inlen + inlen + 1 >= sizeof(temp) || rsiz == 0 || rsiz - 1 >= sizeof(temp) || rsizw * 8 > sizeof(temp))
+ memset(temp, 0, sizeof(temp));
+ if (inlen >= rsiz)
{
local_abort("Bad keccak use");
}
-
if (inlen > 0)
memcpy(temp, in, inlen);
- temp[inlen++] = 1;
- memset(temp + inlen, 0, rsiz - inlen);
- temp[rsiz - 1] |= 0x80;
+ ((uint8_t *) temp)[inlen] = 1;
+ ((uint8_t *) temp)[rsiz - 1] |= 0x80;
- for (i = 0; i < rsizw; i++)
- st[i] ^= swap64le(((uint64_t *) temp)[i]);
+ for (i = 0; i < rsizw; i++) {
+ st[i] ^= SWAP64LE(temp[i]);
+ }
keccakf(st, KECCAK_ROUNDS);
- if (((size_t)mdlen % sizeof(uint64_t)) != 0)
- {
- local_abort("Bad keccak use");
- }
memcpy_swap64le(md, st, mdlen/sizeof(uint64_t));
}
diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c
index e2f856c..62c2786 100644
--- a/src/crypto/slow-hash.c
+++ b/src/crypto/slow-hash.c
@@ -155,11 +155,14 @@ static inline int force_software_aes(void)
} while(0)
#define VARIANT1_INIT64() \
+ uint64_t tweak1_2 = 0; \
if (variant == 1) \
{ \
+ uint64_t nonce; \
VARIANT1_CHECK(); \
- } \
- const uint64_t tweak1_2 = (variant == 1) ? (state.hs.w[24] ^ (*((const uint64_t*)NONCE_POINTER))) : 0
+ memcpy(&nonce, NONCE_POINTER, sizeof(nonce)); \
+ tweak1_2 = state.hs.w[24] ^ nonce; \
+ }
#define VARIANT2_INIT64() \
uint64_t division_result = 0; \
@@ -469,7 +472,6 @@ static inline int force_software_aes(void)
_b1 = _b; \
_b = _c; \
-#pragma pack(push, 1)
union cn_slow_hash_state
{
union hash_state hs;
@@ -479,7 +481,6 @@ union cn_slow_hash_state
uint8_t init[INIT_SIZE_BYTE];
};
};
-#pragma pack(pop)
THREADV uint8_t *hp_state = NULL;
THREADV int hp_allocated = 0;
@@ -1086,7 +1087,6 @@ STATIC INLINE void xor64(uint64_t *a, const uint64_t b)
*a ^= b;
}
-#pragma pack(push, 1)
union cn_slow_hash_state
{
union hash_state hs;
@@ -1096,7 +1096,6 @@ union cn_slow_hash_state
uint8_t init[INIT_SIZE_BYTE];
};
};
-#pragma pack(pop)
#if defined(__aarch64__) && defined(__ARM_FEATURE_CRYPTO)
@@ -1767,7 +1766,6 @@ static void xor64(uint8_t* left, const uint8_t* right)
}
}
-#pragma pack(push, 1)
union cn_slow_hash_state {
union hash_state hs;
struct {
@@ -1775,7 +1773,6 @@ union cn_slow_hash_state {
uint8_t init[INIT_SIZE_BYTE];
};
};
-#pragma pack(pop)
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) {
#ifndef FORCE_USE_HEAP
Why this scored 47/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.