Fix UPER string decoding constrained only by lower bound > 0

This commit is contained in:
Lev Walkin 2017-10-01 17:04:48 -07:00
parent ce6b0a66de
commit 9d1b45f8ac
9 changed files with 129 additions and 104 deletions

View File

@ -10,6 +10,8 @@
(Severity: medium; Security impact: medium)
* Fix REAL type overwrite conversion memory leak.
(Severity: low; Security impact: medium)
* Fix UPER string decoding constrained only by lower bound > 0
(Severity: low; Security impact: none)
0.9.28: 2017-03-26
* PER decoding: avoid memory leak on error. By github.com/simo5

View File

@ -216,7 +216,7 @@ ANY_decode_uper(const asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
int ret;
/* Get the PER length */
raw_len = uper_get_length(pd, -1, &repeat);
raw_len = uper_get_length(pd, -1, 0, &repeat);
if(raw_len < 0) RETURN(RC_WMORE);
ASN_DEBUG("Got PER length len %zu, %s (%s)", raw_len,

View File

@ -656,7 +656,7 @@ INTEGER_decode_uper(const asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t
int ret = 0;
/* Get the PER length */
len = uper_get_length(pd, -1, &repeat);
len = uper_get_length(pd, -1, 0, &repeat);
if(len < 0) ASN__DECODE_STARVED;
p = REALLOC(st->buf, st->size + len + 1);

View File

@ -1424,7 +1424,6 @@ OCTET_STRING_decode_uper(const asn_codec_ctx_t *opt_codec_ctx,
if(inext < 0) RETURN(RC_WMORE);
if(inext) {
csiz = &asn_DEF_OCTET_STRING_constraints.size;
cval = &asn_DEF_OCTET_STRING_constraints.value;
unit_bits = canonical_unit_bits;
}
}
@ -1477,9 +1476,9 @@ OCTET_STRING_decode_uper(const asn_codec_ctx_t *opt_codec_ctx,
int ret;
/* Get the PER length */
raw_len = uper_get_length(pd, csiz->effective_bits, &repeat);
raw_len = uper_get_length(pd, csiz->effective_bits, csiz->lower_bound,
&repeat);
if(raw_len < 0) RETURN(RC_WMORE);
raw_len += csiz->lower_bound;
ASN_DEBUG("Got PER length eb %ld, len %ld, %s (%s)",
(long)csiz->effective_bits, (long)raw_len,
@ -1531,7 +1530,7 @@ OCTET_STRING_encode_uper(asn_TYPE_descriptor_t *td,
int inext = 0; /* Lies not within extension root */
unsigned int unit_bits;
unsigned int canonical_unit_bits;
unsigned int sizeinunits;
size_t size_in_units;
const uint8_t *buf;
int ret;
enum {
@ -1561,23 +1560,23 @@ OCTET_STRING_encode_uper(asn_TYPE_descriptor_t *td,
case ASN_OSUBV_BIT:
canonical_unit_bits = unit_bits = 1;
bpc = OS__BPC_BIT;
sizeinunits = st->size * 8 - (st->bits_unused & 0x07);
ASN_DEBUG("BIT STRING of %d bytes, %d bits unused",
sizeinunits, st->bits_unused);
size_in_units = st->size * 8 - (st->bits_unused & 0x07);
ASN_DEBUG("BIT STRING of %zu bytes, %d bits unused",
size_in_units, st->bits_unused);
break;
case ASN_OSUBV_STR:
canonical_unit_bits = unit_bits = 8;
if(cval->flags & APC_CONSTRAINED)
unit_bits = cval->range_bits;
bpc = OS__BPC_CHAR;
sizeinunits = st->size;
size_in_units = st->size;
break;
case ASN_OSUBV_U16:
canonical_unit_bits = unit_bits = 16;
if(cval->flags & APC_CONSTRAINED)
unit_bits = cval->range_bits;
bpc = OS__BPC_U16;
sizeinunits = st->size >> 1;
size_in_units = st->size >> 1;
if(st->size & 1) {
ASN_DEBUG("%s string size is not modulo 2", td->name);
ASN__ENCODE_FAILED;
@ -1588,7 +1587,7 @@ OCTET_STRING_encode_uper(asn_TYPE_descriptor_t *td,
if(cval->flags & APC_CONSTRAINED)
unit_bits = cval->range_bits;
bpc = OS__BPC_U32;
sizeinunits = st->size >> 2;
size_in_units = st->size >> 2;
if(st->size & 3) {
ASN_DEBUG("%s string size is not modulo 4", td->name);
ASN__ENCODE_FAILED;
@ -1596,92 +1595,85 @@ OCTET_STRING_encode_uper(asn_TYPE_descriptor_t *td,
break;
}
ASN_DEBUG("Encoding %s into %d units of %d bits"
ASN_DEBUG("Encoding %s into %zu units of %d bits"
" (%ld..%ld, effective %d)%s",
td->name, sizeinunits, unit_bits,
td->name, size_in_units, unit_bits,
csiz->lower_bound, csiz->upper_bound,
csiz->effective_bits, ct_extensible ? " EXT" : "");
/* Figure out whether size lies within PER visible constraint */
if(csiz->effective_bits >= 0) {
if((int)sizeinunits < csiz->lower_bound
|| (int)sizeinunits > csiz->upper_bound) {
if(ct_extensible) {
cval = &asn_DEF_OCTET_STRING_constraints.value;
csiz = &asn_DEF_OCTET_STRING_constraints.size;
unit_bits = canonical_unit_bits;
inext = 1;
} else {
ASN__ENCODE_FAILED;
}
}
} else {
inext = 0;
}
if(csiz->effective_bits >= 0) {
if((ssize_t)size_in_units < csiz->lower_bound
|| (ssize_t)size_in_units > csiz->upper_bound) {
if(ct_extensible) {
csiz = &asn_DEF_OCTET_STRING_constraints.size;
unit_bits = canonical_unit_bits;
inext = 1;
} else {
ASN__ENCODE_FAILED;
}
}
} else {
inext = 0;
}
if(ct_extensible) {
if(ct_extensible) {
/* Declare whether length is [not] within extension root */
if(per_put_few_bits(po, inext, 1))
ASN__ENCODE_FAILED;
}
/* X.691, #16.5: zero-length encoding */
/* X.691, #16.6: short fixed length encoding (up to 2 octets) */
/* X.691, #16.7: long fixed length encoding (up to 64K octets) */
if(csiz->effective_bits >= 0) {
ASN_DEBUG("Encoding %zu bytes (%ld), length in %d bits",
st->size, sizeinunits - csiz->lower_bound,
csiz->effective_bits);
ret = per_put_few_bits(po, sizeinunits - csiz->lower_bound,
csiz->effective_bits);
if(ret) ASN__ENCODE_FAILED;
if(bpc) {
ret = OCTET_STRING_per_put_characters(po, st->buf,
sizeinunits, bpc, unit_bits,
cval->lower_bound, cval->upper_bound, pc);
} else {
ret = per_put_many_bits(po, st->buf,
sizeinunits * unit_bits);
}
if(ret) ASN__ENCODE_FAILED;
ASN__ENCODED_OK(er);
}
if(csiz->effective_bits >= 0 && !inext) {
ASN_DEBUG("Encoding %zu bytes (%ld), length in %d bits", st->size,
size_in_units - csiz->lower_bound, csiz->effective_bits);
ret = per_put_few_bits(po, size_in_units - csiz->lower_bound,
csiz->effective_bits);
if(ret) ASN__ENCODE_FAILED;
if(bpc) {
ret = OCTET_STRING_per_put_characters(
po, st->buf, size_in_units, bpc, unit_bits, cval->lower_bound,
cval->upper_bound, pc);
} else {
assert(unit_bits == 1);
ret = per_put_many_bits(po, st->buf, size_in_units);
}
if(ret) ASN__ENCODE_FAILED;
ASN__ENCODED_OK(er);
}
ASN_DEBUG("Encoding %zu bytes", st->size);
ASN_DEBUG("Encoding %zu bytes", st->size);
if(sizeinunits == 0) {
if(uper_put_length(po, 0))
ASN__ENCODE_FAILED;
ASN__ENCODED_OK(er);
}
if(size_in_units == 0) {
if(uper_put_length(po, 0)) ASN__ENCODE_FAILED;
ASN__ENCODED_OK(er);
}
buf = st->buf;
while(sizeinunits) {
ssize_t maySave = uper_put_length(po, sizeinunits);
if(maySave < 0) ASN__ENCODE_FAILED;
buf = st->buf;
while(size_in_units) {
ssize_t maySave = uper_put_length(po, size_in_units);
if(maySave < 0) ASN__ENCODE_FAILED;
ASN_DEBUG("Encoding %ld of %ld",
(long)maySave, (long)sizeinunits);
ASN_DEBUG("Encoding %ld of %ld", (long)maySave, (long)sizeinunits);
if(bpc) {
ret = OCTET_STRING_per_put_characters(po, buf,
maySave, bpc, unit_bits,
cval->lower_bound, cval->upper_bound, pc);
} else {
ret = per_put_many_bits(po, buf, maySave * unit_bits);
}
if(ret) ASN__ENCODE_FAILED;
if(bpc) {
ret = OCTET_STRING_per_put_characters(po, buf, maySave, bpc,
unit_bits, cval->lower_bound,
cval->upper_bound, pc);
} else {
ret = per_put_many_bits(po, buf, maySave);
}
if(ret) ASN__ENCODE_FAILED;
if(bpc)
buf += maySave * bpc;
else
buf += maySave >> 3;
sizeinunits -= maySave;
assert(!(maySave & 0x07) || !sizeinunits);
}
if(bpc)
buf += maySave * bpc;
else
buf += maySave >> 3;
size_in_units -= maySave;
assert(!(maySave & 0x07) || !size_in_units);
}
ASN__ENCODED_OK(er);
ASN__ENCODED_OK(er);
}
#endif /* ASN_DISABLE_PER_SUPPORT */
@ -1987,10 +1979,36 @@ OCTET_STRING_random_fill(const asn_TYPE_descriptor_t *td, void **sptr,
const asn_per_constraint_t *pc =
&td->encoding_constraints.per_constraints->size;
if(pc->flags & APC_CONSTRAINED) {
long suggested_upper_bound =
pc->upper_bound < max_length ? pc->upper_bound : max_length;
if(max_length < (size_t)pc->lower_bound) {
return result_skipped;
}
rnd_len = asn_random_between(pc->lower_bound, pc->upper_bound);
if(pc->flags & APC_EXTENSIBLE) {
switch(asn_random_between(0, 5)) {
case 0:
if(pc->lower_bound > 0) {
rnd_len = pc->lower_bound - 1;
break;
}
/* Fall through */
case 1:
rnd_len = pc->upper_bound + 1;
break;
case 2:
/* Keep rnd_len from the table */
if(rnd_len < max_length) {
break;
}
/* Fall through */
default:
rnd_len = asn_random_between(pc->lower_bound,
suggested_upper_bound);
}
} else {
rnd_len =
asn_random_between(pc->lower_bound, suggested_upper_bound);
}
} else {
rnd_len = asn_random_between(0, max_length - 1);
}

View File

@ -910,8 +910,7 @@ SET_OF_decode_uper(const asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *
do {
int i;
if(nelems < 0) {
nelems = uper_get_length(pd,
ct ? ct->effective_bits : -1, &repeat);
nelems = uper_get_length(pd, -1, 0, &repeat);
ASN_DEBUG("Got to decode %d elements (eff %d)",
(int)nelems, (int)(ct ? ct->effective_bits : -1));
if(nelems < 0) ASN__DECODE_STARVED;

View File

@ -73,10 +73,10 @@ oer_decoder.h oer_decoder.c OPEN_TYPE.h # OER decoding support
oer_encoder.h oer_encoder.c # OER encoding support
oer_support.h oer_support.c # OER support
OPEN_TYPE.h OPEN_TYPE_oer.c constr_CHOICE.h
INTEGER_oer.c
INTEGER_oer.c INTEGER.h
OCTET_STRING_oer.c
NativeInteger_oer.c
NativeEnumerated_oer.c
NativeInteger_oer.c NativeInteger.h
NativeEnumerated_oer.c NativeEnumerated.h
constr_SEQUENCE_oer.c constr_SEQUENCE.h
constr_CHOICE_oer.c
constr_SET_OF_oer.c constr_SET_OF.h asn_SET_OF.h asn_SET_OF.c

View File

@ -73,7 +73,7 @@ uper_open_type_get_simple(const asn_codec_ctx_t *ctx, asn_TYPE_descriptor_t *td,
ASN_DEBUG("Getting open type %s...", td->name);
do {
chunk_bytes = uper_get_length(pd, -1, &repeat);
chunk_bytes = uper_get_length(pd, -1, 0, &repeat);
if(chunk_bytes < 0) {
FREEMEM(buf);
ASN__DECODE_STARVED;
@ -329,7 +329,7 @@ uper_ugot_refill(asn_per_data_t *pd) {
return -1;
}
next_chunk_bytes = uper_get_length(oldpd, -1, &arg->repeat);
next_chunk_bytes = uper_get_length(oldpd, -1, 0, &arg->repeat);
ASN_DEBUG("Open type LENGTH %ld bytes at off %ld, repeat %ld",
(long)next_chunk_bytes, (long)oldpd->moved, (long)arg->repeat);
if(next_chunk_bytes < 0) return -1;

View File

@ -12,14 +12,17 @@
* Get the optionally constrained length "n" from the stream.
*/
ssize_t
uper_get_length(asn_per_data_t *pd, int ebits, int *repeat) {
uper_get_length(asn_per_data_t *pd, int ebits, size_t lower_bound,
int *repeat) {
ssize_t value;
*repeat = 0;
/* #11.9.4.1 Encoding if constrained (according to effective bits) */
if(ebits >= 0 && ebits <= 16) {
return per_get_few_bits(pd, ebits);
value = per_get_few_bits(pd, ebits);
if(value >= 0) value += lower_bound;
return value;
}
value = per_get_few_bits(pd, 8);
@ -58,7 +61,7 @@ uper_get_nslength(asn_per_data_t *pd) {
return length;
} else {
int repeat;
length = uper_get_length(pd, -1, &repeat);
length = uper_get_length(pd, -1, 0, &repeat);
if(length >= 0 && !repeat) return length;
return -1; /* Error, or do not support >16K extensions */
}
@ -170,23 +173,25 @@ int uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v, int
}
/*
* X.691 (08/2015) #11.9 "General rules for encoding a length determinant"
* Put the length "n" (or part of it) into the stream.
*/
ssize_t
uper_put_length(asn_per_outp_t *po, size_t length) {
if(length <= 127) /* #10.9.3.6 */
return per_put_few_bits(po, length, 8)
? -1 : (ssize_t)length;
else if(length < 16384) /* #10.9.3.7 */
return per_put_few_bits(po, length|0x8000, 16)
? -1 : (ssize_t)length;
if(length <= 127) /* #11.9.3.6 */
return per_put_few_bits(po, length, 8)
? -1 : (ssize_t)length;
else if(length < 16384) /* #10.9.3.7 */
return per_put_few_bits(po, length|0x8000, 16)
? -1 : (ssize_t)length;
length >>= 14;
if(length > 4) length = 4;
length >>= 14;
if(length > 4) length = 4;
return per_put_few_bits(po, 0xC0 | length, 8)
? -1 : (ssize_t)(length << 14);
return per_put_few_bits(po, 0xC0 | length, 8)
? -1 : (ssize_t)(length << 14);
}
@ -199,7 +204,7 @@ int
uper_put_nslength(asn_per_outp_t *po, size_t length) {
if(length <= 64) {
/* #10.9.3.4 */
/* #11.9.3.4 */
if(length == 0) return -1;
return per_put_few_bits(po, length-1, 7) ? -1 : 0;
} else {

View File

@ -43,11 +43,11 @@ typedef struct asn_bit_data_s asn_per_data_t;
asn_get_many_bits(data, dst, align, bits)
/*
* X.691 (08/2015) #11.9 "General rules for encoding a length determinant"
* Get the length "n" from the Unaligned PER stream.
*/
ssize_t uper_get_length(asn_per_data_t *pd,
int effective_bound_bits,
int *repeat);
ssize_t uper_get_length(asn_per_data_t *pd, int effective_bound_bits,
size_t lower_bound, int *repeat);
/*
* Get the normally small length "n".
@ -74,6 +74,7 @@ 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);
/*
* X.691 (08/2015) #11.9 "General rules for encoding a length determinant"
* Put the length "n" to the Unaligned PER stream.
* This function returns the number of units which may be flushed
* in the next units saving iteration.