Commit cc6a76b2 authored by Lev Walkin's avatar Lev Walkin

Undefined Behavior sanitizer fixes

parent fa9a326d
...@@ -651,9 +651,11 @@ INTEGER_decode_uper(const asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t ...@@ -651,9 +651,11 @@ INTEGER_decode_uper(const asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t
ASN__DECODE_STARVED; ASN__DECODE_STARVED;
ASN_DEBUG("Got value %lu + low %ld", ASN_DEBUG("Got value %lu + low %ld",
uvalue, ct->lower_bound); uvalue, ct->lower_bound);
svalue = ct->lower_bound + (long)uvalue; if(per_long_range_unrebase(uvalue, ct->lower_bound,
if(asn_long2INTEGER(st, svalue)) ct->upper_bound, &svalue)
|| asn_long2INTEGER(st, svalue)) {
ASN__DECODE_FAILED; ASN__DECODE_FAILED;
}
} }
return rval; return rval;
} }
...@@ -708,7 +710,6 @@ INTEGER_encode_uper(asn_TYPE_descriptor_t *td, ...@@ -708,7 +710,6 @@ INTEGER_encode_uper(asn_TYPE_descriptor_t *td,
const uint8_t *end; const uint8_t *end;
const asn_per_constraint_t *ct; const asn_per_constraint_t *ct;
long value = 0; long value = 0;
unsigned long v = 0;
if(!st || st->size == 0) ASN__ENCODE_FAILED; if(!st || st->size == 0) ASN__ENCODE_FAILED;
...@@ -766,10 +767,13 @@ INTEGER_encode_uper(asn_TYPE_descriptor_t *td, ...@@ -766,10 +767,13 @@ INTEGER_encode_uper(asn_TYPE_descriptor_t *td,
/* X.691-11/2008, #13.2.2, test if constrained whole number */ /* X.691-11/2008, #13.2.2, test if constrained whole number */
if(ct && ct->range_bits >= 0) { if(ct && ct->range_bits >= 0) {
unsigned long v;
/* #11.5.6 -> #11.3 */ /* #11.5.6 -> #11.3 */
ASN_DEBUG("Encoding integer %ld (%lu) with range %d bits", ASN_DEBUG("Encoding integer %ld (%lu) with range %d bits",
value, value - ct->lower_bound, ct->range_bits); value, value - ct->lower_bound, ct->range_bits);
v = value - ct->lower_bound; if(per_long_range_rebase(value, ct->lower_bound, ct->upper_bound, &v)) {
ASN__ENCODE_FAILED;
}
if(uper_put_constrained_whole_number_u(po, v, ct->range_bits)) if(uper_put_constrained_whole_number_u(po, v, ct->range_bits))
ASN__ENCODE_FAILED; ASN__ENCODE_FAILED;
ASN__ENCODED_OK(er); ASN__ENCODED_OK(er);
......
...@@ -147,21 +147,9 @@ int uper_get_constrained_whole_number(asn_per_data_t *pd, unsigned long *out_val ...@@ -147,21 +147,9 @@ int uper_get_constrained_whole_number(asn_per_data_t *pd, unsigned long *out_val
/* X.691-2008/11, #11.5.6 -> #11.3 */ /* X.691-2008/11, #11.5.6 -> #11.3 */
int uper_put_constrained_whole_number_s(asn_per_outp_t *po, long v, int nbits) { int
/* uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v,
* Assume signed number can be safely coerced into int nbits) {
* unsigned of the same range.
* The following testing code will likely be optimized out
* by compiler if it is true.
*/
unsigned long uvalue1 = ULONG_MAX;
long svalue = uvalue1;
unsigned long uvalue2 = svalue;
assert(uvalue1 == uvalue2);
return uper_put_constrained_whole_number_u(po, v, nbits);
}
int uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v, int nbits) {
if(nbits <= 31) { if(nbits <= 31) {
return per_put_few_bits(po, v, nbits); return per_put_few_bits(po, v, nbits);
} else { } else {
...@@ -227,3 +215,81 @@ uper_put_nslength(asn_per_outp_t *po, size_t length) { ...@@ -227,3 +215,81 @@ uper_put_nslength(asn_per_outp_t *po, size_t length) {
return 0; return 0;
} }
static int
per__long_range(long lb, long ub, unsigned long *range_r) {
unsigned long bounds_range;
if((ub < 0) == (lb < 0)) {
bounds_range = ub - lb;
} else if(lb < 0) {
assert(ub >= 0);
bounds_range = 1 + ((unsigned long)ub + (unsigned long)-(lb + 1));
} else {
assert(!"Unreachable");
return -1;
}
*range_r = bounds_range;
return 0;
}
int
per_long_range_rebase(long v, long lb, long ub, unsigned long *output) {
unsigned long range;
assert(lb <= ub);
if(v < lb || v > ub || per__long_range(lb, ub, &range) < 0) {
/* Range error. */
return -1;
}
/*
* Fundamentally what we're doing is returning (v-lb).
* However, this triggers undefined behavior when the word width
* of signed (v) is the same as the size of unsigned (*output).
* In practice, it triggers the UndefinedSanitizer. Therefore we shall
* compute the ranges accurately to avoid C's undefined behavior.
*/
if((v < 0) == (lb < 0)) {
*output = v-lb;
return 0;
} else if(v < 0) {
unsigned long rebased = 1 + (unsigned long)-(v+1) + (unsigned long)lb;
assert(rebased <= range); /* By construction */
*output = rebased;
return 0;
} else if(lb < 0) {
unsigned long rebased = 1 + (unsigned long)-(lb+1) + (unsigned long)v;
assert(rebased <= range); /* By construction */
*output = rebased;
return 0;
} else {
assert(!"Unreachable");
return -1;
}
}
int
per_long_range_unrebase(unsigned long inp, long lb, long ub, long *outp) {
unsigned long range;
if(per__long_range(lb, ub, &range) != 0) {
return -1;
}
if(inp > range) {
/*
* We can encode something in the given number of bits that technically
* exceeds the range. This is an avenue for security errors,
* so we don't allow that.
*/
return -1;
}
if(inp <= LONG_MAX) {
*outp = (long)inp + lb;
} else {
*outp = (lb + LONG_MAX + 1) + (long)((inp - LONG_MAX) - 1);
}
return 0;
}
...@@ -69,8 +69,19 @@ typedef struct asn_bit_outp_s asn_per_outp_t; ...@@ -69,8 +69,19 @@ typedef struct asn_bit_outp_s asn_per_outp_t;
#define per_put_many_bits(out, src, nbits) asn_put_many_bits(out, src, nbits) #define per_put_many_bits(out, src, nbits) asn_put_many_bits(out, src, nbits)
#define per_put_aligned_flush(out) asn_put_aligned_flush(out) #define per_put_aligned_flush(out) asn_put_aligned_flush(out)
/*
* Rebase the given value as an offset into the range specified by the
* lower bound (lb) and upper bound (ub).
* RETURN VALUES:
* -1: Conversion failed due to range problems.
* 0: Conversion was successful.
*/
int per_long_range_rebase(long v, long lb, long ub, unsigned long *output);
/* The inverse operation: restores the value by the offset and its bounds. */
int per_long_range_unrebase(unsigned long inp, long lb, long ub, long *outp);
/* X.691-2008/11, #11.5 */ /* X.691-2008/11, #11.5 */
int uper_put_constrained_whole_number_s(asn_per_outp_t *po, long v, int nbits);
int uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v, int nbits); int uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v, int nbits);
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment