Skip to content

Commit

Permalink
Work around language and compiler bug in memcpy, etc.
Browse files Browse the repository at this point in the history
Most C standard library functions are undefined if passed NULL, even
when the corresponding length is zero. This gives them (and, in turn,
all functions which call them) surprising behavior on empty arrays.
Some compilers will miscompile code due to this rule. See also
https://www.imperialviolet.org/2016/06/26/nonnull.html

Add OPENSSL_memcpy, etc., wrappers which avoid this problem.

BUG=23

Change-Id: I95f42b23e92945af0e681264fffaf578e7f8465e
Reviewed-on: https://boringssl-review.googlesource.com/12928
Commit-Queue: David Benjamin <[email protected]>
Reviewed-by: Adam Langley <[email protected]>
  • Loading branch information
davidben authored and agl committed Dec 21, 2016
1 parent 56cadc3 commit 17cf2cb
Show file tree
Hide file tree
Showing 188 changed files with 1,084 additions and 755 deletions.
10 changes: 10 additions & 0 deletions STYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ not
Rather than `malloc()` and `free()`, use the wrappers `OPENSSL_malloc()`
and `OPENSSL_free()`. Use the standard C `assert()` function freely.

Use the following wrappers, found in `crypto/internal.h` instead of the
corresponding C standard library functions. They behave the same but avoid
confusing undefined behavior.

* `OPENSSL_memchr`
* `OPENSSL_memcmp`
* `OPENSSL_memcpy`
* `OPENSSL_memmove`
* `OPENSSL_memset`

For new constants, prefer enums when the values are sequential and typed
constants for flags. If adding values to an existing set of `#define`s,
continue with `#define`.
Expand Down
9 changes: 5 additions & 4 deletions crypto/aes/aes_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <openssl/aes.h>
#include <openssl/crypto.h>

#include "../internal.h"
#include "../test/file_test.h"


Expand Down Expand Up @@ -54,7 +55,7 @@ static bool TestRaw(FileTest *t) {
}

// Test in-place encryption.
memcpy(block, plaintext.data(), AES_BLOCK_SIZE);
OPENSSL_memcpy(block, plaintext.data(), AES_BLOCK_SIZE);
AES_encrypt(block, block, &aes_key);
if (!t->ExpectBytesEqual(block, AES_BLOCK_SIZE, ciphertext.data(),
ciphertext.size())) {
Expand All @@ -76,7 +77,7 @@ static bool TestRaw(FileTest *t) {
}

// Test in-place decryption.
memcpy(block, ciphertext.data(), AES_BLOCK_SIZE);
OPENSSL_memcpy(block, ciphertext.data(), AES_BLOCK_SIZE);
AES_decrypt(block, block, &aes_key);
if (!t->ExpectBytesEqual(block, AES_BLOCK_SIZE, plaintext.data(),
plaintext.size())) {
Expand Down Expand Up @@ -123,7 +124,7 @@ static bool TestKeyWrap(FileTest *t) {
return false;
}

memset(buf.get(), 0, ciphertext.size());
OPENSSL_memset(buf.get(), 0, ciphertext.size());
if (AES_wrap_key(&aes_key, kDefaultIV, buf.get(), plaintext.data(),
plaintext.size()) != static_cast<int>(ciphertext.size()) ||
!t->ExpectBytesEqual(buf.get(), ciphertext.size(), ciphertext.data(),
Expand All @@ -146,7 +147,7 @@ static bool TestKeyWrap(FileTest *t) {
return false;
}

memset(buf.get(), 0, plaintext.size());
OPENSSL_memset(buf.get(), 0, plaintext.size());
if (AES_unwrap_key(&aes_key, kDefaultIV, buf.get(), ciphertext.data(),
ciphertext.size()) != static_cast<int>(plaintext.size()) ||
!t->ExpectBytesEqual(buf.get(), plaintext.size(), plaintext.data(),
Expand Down
20 changes: 11 additions & 9 deletions crypto/aes/key_wrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@

#include <openssl/mem.h>

#include "../internal.h"


/* kDefaultIV is the default IV value given in RFC 3394, 2.2.3.1. */
static const uint8_t kDefaultIV[] = {
Expand All @@ -73,27 +75,27 @@ int AES_wrap_key(const AES_KEY *key, const uint8_t *iv, uint8_t *out,
iv = kDefaultIV;
}

memmove(out + 8, in, in_len);
OPENSSL_memmove(out + 8, in, in_len);
uint8_t A[AES_BLOCK_SIZE];
memcpy(A, iv, 8);
OPENSSL_memcpy(A, iv, 8);

size_t n = in_len / 8;

for (unsigned j = 0; j < kBound; j++) {
for (size_t i = 1; i <= n; i++) {
memcpy(A + 8, out + 8 * i, 8);
OPENSSL_memcpy(A + 8, out + 8 * i, 8);
AES_encrypt(A, A, key);

uint32_t t = (uint32_t)(n * j + i);
A[7] ^= t & 0xff;
A[6] ^= (t >> 8) & 0xff;
A[5] ^= (t >> 16) & 0xff;
A[4] ^= (t >> 24) & 0xff;
memcpy(out + 8 * i, A + 8, 8);
OPENSSL_memcpy(out + 8 * i, A + 8, 8);
}
}

memcpy(out, A, 8);
OPENSSL_memcpy(out, A, 8);
return (int)in_len + 8;
}

Expand All @@ -110,8 +112,8 @@ int AES_unwrap_key(const AES_KEY *key, const uint8_t *iv, uint8_t *out,
}

uint8_t A[AES_BLOCK_SIZE];
memcpy(A, in, 8);
memmove(out, in + 8, in_len - 8);
OPENSSL_memcpy(A, in, 8);
OPENSSL_memmove(out, in + 8, in_len - 8);

size_t n = (in_len / 8) - 1;

Expand All @@ -122,9 +124,9 @@ int AES_unwrap_key(const AES_KEY *key, const uint8_t *iv, uint8_t *out,
A[6] ^= (t >> 8) & 0xff;
A[5] ^= (t >> 16) & 0xff;
A[4] ^= (t >> 24) & 0xff;
memcpy(A + 8, out + 8 * (i - 1), 8);
OPENSSL_memcpy(A + 8, out + 8 * (i - 1), 8);
AES_decrypt(A, A, key);
memcpy(out + 8 * (i - 1), A + 8, 8);
OPENSSL_memcpy(out + 8 * (i - 1), A + 8, 8);
}
}

Expand Down
9 changes: 6 additions & 3 deletions crypto/asn1/a_bitstr.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@
#include <openssl/err.h>
#include <openssl/mem.h>

#include "../internal.h"


int ASN1_BIT_STRING_set(ASN1_BIT_STRING *x, unsigned char *d, int len)
{
return M_ASN1_BIT_STRING_set(x, d, len);
Expand Down Expand Up @@ -115,7 +118,7 @@ int i2c_ASN1_BIT_STRING(ASN1_BIT_STRING *a, unsigned char **pp)

*(p++) = (unsigned char)bits;
d = a->data;
memcpy(p, d, len);
OPENSSL_memcpy(p, d, len);
p += len;
if (len > 0)
p[-1] &= (0xff << bits);
Expand Down Expand Up @@ -162,7 +165,7 @@ ASN1_BIT_STRING *c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a,
OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
goto err;
}
memcpy(s, p, (int)len);
OPENSSL_memcpy(s, p, (int)len);
s[len - 1] &= (0xff << padding);
p += len;
} else
Expand Down Expand Up @@ -215,7 +218,7 @@ int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *a, int n, int value)
return 0;
}
if (w + 1 - a->length > 0)
memset(c + a->length, 0, w + 1 - a->length);
OPENSSL_memset(c + a->length, 0, w + 1 - a->length);
a->data = c;
a->length = w + 1;
}
Expand Down
5 changes: 4 additions & 1 deletion crypto/asn1/a_enum.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@
#include <openssl/err.h>
#include <openssl/mem.h>

#include "../internal.h"


/*
* Code for ENUMERATED type: identical to INTEGER apart from a different tag.
* for comments on encoding see a_int.c
Expand All @@ -79,7 +82,7 @@ int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v)
OPENSSL_free(a->data);
if ((a->data =
(unsigned char *)OPENSSL_malloc(sizeof(long) + 1)) != NULL)
memset((char *)a->data, 0, sizeof(long) + 1);
OPENSSL_memset((char *)a->data, 0, sizeof(long) + 1);
}
if (a->data == NULL) {
OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
Expand Down
11 changes: 7 additions & 4 deletions crypto/asn1/a_int.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@
#include <openssl/err.h>
#include <openssl/mem.h>

#include "../internal.h"


ASN1_INTEGER *ASN1_INTEGER_dup(const ASN1_INTEGER *x)
{
return M_ASN1_INTEGER_dup(x);
Expand Down Expand Up @@ -157,7 +160,7 @@ int i2c_ASN1_INTEGER(ASN1_INTEGER *a, unsigned char **pp)
if (a->length == 0)
*(p++) = 0;
else if (!neg)
memcpy(p, a->data, (unsigned int)a->length);
OPENSSL_memcpy(p, a->data, (unsigned int)a->length);
else {
/* Begin at the end of the encoding */
n = a->data + a->length - 1;
Expand Down Expand Up @@ -254,7 +257,7 @@ ASN1_INTEGER *c2i_ASN1_INTEGER(ASN1_INTEGER **a, const unsigned char **pp,
p++;
len--;
}
memcpy(s, p, (int)len);
OPENSSL_memcpy(s, p, (int)len);
}

if (ret->data != NULL)
Expand Down Expand Up @@ -322,7 +325,7 @@ ASN1_INTEGER *d2i_ASN1_UINTEGER(ASN1_INTEGER **a, const unsigned char **pp,
p++;
len--;
}
memcpy(s, p, (int)len);
OPENSSL_memcpy(s, p, (int)len);
p += len;
}

Expand Down Expand Up @@ -354,7 +357,7 @@ int ASN1_INTEGER_set(ASN1_INTEGER *a, long v)
OPENSSL_free(a->data);
if ((a->data =
(unsigned char *)OPENSSL_malloc(sizeof(long) + 1)) != NULL)
memset((char *)a->data, 0, sizeof(long) + 1);
OPENSSL_memset((char *)a->data, 0, sizeof(long) + 1);
}
if (a->data == NULL) {
OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
Expand Down
7 changes: 5 additions & 2 deletions crypto/asn1/a_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@
#include <openssl/mem.h>
#include <openssl/obj.h>

#include "../internal.h"


int i2d_ASN1_OBJECT(ASN1_OBJECT *a, unsigned char **pp)
{
unsigned char *p;
Expand All @@ -77,7 +80,7 @@ int i2d_ASN1_OBJECT(ASN1_OBJECT *a, unsigned char **pp)

p = *pp;
ASN1_put_object(&p, 0, a->length, V_ASN1_OBJECT, V_ASN1_UNIVERSAL);
memcpy(p, a->data, a->length);
OPENSSL_memcpy(p, a->data, a->length);
p += a->length;

*pp = p;
Expand Down Expand Up @@ -321,7 +324,7 @@ ASN1_OBJECT *c2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp,
}
ret->flags |= ASN1_OBJECT_FLAG_DYNAMIC_DATA;
}
memcpy(data, p, length);
OPENSSL_memcpy(data, p, length);
/* reattach data to object, after which it remains const */
ret->data = data;
ret->length = length;
Expand Down
2 changes: 1 addition & 1 deletion crypto/asn1/a_utctm.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ time_t ASN1_UTCTIME_get(const ASN1_UTCTIME *s)
struct tm tm;
int offset;

memset(&tm, '\0', sizeof tm);
OPENSSL_memset(&tm, '\0', sizeof tm);

# define g2(p) (((p)[0]-'0')*10+(p)[1]-'0')
tm.tm_year = g2(s->data);
Expand Down
7 changes: 5 additions & 2 deletions crypto/asn1/asn1_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@
#include <openssl/err.h>
#include <openssl/mem.h>

#include "../internal.h"


/* Cross-module errors from crypto/x509/i2d_pr.c. */
OPENSSL_DECLARE_ERROR_REASON(ASN1, UNSUPPORTED_PUBLIC_KEY_TYPE)

Expand Down Expand Up @@ -401,7 +404,7 @@ int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len)
}
str->length = len;
if (data != NULL) {
memcpy(str->data, data, len);
OPENSSL_memcpy(str->data, data, len);
/* an allowance for strings :-) */
str->data[len] = '\0';
}
Expand Down Expand Up @@ -452,7 +455,7 @@ int ASN1_STRING_cmp(const ASN1_STRING *a, const ASN1_STRING *b)

i = (a->length - b->length);
if (i == 0) {
i = memcmp(a->data, b->data, a->length);
i = OPENSSL_memcmp(a->data, b->data, a->length);
if (i == 0)
return (a->type - b->type);
else
Expand Down
2 changes: 1 addition & 1 deletion crypto/asn1/tasn_dec.c
Original file line number Diff line number Diff line change
Expand Up @@ -1108,7 +1108,7 @@ static int collect_data(BUF_MEM *buf, const unsigned char **p, long plen)
OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
return 0;
}
memcpy(buf->data + len, *p, plen);
OPENSSL_memcpy(buf->data + len, *p, plen);
}
*p += plen;
return 1;
Expand Down
9 changes: 6 additions & 3 deletions crypto/asn1/tasn_enc.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
#include <openssl/asn1t.h>
#include <openssl/mem.h>

#include "../internal.h"


static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out,
const ASN1_ITEM *it, int tag, int aclass);
static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out,
Expand Down Expand Up @@ -415,7 +418,7 @@ static int der_cmp(const void *a, const void *b)
const DER_ENC *d1 = a, *d2 = b;
int cmplen, i;
cmplen = (d1->length < d2->length) ? d1->length : d2->length;
i = memcmp(d1->data, d2->data, cmplen);
i = OPENSSL_memcmp(d1->data, d2->data, cmplen);
if (i)
return i;
return d1->length - d2->length;
Expand Down Expand Up @@ -470,7 +473,7 @@ static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out,
/* Output sorted DER encoding */
p = *out;
for (i = 0, tder = derlst; i < sk_ASN1_VALUE_num(sk); i++, tder++) {
memcpy(p, tder->data, tder->length);
OPENSSL_memcpy(p, tder->data, tder->length);
p += tder->length;
}
*out = p;
Expand Down Expand Up @@ -660,6 +663,6 @@ int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cout, int *putype,

}
if (cout && len)
memcpy(cout, cont, len);
OPENSSL_memcpy(cout, cont, len);
return len;
}
7 changes: 5 additions & 2 deletions crypto/asn1/tasn_new.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@
#include <openssl/mem.h>
#include <openssl/obj.h>

#include "../internal.h"


static int asn1_item_ex_combine_new(ASN1_VALUE **pval, const ASN1_ITEM *it,
int combine);
static void asn1_item_clear(ASN1_VALUE **pval, const ASN1_ITEM *it);
Expand Down Expand Up @@ -153,7 +156,7 @@ static int asn1_item_ex_combine_new(ASN1_VALUE **pval, const ASN1_ITEM *it,
*pval = OPENSSL_malloc(it->size);
if (!*pval)
goto memerr;
memset(*pval, 0, it->size);
OPENSSL_memset(*pval, 0, it->size);
}
asn1_set_choice_selector(pval, -1, it);
if (asn1_cb && !asn1_cb(ASN1_OP_NEW_POST, pval, it, NULL))
Expand All @@ -178,7 +181,7 @@ static int asn1_item_ex_combine_new(ASN1_VALUE **pval, const ASN1_ITEM *it,
*pval = OPENSSL_malloc(it->size);
if (!*pval)
goto memerr;
memset(*pval, 0, it->size);
OPENSSL_memset(*pval, 0, it->size);
asn1_refcount_set_one(pval, it);
asn1_enc_init(pval, it);
}
Expand Down
4 changes: 2 additions & 2 deletions crypto/asn1/tasn_utl.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ int asn1_enc_save(ASN1_VALUE **pval, const unsigned char *in, int inlen,
if (!enc->enc) {
return 0;
}
memcpy(enc->enc, in, inlen);
OPENSSL_memcpy(enc->enc, in, inlen);
}

enc->len = inlen;
Expand All @@ -195,7 +195,7 @@ int asn1_enc_restore(int *len, unsigned char **out, ASN1_VALUE **pval,
return 0;
}
if (out) {
memcpy(*out, enc->enc, enc->len);
OPENSSL_memcpy(*out, enc->enc, enc->len);
*out += enc->len;
}
if (len) {
Expand Down
Loading

0 comments on commit 17cf2cb

Please sign in to comment.