summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/freebl/stubs.c13
-rw-r--r--lib/freebl/stubs.h1
-rw-r--r--lib/util/nssutil.def6
-rw-r--r--lib/util/secport.c77
-rw-r--r--lib/util/secport.h1
5 files changed, 98 insertions, 0 deletions
diff --git a/lib/freebl/stubs.c b/lib/freebl/stubs.c
index 82b5b2e78..3697a7aff 100644
--- a/lib/freebl/stubs.c
+++ b/lib/freebl/stubs.c
@@ -86,6 +86,10 @@
if (ptr_##fn) { \
return ptr_##fn(a1, a2, a3, a4); \
}
+#define STUB_SAFE_CALL5(fn, a1, a2, a3, a4, a5) \
+ if (ptr_##fn) { \
+ return ptr_##fn(a1, a2, a3, a4, a5); \
+ }
#define STUB_SAFE_CALL6(fn, a1, a2, a3, a4, a5, a6) \
if (ptr_##fn) { \
return ptr_##fn(a1, a2, a3, a4, a5, a6); \
@@ -177,6 +181,7 @@ STUB_DECLARE(void, SECITEM_ZfreeItem_Util, (SECItem * zap, PRBool freeit));
STUB_DECLARE(SECOidTag, SECOID_FindOIDTag_Util, (const SECItem *oid));
STUB_DECLARE(int, NSS_SecureMemcmp, (const void *a, const void *b, size_t n));
STUB_DECLARE(unsigned int, NSS_SecureMemcmpZero, (const void *mem, size_t n));
+STUB_DECLARE(void, NSS_SecureSelect, (void *dest, const void *src0, const void *src1, size_t n, unsigned char b));
#define PORT_ZNew_stub(type) (type *)PORT_ZAlloc_stub(sizeof(type))
#define PORT_New_stub(type) (type *)PORT_Alloc_stub(sizeof(type))
@@ -700,6 +705,13 @@ NSS_SecureMemcmpZero_stub(const void *mem, size_t n)
abort();
}
+extern void
+NSS_SecureSelect_stub(void *dest, const void *src0, const void *src1, size_t n, unsigned char b)
+{
+ STUB_SAFE_CALL5(NSS_SecureSelect, dest, src0, src1, n, b);
+ abort();
+}
+
#ifdef FREEBL_NO_WEAK
static const char *nsprLibName = SHLIB_PREFIX "nspr4." SHLIB_SUFFIX;
@@ -753,6 +765,7 @@ freebl_InitNSSUtil(void *lib)
STUB_FETCH_FUNCTION(SECOID_FindOIDTag_Util);
STUB_FETCH_FUNCTION(NSS_SecureMemcmp);
STUB_FETCH_FUNCTION(NSS_SecureMemcmpZero);
+ STUB_FETCH_FUNCTION(NSS_SecureSelect);
return SECSuccess;
}
diff --git a/lib/freebl/stubs.h b/lib/freebl/stubs.h
index e6d9ed03a..f773e1043 100644
--- a/lib/freebl/stubs.h
+++ b/lib/freebl/stubs.h
@@ -42,6 +42,7 @@
#define SECOID_FindOIDTag SECOID_FindOIDTag_stub
#define NSS_SecureMemcmp NSS_SecureMemcmp_stub
#define NSS_SecureMemcmpZero NSS_SecureMemcmpZero_stub
+#define NSS_SecureSelect NSS_SecureSelect_stub
#define PR_Assert PR_Assert_stub
#define PR_Access PR_Access_stub
diff --git a/lib/util/nssutil.def b/lib/util/nssutil.def
index fa0373074..a48794e47 100644
--- a/lib/util/nssutil.def
+++ b/lib/util/nssutil.def
@@ -348,3 +348,9 @@ PK11URI_GetQueryAttributeItem;
;+ local:
;+ *;
;+};
+;+NSSUTIL_3.90 { # NSS Utilities 3.90 release
+;+ global:
+NSS_SecureSelect;
+;+ local:
+;+ *;
+;+};
diff --git a/lib/util/secport.c b/lib/util/secport.c
index 8594072df..fb5223d64 100644
--- a/lib/util/secport.c
+++ b/lib/util/secport.c
@@ -800,3 +800,80 @@ NSS_SecureMemcmpZero(const void *mem, size_t n)
/* 0 <= r < 256, so -r has bit 8 set when r != 0 */
return 1 & (-r >> 8);
}
+
+/*
+ * A "value barrier" prevents the compiler from making optimizations based on
+ * the value that a variable takes.
+ *
+ * Standard C does not have value barriers, so C implementations of them are
+ * compiler-specific and are not guaranteed to be effective. Thus, the value
+ * barriers here are a best-effort, defense-in-depth, strategy. They are not a
+ * substitute for standard constant-time programming discipline.
+ *
+ * Some implementations have a performance penalty, so value barriers should
+ * be used sparingly.
+ */
+static inline int
+value_barrier_int(int x)
+{
+#if defined(__GNUC__) || defined(__clang__)
+ /* This inline assembly trick from Chandler Carruth's CppCon 2015 talk
+ * generates no instructions.
+ *
+ * "+r"(x) means that x will be mapped to a register that is both an input
+ * and an output to the assembly routine (""). The compiler will not
+ * inspect the assembly routine itself, so it cannot assume anything about
+ * the value of x after this line.
+ */
+ __asm__(""
+ : "+r"(x)
+ : /* no other inputs */);
+ return x;
+#else
+ /* If the compiler does not support the inline assembly trick above, we can
+ * put x in `volatile` storage and read it out again. This will generate
+ * explict store and load instructions, and possibly more depending on the
+ * target.
+ */
+ volatile int y = x;
+ return y;
+#endif
+}
+
+/*
+ * A branch-free implementation of
+ * if (!b) {
+ * memmove(dest, src0, n);
+ * } else {
+ * memmove(dest, src1, n);
+ * }
+ *
+ * The memmove is performed with src0 if `b == 0` and with src1
+ * otherwise.
+ *
+ * As with memmove, the selected src can overlap dest.
+ *
+ * Each of dest, src0, and src1 must point to an allocated buffer
+ * of at least n bytes.
+ */
+void
+NSS_SecureSelect(void *dest, const void *src0, const void *src1, size_t n, unsigned char b)
+
+{
+ // This value barrier makes it safe for the compiler to inline
+ // NSS_SecureSelect into a routine where it could otherwise infer something
+ // about the value of b, e.g. that b is 0/1 valued.
+ int w = value_barrier_int(b);
+
+ // 0 <= b < 256, and int is at least 16 bits, so -w has bits 8-15
+ // set when w != 0.
+ unsigned char mask = 0xff & (-w >> 8);
+
+ for (size_t i = 0; i < n; ++i) {
+ unsigned char s0i = ((unsigned char *)src0)[i];
+ unsigned char s1i = ((unsigned char *)src1)[i];
+ // if mask == 0 this simplifies to s0 ^ 0
+ // if mask == -1 this simplifies to s0 ^ s0 ^ s1
+ ((unsigned char *)dest)[i] = s0i ^ (mask & (s0i ^ s1i));
+ }
+}
diff --git a/lib/util/secport.h b/lib/util/secport.h
index fc1e1f538..fa587b28a 100644
--- a/lib/util/secport.h
+++ b/lib/util/secport.h
@@ -261,6 +261,7 @@ extern int NSS_PutEnv(const char *envVarName, const char *envValue);
extern int NSS_SecureMemcmp(const void *a, const void *b, size_t n);
extern unsigned int NSS_SecureMemcmpZero(const void *mem, size_t n);
+extern void NSS_SecureSelect(void *dest, const void *src0, const void *src1, size_t n, unsigned char b);
/*
* Load a shared library called "newShLibName" in the same directory as