added stack control to PER

diff --git a/skeletons/asn-decoder-template.c b/skeletons/asn-decoder-template.c
index c50d8b9..b428064 100644
--- a/skeletons/asn-decoder-template.c
+++ b/skeletons/asn-decoder-template.c
@@ -19,6 +19,7 @@
 #include <errno.h>	/* for errno */
 
 #include <asn_application.h>
+#include <asn_internal.h>	/* 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 @@
 	OUT_NULL	/* -onull: No pretty-printing */
 } oform;	/* -o<format> */
 
-#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 @@
 		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 @@
 		"  -c           Check ASN.1 constraints after decoding\n"
 		"  -d           Enable debugging (-dd is even better)\n"
 		"  -n <num>     Process files <num> times\n"
-		"  -s <size>    Set the stack usage limit\n"
-		, (long)suggested_bufsize);
+		"  -s <size>    Set the stack usage limit (default is %d)\n"
+		, (long)suggested_bufsize, _ASN_DEFAULT_STACK_MAX);
 		exit(EX_USAGE);
 	}
 
@@ -250,11 +257,12 @@
 	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 @@
 /*
  * 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 @@
 		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 @@
 		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 @@
 		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 @@
 	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 @@
 				(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 f25a797..bd6b946 100644
--- a/skeletons/asn_codecs.h
+++ b/skeletons/asn_codecs.h
@@ -13,11 +13,13 @@
 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 4a86141..89c9c41 100644
--- a/skeletons/asn_internal.h
+++ b/skeletons/asn_internal.h
@@ -82,6 +82,28 @@
 		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 bb7a090..601f66c 100644
--- a/skeletons/ber_decoder.c
+++ b/skeletons/ber_decoder.c
@@ -33,11 +33,18 @@
 	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 @@
 	/*
 	 * 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 fc25984..768133b 100644
--- a/skeletons/ber_decoder.h
+++ b/skeletons/ber_decoder.h
@@ -46,8 +46,8 @@
  * 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 49343d3..f7342e7 100644
--- a/skeletons/ber_tlv_length.c
+++ b/skeletons/ber_tlv_length.c
@@ -84,17 +84,8 @@
 	/*
 	 * 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 d912706..3496802 100644
--- a/skeletons/ber_tlv_length.h
+++ b/skeletons/ber_tlv_length.h
@@ -31,7 +31,6 @@
  * 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 d3738df..33cec7e 100644
--- a/skeletons/constr_CHOICE.c
+++ b/skeletons/constr_CHOICE.c
@@ -827,6 +827,9 @@
 	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 decc56d..e4a5b08 100644
--- a/skeletons/constr_SEQUENCE.c
+++ b/skeletons/constr_SEQUENCE.c
@@ -703,7 +703,8 @@
 		}
 
 		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 @@
 			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 @@
 
 	(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 103817b..e93c70c 100644
--- a/skeletons/constr_SET_OF.c
+++ b/skeletons/constr_SET_OF.c
@@ -864,6 +864,9 @@
 	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 55c04cb..506ad67 100644
--- a/skeletons/per_decoder.c
+++ b/skeletons/per_decoder.c
@@ -1,2 +1,38 @@
 #include <asn_application.h>
+#include <asn_internal.h>
 #include <per_decoder.h>
+
+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 <limits.h> */
+
+	/*
+	 * 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 6303b3c..97eef14 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 @@
 	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 @@
 	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 9b048f0..63b39d0 100644
--- a/skeletons/per_support.c
+++ b/skeletons/per_support.c
@@ -12,7 +12,7 @@
 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 a8f15fb..44f0bb4 100644
--- a/skeletons/per_support.h
+++ b/skeletons/per_support.h
@@ -15,9 +15,9 @@
  * 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 e48a5ed..2b3f16a 100644
--- a/skeletons/xer_decoder.c
+++ b/skeletons/xer_decoder.c
@@ -16,11 +16,18 @@
 	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 349f349..cf0d846 100644
--- a/skeletons/xer_decoder.h
+++ b/skeletons/xer_decoder.h
@@ -14,13 +14,13 @@
 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 */
 	);
 
 /*