From ac7f5c711a2a2ab08a0d36260c795f94e1501e41 Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Fri, 5 Jun 2026 20:39:12 +0000 Subject: [PATCH] fix memmove direction --- src/CMakeLists.txt | 1 + src/compat-string.c | 53 ++++++++++++++++----------------------------- 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4c3cd17..5cc779a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(bbs_sources OBJECT compat-string.c ) add_dependencies(bbs_sources blst) +set_source_files_properties(compat-string.c PROPERTIES COMPILE_OPTIONS "-fno-builtin") # Main library diff --git a/src/compat-string.c b/src/compat-string.c index 89a1ac9..b05cb31 100644 --- a/src/compat-string.c +++ b/src/compat-string.c @@ -2,9 +2,16 @@ #include "compat-string.h" #include -// In general, functions which set memory have a memory barrier if the compiler -// supports them, and functions reading from memory should have timing -// independent of the respective memory contents. +// Constant-time memory operations for cryptographic use. +// +// Functions that write memory include a compiler barrier to prevent +// dead-store elimination. Functions that read memory execute in time +// independent of the memory contents. +// +// These are intentionally NOT named memset/memcpy/etc. — overriding libc +// symbols affects the entire linked program, breaking performance and +// correctness of unrelated code. The -fno-builtin flag (set in CMakeLists) +// prevents the compiler from replacing these byte loops with calls to libc. static void *barrier(void *ret) { #if defined(__GNUC__) || defined(__clang__) @@ -13,11 +20,9 @@ static void *barrier(void *ret) { return ret; } -// GCC likes to replace loops with calls to these four functions. That is a) why -// they still have their original symbol names here, and b) why we must use -// function attributes to prevent GCC from implementing e.g. memcpy as a -// recursive call to memcpy *facepalm-emoji* - +// GCC's -ftree-loop-distribute-patterns pass recognizes byte-by-byte loops +// and replaces them with calls to memset/memcpy. This attribute disables +// that pass per-function. Clang relies on -fno-builtin instead. #if defined (__GNUC__) && !defined (__clang__) # define NO_OPTIMIZE __attribute__((optimize("no-tree-loop-distribute-patterns"))) #else @@ -26,16 +31,16 @@ static void *barrier(void *ret) { NO_OPTIMIZE -void *memcpy(void *dest, const void *src, size_t n) { +void *bbs_memcpy(void *dest, const void *src, size_t n) { char *sc = (char*)src, *dc = (char*)dest; while(n--) *dc++ = *sc++; return barrier(dest); } NO_OPTIMIZE -void *memmove(void *dest, const void *src, size_t n) { +void *bbs_memmove(void *dest, const void *src, size_t n) { char *sc = (char*)src + n, *dc = (char*)dest + n; - if(src < dest) return memcpy(dest, src, n); + if(dest <= src) return bbs_memcpy(dest, src, n); while(n--) *--dc = *--sc; return barrier(dest); } @@ -45,39 +50,19 @@ void *memmove(void *dest, const void *src, size_t n) { // a public volatile all-zero value. volatile int _bbs_zero = 0; NO_OPTIMIZE -int memcmp(const void *s1, const void *s2, size_t n) { +int bbs_memcmp(const void *s1, const void *s2, size_t n) { char *s1c = (char*)s1 + n, *s2c = (char*)s2 + n; volatile int res = 0, diff; while(n--) { diff = (*--s1c - *--s2c) ^ _bbs_zero; res = (res & (((diff-1) & ~diff) >> 8)) | diff; // cselect } - return res; // right shift on signed values was undefined + return res; } NO_OPTIMIZE -void *memset(void *s, int c, size_t n) { +void *bbs_memset(void *s, int c, size_t n) { char *sc = (char*)s; while(n--) *sc++ = c; return barrier(s); } - -// gcc likes to generate calls to the four library functions above, unless we -// Since our implementations have some guardrails, so we do not want to let the -// compiler assume we are explicitly using library functions outside this -// compilation unit. However, GCC will replace e.g. memset with -// __builtin_memset, which may be implemented and potentially removed inline. -// Hence the different names given here. That said, do not enable LTO in case -// compilers get smarter. -// An alternative, suggested online, is to use -fno-builtins, which is -// gcc-specific and may in some cases degrade performance. - -void *bbs_memset(void *s, int c, size_t n) - { return memset(s, c, n); } -int bbs_memcmp(const void *s1, const void *s2, size_t n) - { return memcmp(s1,s2,n); } -void *bbs_memmove(void *dest, const void *src, size_t n) - { return memmove(dest,src,n); } -void *bbs_memcpy(void *dest, const void *src, size_t n) - { return memcpy(dest,src,n); } -