Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 19 additions & 34 deletions src/compat-string.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@
#include "compat-string.h"
#include <stdint.h>

// 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__)
Expand All @@ -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
Expand All @@ -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);
}
Expand All @@ -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); }