mirror of https://gerrit.osmocom.org/asn1c
invalid memory reference fix and test case
git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@56 59561ff5-6e30-0410-9f3c-9617f08c8826
This commit is contained in:
parent
7d278c491d
commit
796c1da2cb
14
ChangeLog
14
ChangeLog
|
@ -1,8 +1,20 @@
|
||||||
|
|
||||||
0.8.15: 2004-Jul-15
|
0.8.15: 2004-Jul-20
|
||||||
|
|
||||||
* Fixed parser: memory leak in free_struct code for SET OF/SEQUENCE OF.
|
* Fixed parser: memory leak in free_struct code for SET OF/SEQUENCE OF.
|
||||||
(Severity: high, Security impact: medium)
|
(Severity: high, Security impact: medium)
|
||||||
|
* Fixed parser: invalid memory reference in CHECK.
|
||||||
|
(Test case 48) (Severity: high, Security impact: medium)
|
||||||
|
When encoding data for certain ASN.1 specifications containing
|
||||||
|
explicit tags, the tag is always written incorrectly due to
|
||||||
|
incorrect memory reference. The encoding will almost always produce
|
||||||
|
unparseable data and might well reference unmapped region so program
|
||||||
|
would produce segmentation violation. Fortunately, memory is
|
||||||
|
read, not written, so remote exploits cannot execute arbitrary
|
||||||
|
code and triggering unmapped memory reference is highly unlikely
|
||||||
|
even it attacker knows the code (basically, the compiler should place
|
||||||
|
asn1_DEF_... right before the end of the mapped memory region, which
|
||||||
|
is extremely rare).
|
||||||
* Improved INTEGER type printing.
|
* Improved INTEGER type printing.
|
||||||
|
|
||||||
0.8.14: 2004-Jun-30
|
0.8.14: 2004-Jun-30
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
#undef NDEBUG
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <UserIdentifier.h>
|
||||||
|
|
||||||
|
static unsigned char buf[4096];
|
||||||
|
static int buf_offset;
|
||||||
|
|
||||||
|
static int
|
||||||
|
_buf_writer(const void *buffer, size_t size, void *app_key) {
|
||||||
|
unsigned char *b, *bend;
|
||||||
|
(void)app_key;
|
||||||
|
assert(buf_offset + size < sizeof(buf));
|
||||||
|
memcpy(buf + buf_offset, buffer, size);
|
||||||
|
b = buf + buf_offset;
|
||||||
|
bend = b + size;
|
||||||
|
printf("=> [");
|
||||||
|
for(; b < bend; b++)
|
||||||
|
printf(" %02X", *b);
|
||||||
|
printf("]:%ld\n", (long)size);
|
||||||
|
buf_offset += size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
save_object(void *bs, asn1_TYPE_descriptor_t *td) {
|
||||||
|
der_enc_rval_t rval; /* Return value */
|
||||||
|
int i;
|
||||||
|
|
||||||
|
rval = der_encode(td, bs, _buf_writer, 0);
|
||||||
|
if (rval.encoded == -1) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Cannot encode %s: %s\n",
|
||||||
|
rval.failed_type->name, strerror(errno));
|
||||||
|
assert(rval.encoded != -1);
|
||||||
|
return -1; /* JIC */
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[buf_offset++] = 123; /* Finalize with garbage */
|
||||||
|
|
||||||
|
asn_fprint(stderr, td, bs);
|
||||||
|
|
||||||
|
printf("OUT: [");
|
||||||
|
for(i = 0; i < buf_offset; i++)
|
||||||
|
printf(" %02x", buf[i]);
|
||||||
|
printf("]\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
load_object(void *bs, asn1_TYPE_descriptor_t *td) {
|
||||||
|
ber_dec_rval_t rval;
|
||||||
|
|
||||||
|
fprintf(stderr, "\nLOADING OBJECT OF SIZE %d\n", buf_offset);
|
||||||
|
|
||||||
|
rval = ber_decode(td, (void **)&bs, buf, buf_offset);
|
||||||
|
assert(rval.code == RC_OK);
|
||||||
|
|
||||||
|
asn_fprint(stderr, td, bs);
|
||||||
|
|
||||||
|
return (rval.code == RC_OK)?0:-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main() {
|
||||||
|
asn1_TYPE_descriptor_t *td = &asn1_DEF_UserIdentifier;
|
||||||
|
UserIdentifier_t user;
|
||||||
|
UserIdentifier_t user_new;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memset(&user, 0, sizeof user);
|
||||||
|
memset(&user_new, 0, sizeof user_new);
|
||||||
|
|
||||||
|
user.present = UserIdentifier_PR_phoneNumber;
|
||||||
|
OCTET_STRING_fromBuf(
|
||||||
|
&user.choice.phoneNumber,
|
||||||
|
"0123456789", -1);
|
||||||
|
|
||||||
|
/* Save->Load must succeed */
|
||||||
|
save_object(&user, td);
|
||||||
|
ret = load_object(&user_new, td);
|
||||||
|
|
||||||
|
assert(user_new.present == UserIdentifier_PR_phoneNumber);
|
||||||
|
|
||||||
|
assert(ret == 0);
|
||||||
|
|
||||||
|
printf("OK\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -39,7 +39,7 @@ ber_decode(asn1_TYPE_descriptor_t *type_descriptor,
|
||||||
* Check the set of <TL<TL<TL...>>> tags matches the definition.
|
* Check the set of <TL<TL<TL...>>> tags matches the definition.
|
||||||
*/
|
*/
|
||||||
ber_dec_rval_t
|
ber_dec_rval_t
|
||||||
ber_check_tags(asn1_TYPE_descriptor_t *head, ber_dec_ctx_t *ctx,
|
ber_check_tags(asn1_TYPE_descriptor_t *td, ber_dec_ctx_t *ctx,
|
||||||
void *ptr, size_t size, int tag_mode,
|
void *ptr, size_t size, int tag_mode,
|
||||||
ber_tlv_len_t *last_length, int *opt_tlv_form) {
|
ber_tlv_len_t *last_length, int *opt_tlv_form) {
|
||||||
ssize_t consumed_myself = 0;
|
ssize_t consumed_myself = 0;
|
||||||
|
@ -83,28 +83,33 @@ ber_check_tags(asn1_TYPE_descriptor_t *head, ber_dec_ctx_t *ctx,
|
||||||
* This is because the implicit tag at above structure may replace
|
* This is because the implicit tag at above structure may replace
|
||||||
* zero or more (or every) tags which follow it. We don't care
|
* zero or more (or every) tags which follow it. We don't care
|
||||||
* about the precise number, as it is already computed for us
|
* about the precise number, as it is already computed for us
|
||||||
* by the ASN.1 compiler and placed into head->tags_impl_skip.
|
* by the ASN.1 compiler and placed into td->tags_impl_skip.
|
||||||
* So let's suppose the only tag left after implicit tagging is {I}.
|
* So let's suppose the only tag left after implicit tagging is {I}.
|
||||||
* Yet, the table we have is {A,B,C} and head->tags_impl_skip=3.
|
* Yet, the table we have is {A,B,C} and td->tags_impl_skip=3.
|
||||||
* We need to check at least one tag in the loop, so the loop range
|
* We need to check at least one tag in the loop, so the loop range
|
||||||
* is modified so it will be invoked at least one time.
|
* is modified so it will be invoked at least one time.
|
||||||
*/
|
*/
|
||||||
tagno = ctx->step /* Continuing where left previously */
|
tagno = ctx->step /* Continuing where left previously */
|
||||||
+ (tag_mode==-1?(head->tags_impl_skip-1):0)
|
+ (tag_mode==-1?(td->tags_impl_skip-1):0)
|
||||||
+ (tag_mode==1?-1:0)
|
+ (tag_mode==1?-1:0)
|
||||||
;
|
;
|
||||||
//assert(head->tags_count >= 1); ?May not be the case for CHOICE!
|
ASN_DEBUG("ber_check_tags(%s, size=%ld, tm=%d, step=%d, tagno=%d)",
|
||||||
assert(tagno < head->tags_count); /* At least one loop */
|
td->name, (long)size, tag_mode, ctx->step, tagno);
|
||||||
for((void)tagno; tagno < head->tags_count; tagno++, ctx->step++) {
|
//assert(td->tags_count >= 1); ?May not be the case for CHOICE!
|
||||||
|
assert(tagno < td->tags_count); /* At least one loop */
|
||||||
|
for((void)tagno; tagno < td->tags_count; tagno++, ctx->step++) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fetch and process T from TLV.
|
* Fetch and process T from TLV.
|
||||||
*/
|
*/
|
||||||
tag_len = ber_fetch_tag(ptr, size, &tlv_tag);
|
tag_len = ber_fetch_tag(ptr, size, &tlv_tag);
|
||||||
ASN_DEBUG("Fetching tag from {%p,%ld} %02X: "
|
ASN_DEBUG("Fetching tag from {%p,%ld} %02X..%02X: "
|
||||||
"len %ld, tag %s",
|
"len %ld, tag %s",
|
||||||
ptr, (long)size,
|
ptr, (long)size,
|
||||||
*(uint8_t *)ptr, (long)tag_len,
|
size?*(uint8_t *)ptr:0,
|
||||||
|
(tag_len<size&&tag_len>0)
|
||||||
|
?*((uint8_t *)ptr + tag_len):0,
|
||||||
|
(long)tag_len,
|
||||||
ber_tlv_tag_string(tlv_tag));
|
ber_tlv_tag_string(tlv_tag));
|
||||||
switch(tag_len) {
|
switch(tag_len) {
|
||||||
case -1: RETURN(RC_FAIL);
|
case -1: RETURN(RC_FAIL);
|
||||||
|
@ -125,12 +130,12 @@ ber_check_tags(asn1_TYPE_descriptor_t *head, ber_dec_ctx_t *ctx,
|
||||||
*/
|
*/
|
||||||
} else {
|
} else {
|
||||||
assert(tagno >= 0); /* Guaranteed by the code above */
|
assert(tagno >= 0); /* Guaranteed by the code above */
|
||||||
if(tlv_tag != head->tags[tagno]) {
|
if(tlv_tag != td->tags[tagno]) {
|
||||||
/*
|
/*
|
||||||
* Unexpected tag. Too bad.
|
* Unexpected tag. Too bad.
|
||||||
*/
|
*/
|
||||||
ASN_DEBUG("Expected: %s, expectation failed",
|
ASN_DEBUG("Expected: %s, expectation failed",
|
||||||
ber_tlv_tag_string(head->tags[tagno]));
|
ber_tlv_tag_string(td->tags[tagno]));
|
||||||
RETURN(RC_FAIL);
|
RETURN(RC_FAIL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,13 +147,13 @@ ber_check_tags(asn1_TYPE_descriptor_t *head, ber_dec_ctx_t *ctx,
|
||||||
* If this one is the last one, check that the tag form
|
* If this one is the last one, check that the tag form
|
||||||
* matches the one given in descriptor.
|
* matches the one given in descriptor.
|
||||||
*/
|
*/
|
||||||
if(tagno < (head->tags_count - 1)) {
|
if(tagno < (td->tags_count - 1)) {
|
||||||
if(tlv_constr == 0) {
|
if(tlv_constr == 0) {
|
||||||
RETURN(RC_FAIL);
|
RETURN(RC_FAIL);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(head->last_tag_form != tlv_constr
|
if(td->last_tag_form != tlv_constr
|
||||||
&& head->last_tag_form != -1) {
|
&& td->last_tag_form != -1) {
|
||||||
RETURN(RC_FAIL);
|
RETURN(RC_FAIL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,22 +43,32 @@ der_write_tags(asn1_TYPE_descriptor_t *sd,
|
||||||
ssize_t *lens;
|
ssize_t *lens;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
ASN_DEBUG("Writing tags (%s, tm=%d, tc=%d, iskip=%d, tag=%s, mtc=%d)",
|
||||||
|
sd->name, tag_mode, sd->tags_count, sd->tags_impl_skip,
|
||||||
|
ber_tlv_tag_string(tag),
|
||||||
|
tag_mode
|
||||||
|
?(sd->tags_count+1
|
||||||
|
-((tag_mode==-1)?sd->tags_impl_skip:0))
|
||||||
|
:sd->tags_count
|
||||||
|
);
|
||||||
|
|
||||||
if(tag_mode) {
|
if(tag_mode) {
|
||||||
/*
|
/*
|
||||||
* Instead of doing shaman dance like we do in ber_check_tags(),
|
* Instead of doing shaman dance like we do in ber_check_tags(),
|
||||||
* allocate a small array on the stack
|
* allocate a small array on the stack
|
||||||
* and initialize it appropriately.
|
* and initialize it appropriately.
|
||||||
*/
|
*/
|
||||||
tags = alloca((sd->tags_count + (tag_mode?1:0))
|
int stag_offset;
|
||||||
* sizeof(ber_tlv_tag_t));
|
tags = alloca((sd->tags_count + 1) * sizeof(ber_tlv_tag_t));
|
||||||
if(tags == NULL) return -1; /* Impossible on i386 */
|
if(tags == NULL) return -1; /* Impossible on i386 */
|
||||||
tags_count = sd->tags_count
|
tags_count = sd->tags_count
|
||||||
+ 1 /* EXPLICIT or IMPLICIT tag is given */
|
+ 1 /* EXPLICIT or IMPLICIT tag is given */
|
||||||
- ((tag_mode==-1)?sd->tags_impl_skip:0);
|
- ((tag_mode==-1)?sd->tags_impl_skip:0);
|
||||||
/* Copy tags over */
|
/* Copy tags over */
|
||||||
tags[0] = tag;
|
tags[0] = tag;
|
||||||
|
stag_offset = -1 + ((tag_mode==-1)?sd->tags_impl_skip:0);
|
||||||
for(i = 1; i < tags_count; i++)
|
for(i = 1; i < tags_count; i++)
|
||||||
tags[i] = sd->tags[i - 1 + sd->tags_impl_skip];
|
tags[i] = sd->tags[i + stag_offset];
|
||||||
} else {
|
} else {
|
||||||
tags = sd->tags;
|
tags = sd->tags;
|
||||||
tags_count = sd->tags_count;
|
tags_count = sd->tags_count;
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
-- OK: Everything is fine
|
||||||
|
|
||||||
|
-- iso.org.dod.internet.private.enterprise (1.3.6.1.4.1)
|
||||||
|
-- .spelio.software.asn1c.test (9363.1.5.1)
|
||||||
|
-- .48
|
||||||
|
|
||||||
|
ModuleSetChoiceExtensibility
|
||||||
|
{ iso org(3) dod(6) internet (1) private(4) enterprise(1)
|
||||||
|
spelio(9363) software(1) asn1c(5) test(1) 48 }
|
||||||
|
DEFINITIONS ::=
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0.8.14 had problem saving/reloading this object
|
||||||
|
*/
|
||||||
|
UserIdentifier ::= CHOICE {
|
||||||
|
phoneNumber [3] EXPLICIT IA5String
|
||||||
|
}
|
||||||
|
|
||||||
|
END
|
Loading…
Reference in New Issue