From 1d9e8dd64e13be94e4253a65d56c258de9a34c8f Mon Sep 17 00:00:00 2001 From: Lev Walkin Date: Wed, 7 Dec 2005 05:46:03 +0000 Subject: [PATCH] added stack control to PER --- skeletons/asn-decoder-template.c | 98 ++++++++++++++++++-------------- skeletons/asn_codecs.h | 10 ++-- skeletons/asn_internal.h | 22 +++++++ skeletons/ber_decoder.c | 26 ++++----- skeletons/ber_decoder.h | 4 +- skeletons/ber_tlv_length.c | 13 +---- skeletons/ber_tlv_length.h | 1 - skeletons/constr_CHOICE.c | 3 + skeletons/constr_SEQUENCE.c | 14 ++++- skeletons/constr_SET_OF.c | 3 + skeletons/per_decoder.c | 36 ++++++++++++ skeletons/per_decoder.h | 16 ++++-- skeletons/per_support.c | 2 +- skeletons/per_support.h | 6 +- skeletons/xer_decoder.c | 13 ++++- skeletons/xer_decoder.h | 4 +- 16 files changed, 180 insertions(+), 91 deletions(-) diff --git a/skeletons/asn-decoder-template.c b/skeletons/asn-decoder-template.c index c50d8b9c..b4280646 100644 --- a/skeletons/asn-decoder-template.c +++ b/skeletons/asn-decoder-template.c @@ -19,6 +19,7 @@ #include /* for errno */ #include +#include /* for _ASN_DEFAULT_STACK_MAX */ extern asn_TYPE_descriptor_t asn_DEF; /* ASN.1 type to be decoded */ #ifdef ASN_PDU_COLLECTION /* Generated by asn1c: -pdu=... */ @@ -51,11 +52,17 @@ static enum output_format { OUT_NULL /* -onull: No pretty-printing */ } oform; /* -o */ -#define DEBUG(fmt, args...) do { \ - if(!opt_debug) break; \ - fprintf(stderr, fmt, ##args); \ - fprintf(stderr, "\n"); \ -} while(0) +/* Debug output function */ +static inline void +DEBUG(const char *fmt, ...) { + va_list ap; + if(!opt_debug) return; + fprintf(stderr, "AD: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); +} int main(int ac, char **av) { @@ -131,9 +138,9 @@ main(int ac, char **av) { break; case 's': opt_stack = atoi(optarg); - if(opt_stack <= 0) { + if(opt_stack < 0) { fprintf(stderr, - "-s %s: Value greater than 0 expected\n", + "-s %s: Non-negative value expected\n", optarg); exit(EX_UNAVAILABLE); } @@ -164,8 +171,8 @@ main(int ac, char **av) { " -c Check ASN.1 constraints after decoding\n" " -d Enable debugging (-dd is even better)\n" " -n Process files times\n" - " -s Set the stack usage limit\n" - , (long)suggested_bufsize); + " -s Set the stack usage limit (default is %d)\n" + , (long)suggested_bufsize, _ASN_DEFAULT_STACK_MAX); exit(EX_USAGE); } @@ -250,11 +257,12 @@ static int write_out(const void *buffer, size_t size, void *key) { return (fwrite(buffer, 1, size, fp) == size) ? 0 : -1; } -static char *buffer; -static size_t buf_offset; /* Offset from the start */ +static char *buffer; static size_t buf_len; /* Length of meaningful contents */ -static size_t buf_size; /* Allocated memory */ -static off_t buf_shifted; /* Number of bytes ever shifted */ +static size_t buf_size; /* Allocated memory */ +static size_t buf_offset; /* Offset from the start */ +static off_t buf_shifted; /* Number of bytes ever shifted */ +static int buf_nreallocs; /* Number of reallocations */ #define bufptr (buffer + buf_offset) #define bufend (buffer + buf_offset + buf_len) @@ -262,13 +270,13 @@ static off_t buf_shifted; /* Number of bytes ever shifted */ /* * Ensure that the buffer contains at least this amount of free space. */ -static void buf_extend(size_t bySize) { +static void buf_extend(const void *data2add, size_t bySize) { DEBUG("buf_extend(%ld) { o=%ld l=%ld s=%ld }", (long)bySize, (long)buf_offset, (long)buf_len, (long)buf_size); if(buf_size >= (buf_offset + buf_len + bySize)) { - return; /* Nothing to do */ + /* Nothing to do */ } else if(bySize <= buf_offset) { DEBUG("\tContents shifted by %ld", (long)buf_offset); @@ -278,17 +286,22 @@ static void buf_extend(size_t bySize) { buf_offset = 0; } else { size_t newsize = (buf_size << 2) + bySize; - void *p = realloc(buffer, newsize); - if(p) { - buffer = (char *)p; - buf_size = newsize; - - DEBUG("\tBuffer reallocated to %ld", (long)newsize); - } else { + void *p = malloc(newsize); + if(!p) { perror("realloc()"); exit(EX_OSERR); } + memcpy(p, buffer, buf_len); + free(buffer); + buffer = (char *)p; + buf_size = newsize; + buf_offset = 0; + DEBUG("\tBuffer reallocated to %ld, %d time", + (long)newsize, ++buf_nreallocs); } + + memcpy(buffer + buf_offset + buf_len, data2add, bySize); + buf_len += bySize; } static void *data_decode_from_file(asn_TYPE_descriptor_t *pduType, const char *fname, ssize_t suggested_bufsize) { @@ -310,7 +323,7 @@ static void *data_decode_from_file(asn_TYPE_descriptor_t *pduType, const char *f DEBUG("Processing file %s", fname); fp = fopen(fname, "r"); } else { - DEBUG("Processing standard input"); + DEBUG("Processing %s", "standard input"); fname = "stdin"; fp = stdin; } @@ -330,8 +343,10 @@ static void *data_decode_from_file(asn_TYPE_descriptor_t *pduType, const char *f fbuf_size = suggested_bufsize; } + buf_nreallocs = 0; buf_shifted = 0; buf_offset = 0; + buf_size = 0; buf_len = 0; /* Pretend immediate EOF */ @@ -339,19 +354,15 @@ static void *data_decode_from_file(asn_TYPE_descriptor_t *pduType, const char *f rval.consumed = 0; while((rd = fread(fbuf, 1, fbuf_size, fp)) || !feof(fp)) { - asn_per_data_t pd; char *i_bptr; size_t i_size; /* * Copy the data over, or use the original buffer. */ - if(buf_len) { + if(buf_size) { /* Append the new data into the intermediate buffer */ - buf_extend(rd); - memcpy(bufend, fbuf, rd); - buf_len += rd; - + buf_extend(fbuf, rd); i_bptr = bufptr; i_size = buf_len; } else { @@ -369,36 +380,35 @@ static void *data_decode_from_file(asn_TYPE_descriptor_t *pduType, const char *f (void **)&structure, i_bptr, i_size); break; case INP_PER: - pd.buffer = (uint8_t *)i_bptr; - pd.nboff = 0; - pd.nbits = i_size * 8; - rval = pduType->uper_decoder(opt_codec_ctx, pduType, 0, - (void **)&structure, &pd); + rval = uper_decode(opt_codec_ctx, pduType, + (void **)&structure, i_bptr, i_size); break; } - DEBUG("decode(%ld) consumed %ld, code %d", - (long)buf_len, (long)rval.consumed, rval.code); + DEBUG("decode(%ld) consumed %ld (%ld), code %d", + (long)buf_len, (long)rval.consumed, (long)i_size, + rval.code); - if(buf_len == 0) { + if(buf_size == 0) { /* * Switch the remainder into the intermediate buffer. */ if(rval.code != RC_FAIL && rval.consumed < rd) { - buf_extend(rd - rval.consumed); - memcpy(bufend, - fbuf + rval.consumed, - rd - rval.consumed); - buf_len = rd - rval.consumed; + buf_extend(fbuf + rval.consumed, + rd - rval.consumed); + rval.consumed = 0; } } switch(rval.code) { case RC_OK: - DEBUG("RC_OK, finishing up"); + DEBUG("RC_OK, finishing up with %ld", + (long)rval.consumed); if(fp != stdin) fclose(fp); return structure; case RC_WMORE: - DEBUG("RC_WMORE, continuing..."); + DEBUG("RC_WMORE, continuing %ld with %ld..%ld..%ld", + (long)rval.consumed, (long)buf_offset, + (long)buf_len, (long)buf_size); /* * Adjust position inside the source buffer. */ diff --git a/skeletons/asn_codecs.h b/skeletons/asn_codecs.h index f25a7975..bd6b946f 100644 --- a/skeletons/asn_codecs.h +++ b/skeletons/asn_codecs.h @@ -13,11 +13,13 @@ extern "C" { struct asn_TYPE_descriptor_s; /* Forward declaration */ /* - * This structure defines a context that may be passed to every ASN.1 encoder - * or decoder function. + * This structure defines a set of parameters that may be passed + * to every ASN.1 encoder or decoder function. * WARNING: if max_stack_size member is set, and you are calling the - * function pointers of the asn_TYPE_descriptor_t directly, - * this structure must be ALLOCATED ON THE STACK! + * function pointers of the asn_TYPE_descriptor_t directly, + * this structure must be ALLOCATED ON THE STACK! + * If you can't always satisfy this requirement, use ber_decode(), + * xer_decode() and uper_decode() functions instead. */ typedef struct asn_codec_ctx_s { /* diff --git a/skeletons/asn_internal.h b/skeletons/asn_internal.h index 4a86141d..89c9c418 100644 --- a/skeletons/asn_internal.h +++ b/skeletons/asn_internal.h @@ -82,6 +82,28 @@ static inline void ASN_DEBUG(const char *fmt, ...) { (void)fmt; } if(cb(" ", 4, app_key) < 0) return -1; \ } while(0) +/* + * Check stack against overflow, if limit is set. + */ +#define _ASN_DEFAULT_STACK_MAX (30000) +static inline int +_ASN_STACK_OVERFLOW_CHECK(asn_codec_ctx_t *ctx) { + if(ctx && ctx->max_stack_size) { + + /* ctx MUST be allocated on the stack */ + ptrdiff_t usedstack = ((char *)ctx - (char *)&ctx); + if(usedstack > 0) usedstack = -usedstack; /* grows up! */ + + /* double negative required to avoid int wrap-around */ + if(usedstack < -(ptrdiff_t)ctx->max_stack_size) { + ASN_DEBUG("Stack limit %ld reached", + (long)ctx->max_stack_size); + return -1; + } + } + return 0; +} + #ifdef __cplusplus } #endif diff --git a/skeletons/ber_decoder.c b/skeletons/ber_decoder.c index bb7a0904..601f66c0 100644 --- a/skeletons/ber_decoder.c +++ b/skeletons/ber_decoder.c @@ -33,11 +33,18 @@ ber_decode(asn_codec_ctx_t *opt_codec_ctx, asn_codec_ctx_t s_codec_ctx; /* - * Satisfy the requirement that the codec context + * Stack checker requires that the codec context * must be allocated on the stack. */ - if(opt_codec_ctx && opt_codec_ctx->max_stack_size) { - s_codec_ctx = *opt_codec_ctx; + if(opt_codec_ctx) { + if(opt_codec_ctx->max_stack_size) { + s_codec_ctx = *opt_codec_ctx; + opt_codec_ctx = &s_codec_ctx; + } + } else { + /* If context is not given, be security-conscious anyway */ + memset(&s_codec_ctx, 0, sizeof(s_codec_ctx)); + s_codec_ctx.max_stack_size = _ASN_DEFAULT_STACK_MAX; opt_codec_ctx = &s_codec_ctx; } @@ -73,17 +80,8 @@ ber_check_tags(asn_codec_ctx_t *opt_codec_ctx, /* * Make sure we didn't exceed the maximum stack size. */ - if(opt_codec_ctx && opt_codec_ctx->max_stack_size) { - ptrdiff_t usedstack = ((char *)opt_codec_ctx - (char *)&size); - /* double negative is required to avoid int wrap-around */ - if(usedstack > 0) usedstack = -usedstack; - ASN_DEBUG("Current stack size %ld", -(long)usedstack); - if(usedstack < -(ptrdiff_t)opt_codec_ctx->max_stack_size) { - ASN_DEBUG("Stack limit %ld reached", - (long)opt_codec_ctx->max_stack_size); - RETURN(RC_FAIL); - } - } + if(_ASN_STACK_OVERFLOW_CHECK(opt_codec_ctx)) + RETURN(RC_FAIL); /* * So what does all this implicit skip stuff mean? diff --git a/skeletons/ber_decoder.h b/skeletons/ber_decoder.h index fc259843..768133b6 100644 --- a/skeletons/ber_decoder.h +++ b/skeletons/ber_decoder.h @@ -46,8 +46,8 @@ typedef asn_dec_rval_t (ber_type_decoder_f)( * head->last_tag_form is non-zero. */ asn_dec_rval_t ber_check_tags( - struct asn_codec_ctx_s *opt_codec_ctx, /* optional context */ - struct asn_TYPE_descriptor_s *type_dsc, + struct asn_codec_ctx_s *opt_codec_ctx, /* codec options */ + struct asn_TYPE_descriptor_s *type_descriptor, asn_struct_ctx_t *opt_ctx, /* saved decoding context */ const void *ptr, size_t size, int tag_mode, /* {-1,0,1}: IMPLICIT, no, EXPLICIT */ diff --git a/skeletons/ber_tlv_length.c b/skeletons/ber_tlv_length.c index 49343d37..f7342e7e 100644 --- a/skeletons/ber_tlv_length.c +++ b/skeletons/ber_tlv_length.c @@ -84,17 +84,8 @@ ber_skip_length(asn_codec_ctx_t *opt_codec_ctx, /* * Make sure we didn't exceed the maximum stack size. */ - if(opt_codec_ctx && opt_codec_ctx->max_stack_size) { - ptrdiff_t usedstack = ((char *)opt_codec_ctx - (char *)&size); - /* double negative is required to avoid int wrap-around */ - if(usedstack > 0) usedstack = -usedstack; - ASN_DEBUG("Current stack size %ld", -(long)usedstack); - if(usedstack < -(ptrdiff_t)opt_codec_ctx->max_stack_size) { - ASN_DEBUG("Stack limit %ld reached", - (long)opt_codec_ctx->max_stack_size); - return -1; - } - } + if(_ASN_STACK_OVERFLOW_CHECK(opt_codec_ctx)) + return -1; /* * Determine the size of L in TLV. diff --git a/skeletons/ber_tlv_length.h b/skeletons/ber_tlv_length.h index d9127061..34968022 100644 --- a/skeletons/ber_tlv_length.h +++ b/skeletons/ber_tlv_length.h @@ -31,7 +31,6 @@ ssize_t ber_fetch_length(int _is_constructed, const void *bufptr, size_t size, * RETURN VALUES: * Standard {-1,0,>0} convention. */ -struct asn_codec_ctx_s; /* Forward declaration */ ssize_t ber_skip_length( struct asn_codec_ctx_s *opt_codec_ctx, /* optional context */ int _is_constructed, const void *bufptr, size_t size); diff --git a/skeletons/constr_CHOICE.c b/skeletons/constr_CHOICE.c index d3738df3..33cec7e6 100644 --- a/skeletons/constr_CHOICE.c +++ b/skeletons/constr_CHOICE.c @@ -827,6 +827,9 @@ CHOICE_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, void *st = *sptr; int value; + if(_ASN_STACK_OVERFLOW_CHECK(opt_codec_ctx)) + _ASN_DECODE_FAILED; + /* * Create the target structure if it is not present already. */ diff --git a/skeletons/constr_SEQUENCE.c b/skeletons/constr_SEQUENCE.c index decc56d8..e4a5b080 100644 --- a/skeletons/constr_SEQUENCE.c +++ b/skeletons/constr_SEQUENCE.c @@ -703,7 +703,8 @@ SEQUENCE_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, } tcv = xer_check_tag(buf_ptr, ch_size, xml_tag); - ASN_DEBUG("XER/SEQUENCE: tcv = %d, ph=%d", tcv, ctx->phase); + ASN_DEBUG("XER/SEQUENCE: tcv = %d, ph=%d [%s]", + tcv, ctx->phase, xml_tag); /* Skip the extensions section */ if(ctx->phase == 3) { @@ -829,7 +830,13 @@ SEQUENCE_decode_xer(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, break; } - ASN_DEBUG("Unexpected XML tag in SEQUENCE"); + ASN_DEBUG("Unexpected XML tag in SEQUENCE [%c%c%c%c%c%c]", + size>0?((const char *)buf_ptr)[0]:'.', + size>1?((const char *)buf_ptr)[1]:'.', + size>2?((const char *)buf_ptr)[2]:'.', + size>3?((const char *)buf_ptr)[3]:'.', + size>4?((const char *)buf_ptr)[4]:'.', + size>5?((const char *)buf_ptr)[5]:'.'); break; } @@ -1028,6 +1035,9 @@ SEQUENCE_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, (void)constraints; + if(_ASN_STACK_OVERFLOW_CHECK(opt_codec_ctx)) + _ASN_DECODE_FAILED; + if(!st) { st = *sptr = CALLOC(1, specs->struct_size); if(!st) _ASN_DECODE_FAILED; diff --git a/skeletons/constr_SET_OF.c b/skeletons/constr_SET_OF.c index 103817b4..e93c70c8 100644 --- a/skeletons/constr_SET_OF.c +++ b/skeletons/constr_SET_OF.c @@ -864,6 +864,9 @@ SET_OF_decode_uper(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, int repeat = 0; ssize_t nelems; + if(_ASN_STACK_OVERFLOW_CHECK(opt_codec_ctx)) + _ASN_DECODE_FAILED; + /* * Create the target structure if it is not present already. */ diff --git a/skeletons/per_decoder.c b/skeletons/per_decoder.c index 55c04cb5..506ad674 100644 --- a/skeletons/per_decoder.c +++ b/skeletons/per_decoder.c @@ -1,2 +1,38 @@ #include +#include #include + +asn_dec_rval_t +uper_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, void **sptr, const void *buffer, size_t size) { + asn_codec_ctx_t s_codec_ctx; + asn_per_data_t pd; + + /* + * Stack checker requires that the codec context + * must be allocated on the stack. + */ + if(opt_codec_ctx) { + if(opt_codec_ctx->max_stack_size) { + s_codec_ctx = *opt_codec_ctx; + opt_codec_ctx = &s_codec_ctx; + } + } else { + /* If context is not given, be security-conscious anyway */ + memset(&s_codec_ctx, 0, sizeof(s_codec_ctx)); + s_codec_ctx.max_stack_size = _ASN_DEFAULT_STACK_MAX; + opt_codec_ctx = &s_codec_ctx; + } + + /* Fill in the position indicator */ + pd.buffer = (const uint8_t *)buffer; + pd.nboff = 0; + pd.nbits = 8 * size; /* 8 is CHAR_BIT from */ + + /* + * Invoke type-specific decoder. + */ + if(!td->uper_decoder) + _ASN_DECODE_FAILED; /* PER is not compiled in */ + return td->uper_decoder(opt_codec_ctx, td, 0, sptr, &pd); +} + diff --git a/skeletons/per_decoder.h b/skeletons/per_decoder.h index 6303b3cf..97eef147 100644 --- a/skeletons/per_decoder.h +++ b/skeletons/per_decoder.h @@ -12,6 +12,18 @@ extern "C" { #endif +struct asn_TYPE_descriptor_s; /* Forward declaration */ + +/* + * Unaligned PER decoder of any ASN.1 type. May be invoked by the application. + */ +asn_dec_rval_t uper_decode(struct asn_codec_ctx_s *opt_codec_ctx, + struct asn_TYPE_descriptor_s *type_descriptor, /* Type to decode */ + void **struct_ptr, /* Pointer to a target structure's pointer */ + const void *buffer, /* Data to be decoded */ + size_t size /* Size of data buffer */ + ); + /* * Pre-computed PER constraints. @@ -23,7 +35,6 @@ extern "C" { APC_EXTENSIBLE = 0x4 /* May have extension */ }; typedef struct asn_per_constraint_s { - enum asn_per_constraint_flags flags; int range_bits; /* Full number of bits in the range */ int effective_bits; /* Effective bits */ @@ -35,9 +46,6 @@ typedef struct asn_per_constraints_s { asn_per_constraint_t size; } asn_per_constraints_t; - -struct asn_TYPE_descriptor_s; /* Forward declaration */ - /* * Type of the type-specific PER decoder function. */ diff --git a/skeletons/per_support.c b/skeletons/per_support.c index 9b048f0f..63b39d08 100644 --- a/skeletons/per_support.c +++ b/skeletons/per_support.c @@ -12,7 +12,7 @@ int32_t per_get_few_bits(asn_per_data_t *pd, int nbits) { size_t off; /* Next after last bit offset */ uint32_t accum; - uint8_t *buf; + const uint8_t *buf; if(nbits < 0 || pd->nboff + nbits > pd->nbits) return -1; diff --git a/skeletons/per_support.h b/skeletons/per_support.h index a8f15fbe..44f0bb40 100644 --- a/skeletons/per_support.h +++ b/skeletons/per_support.h @@ -15,9 +15,9 @@ extern "C" { * This structure describes a position inside a PER bit stream. */ typedef struct asn_per_data_s { - uint8_t *buffer; /* Pointer to the octet stream */ - size_t nboff; /* Bit offset to the meaningful bit */ - size_t nbits; /* Number of bits in the stream */ + const uint8_t *buffer; /* Pointer to the octet stream */ + size_t nboff; /* Bit offset to the meaningful bit */ + size_t nbits; /* Number of bits in the stream */ } asn_per_data_t; /* diff --git a/skeletons/xer_decoder.c b/skeletons/xer_decoder.c index e48a5ed0..2b3f16a6 100644 --- a/skeletons/xer_decoder.c +++ b/skeletons/xer_decoder.c @@ -16,11 +16,18 @@ xer_decode(asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td, asn_codec_ctx_t s_codec_ctx; /* - * Satisfy the requirement that the codec context + * Stack checker requires that the codec context * must be allocated on the stack. */ - if(opt_codec_ctx && opt_codec_ctx->max_stack_size) { - s_codec_ctx = *opt_codec_ctx; + if(opt_codec_ctx) { + if(opt_codec_ctx->max_stack_size) { + s_codec_ctx = *opt_codec_ctx; + opt_codec_ctx = &s_codec_ctx; + } + } else { + /* If context is not given, be security-conscious anyway */ + memset(&s_codec_ctx, 0, sizeof(s_codec_ctx)); + s_codec_ctx.max_stack_size = _ASN_DEFAULT_STACK_MAX; opt_codec_ctx = &s_codec_ctx; } diff --git a/skeletons/xer_decoder.h b/skeletons/xer_decoder.h index 349f349e..cf0d846f 100644 --- a/skeletons/xer_decoder.h +++ b/skeletons/xer_decoder.h @@ -14,13 +14,13 @@ extern "C" { struct asn_TYPE_descriptor_s; /* Forward declaration */ /* - * The XER decoder of any type. May be invoked by the application. + * The XER decoder of any ASN.1 type. May be invoked by the application. */ asn_dec_rval_t xer_decode(struct asn_codec_ctx_s *opt_codec_ctx, struct asn_TYPE_descriptor_s *type_descriptor, void **struct_ptr, /* Pointer to a target structure's pointer */ const void *buffer, /* Data to be decoded */ - size_t size /* Size of that buffer */ + size_t size /* Size of data buffer */ ); /*