-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid implementation-defined and undefined behavior when dealing with sizes #578
Changes from all commits
3cb057f
01ee1b3
ec8f20b
14c7dbd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,7 +32,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ | |
lenbyte = input[pos++]; | ||
if (lenbyte & 0x80) { | ||
lenbyte -= 0x80; | ||
if (pos + lenbyte > inputlen) { | ||
if (lenbyte > inputlen - pos) { | ||
return 0; | ||
} | ||
pos += lenbyte; | ||
|
@@ -51,7 +51,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ | |
lenbyte = input[pos++]; | ||
if (lenbyte & 0x80) { | ||
lenbyte -= 0x80; | ||
if (pos + lenbyte > inputlen) { | ||
if (lenbyte > inputlen - pos) { | ||
return 0; | ||
} | ||
while (lenbyte > 0 && input[pos] == 0) { | ||
|
@@ -89,7 +89,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ | |
lenbyte = input[pos++]; | ||
if (lenbyte & 0x80) { | ||
lenbyte -= 0x80; | ||
if (pos + lenbyte > inputlen) { | ||
if (lenbyte > inputlen - pos) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here too |
||
return 0; | ||
} | ||
while (lenbyte > 0 && input[pos] == 0) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,68 +46,73 @@ static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CON | |
0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL | ||
); | ||
|
||
static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned char *sigend) { | ||
int lenleft, b1; | ||
size_t ret = 0; | ||
static int secp256k1_der_read_len(size_t *len, const unsigned char **sigp, const unsigned char *sigend) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need a not-NULL check/assertion for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sounds like a good idea There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. n.b. This returns via output pointer instead of return value, to prevent accidentally casting to int (or other wrong types) implicitly. |
||
size_t lenleft; | ||
unsigned char b1; | ||
VERIFY_CHECK(len != NULL); | ||
*len = 0; | ||
if (*sigp >= sigend) { | ||
return -1; | ||
return 0; | ||
} | ||
b1 = *((*sigp)++); | ||
if (b1 == 0xFF) { | ||
/* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */ | ||
return -1; | ||
return 0; | ||
} | ||
if ((b1 & 0x80) == 0) { | ||
/* X.690-0207 8.1.3.4 short form length octets */ | ||
return b1; | ||
*len = b1; | ||
return 1; | ||
} | ||
if (b1 == 0x80) { | ||
/* Indefinite length is not allowed in DER. */ | ||
return -1; | ||
return 0; | ||
} | ||
/* X.690-207 8.1.3.5 long form length octets */ | ||
lenleft = b1 & 0x7F; | ||
if (lenleft > sigend - *sigp) { | ||
return -1; | ||
lenleft = b1 & 0x7F; /* lenleft is at least 1 */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ACK, |
||
if (lenleft > (size_t)(sigend - *sigp)) { | ||
return 0; | ||
} | ||
if (**sigp == 0) { | ||
/* Not the shortest possible length encoding. */ | ||
return -1; | ||
return 0; | ||
} | ||
if ((size_t)lenleft > sizeof(size_t)) { | ||
if (lenleft > sizeof(size_t)) { | ||
/* The resulting length would exceed the range of a size_t, so | ||
* certainly longer than the passed array size. | ||
*/ | ||
return -1; | ||
return 0; | ||
} | ||
while (lenleft > 0) { | ||
ret = (ret << 8) | **sigp; | ||
if (ret + lenleft > (size_t)(sigend - *sigp)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks like it was wrong. If the remaining digits are all zero, the final size is just If my understanding is correct, is there a possibility fixing this bug could create a a consensus incompatibility? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [edit: Deleted what I wrote here because it was wrong. But did you see the commit message? I think that explains the change. If not, I don't understand your sentence about the remaining digits] It should be noted that none of this is code is consensus-critical (for Bitcoin) because Bitcoin relies on lax DER parsing function (as implemented in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ah I think now I know what you are saying. It took me also a while to understand that code. The purpose of |
||
/* Result exceeds the length of the passed array. */ | ||
return -1; | ||
} | ||
*len = (*len << 8) | **sigp; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No initial value is set for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch |
||
(*sigp)++; | ||
lenleft--; | ||
} | ||
if (ret < 128) { | ||
if (*len > (size_t)(sigend - *sigp)) { | ||
/* Result exceeds the length of the passed array. */ | ||
return 0; | ||
} | ||
if (*len < 128) { | ||
/* Not the shortest possible length encoding. */ | ||
return -1; | ||
return 0; | ||
} | ||
return ret; | ||
return 1; | ||
} | ||
|
||
static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) { | ||
int overflow = 0; | ||
unsigned char ra[32] = {0}; | ||
int rlen; | ||
size_t rlen; | ||
|
||
if (*sig == sigend || **sig != 0x02) { | ||
/* Not a primitive integer (X.690-0207 8.3.1). */ | ||
return 0; | ||
} | ||
(*sig)++; | ||
rlen = secp256k1_der_read_len(sig, sigend); | ||
if (rlen <= 0 || (*sig) + rlen > sigend) { | ||
if (secp256k1_der_read_len(&rlen, sig, sigend) == 0) { | ||
return 0; | ||
} | ||
if (rlen == 0 || *sig + rlen > sigend) { | ||
/* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */ | ||
return 0; | ||
} | ||
|
@@ -123,8 +128,11 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char | |
/* Negative. */ | ||
overflow = 1; | ||
} | ||
while (rlen > 0 && **sig == 0) { | ||
/* Skip leading zero bytes */ | ||
/* There is at most one leading zero byte: | ||
* if there were two leading zero bytes, we would have failed and returned 0 | ||
* because of excessive 0x00 padding already. */ | ||
if (rlen > 0 && **sig == 0) { | ||
/* Skip leading zero byte */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. utACK |
||
rlen--; | ||
(*sig)++; | ||
} | ||
|
@@ -144,18 +152,16 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char | |
|
||
static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) { | ||
const unsigned char *sigend = sig + size; | ||
int rlen; | ||
size_t rlen; | ||
if (sig == sigend || *(sig++) != 0x30) { | ||
/* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */ | ||
return 0; | ||
} | ||
rlen = secp256k1_der_read_len(&sig, sigend); | ||
if (rlen < 0 || sig + rlen > sigend) { | ||
/* Tuple exceeds bounds */ | ||
if (secp256k1_der_read_len(&rlen, &sig, sigend) == 0) { | ||
return 0; | ||
} | ||
if (sig + rlen != sigend) { | ||
/* Garbage after tuple. */ | ||
if (rlen != (size_t)(sigend - sig)) { | ||
/* Tuple exceeds bounds or garage after tuple. */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. utACK There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I admit that "garage after tuple" seems to be an unrealistic concern. 🔧🚗 |
||
return 0; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -131,7 +131,8 @@ static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) { | |
static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t len) { | ||
size_t bufsize = hash->bytes & 0x3F; | ||
hash->bytes += len; | ||
while (bufsize + len >= 64) { | ||
VERIFY_CHECK(hash->bytes >= len); | ||
while (len >= 64 - bufsize) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. utACK |
||
/* Fill the buffer, and process it. */ | ||
size_t chunk_len = 64 - bufsize; | ||
memcpy(((unsigned char*)hash->buf) + bufsize, data, chunk_len); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pos
could possibly be larger thaninputlen
here, since it was just post-incremented on line 51 without a check.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right before line 51, we have
pos < inputlen
since we havepos != input
due to the check in line 48.So after the increment in line 51, we have
pos <= inputlen