commit 4633168cd5ab694a053e4ee433f999f8e82c1f47
Author: Nikos Mavrogiannopoulos <nmav@redhat.com>
Date:   Mon Apr 4 14:21:30 2016 +0200

    decoding: improved tail cache in _asn1_append_sequence_set
    
    We keep the head node in addition to the tail information
    to allow easier deduction of the validity of the cache.

Index: libtasn1-3.6/lib/element.c
===================================================================
--- libtasn1-3.6.orig/lib/element.c
+++ libtasn1-3.6/lib/element.c
@@ -130,37 +130,56 @@ _asn1_convert_integer (const unsigned ch
 
 
 int
-_asn1_append_sequence_set (asn1_node node)
+_asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache)
 {
-  asn1_node p, p2;
-  char temp[LTOSTR_MAX_SIZE];
-  long n;
-
-  if (!node || !(node->down))
-    return ASN1_GENERIC_ERROR;
-
-  p = node->down;
-  while ((type_field (p->type) == ASN1_ETYPE_TAG)
-	 || (type_field (p->type) == ASN1_ETYPE_SIZE))
-    p = p->right;
-  p2 = _asn1_copy_structure3 (p);
-  while (p->right)
-    p = p->right;
-  _asn1_set_right (p, p2);
-
-  if (p->name[0] == 0)
-    _asn1_str_cpy (temp, sizeof (temp), "?1");
-  else
-    {
-      n = strtol (p->name + 1, NULL, 0);
-      n++;
-      temp[0] = '?';
-      _asn1_ltostr (n, temp + 1);
-    }
-  _asn1_set_name (p2, temp);
-  /*  p2->type |= CONST_OPTION; */
-
-  return ASN1_SUCCESS;
+	asn1_node p, p2;
+	char temp[LTOSTR_MAX_SIZE+1];
+	long n;
+	
+	if (!node || !(node->down))
+		return ASN1_GENERIC_ERROR;
+	
+	p = node->down;
+	while ((type_field (p->type) == ASN1_ETYPE_TAG)
+			|| (type_field (p->type) == ASN1_ETYPE_SIZE))
+		p = p->right;
+	
+	p2 = _asn1_copy_structure3 (p);
+	if (p2 == NULL)
+		return ASN1_GENERIC_ERROR;
+			
+	if (pcache == NULL || pcache->tail == NULL || pcache->head != node)
+		{       
+		while (p->right)
+			{ 
+			p = p->right;
+			} 
+		}
+	else
+		{
+		p = pcache->tail;
+		}
+	
+	_asn1_set_right (p, p2);
+	if (pcache)
+		{
+		pcache->head = node;
+		pcache->tail = p2;
+		}
+	
+	if (p->name[0] == 0)
+		_asn1_str_cpy (temp, sizeof (temp), "?1");
+	else
+		{
+		n = strtol (p->name + 1, NULL, 0);
+		n++;
+		temp[0] = '?';
+		_asn1_ltostr (n, temp + 1);
+		}
+	_asn1_set_name (p2, temp);
+	/*  p2->type |= CONST_OPTION; */
+	
+	return ASN1_SUCCESS;
 }
 
 
Index: libtasn1-3.6/lib/element.h
===================================================================
--- libtasn1-3.6.orig/lib/element.h
+++ libtasn1-3.6/lib/element.h
@@ -22,8 +22,13 @@
 #ifndef _ELEMENT_H
 #define _ELEMENT_H
 
+struct node_tail_cache_st
+{
+	asn1_node head; /* the first element of the sequence */
+	asn1_node tail;
+};
 
-int _asn1_append_sequence_set (asn1_node node);
+int _asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcached);
 
 int _asn1_convert_integer (const unsigned char *value,
 			   unsigned char *value_out,
Index: libtasn1-3.6/lib/decoding.c
===================================================================
--- libtasn1-3.6.orig/lib/decoding.c
+++ libtasn1-3.6/lib/decoding.c
@@ -912,6 +912,7 @@ asn1_internal_der_decoding (asn1_node *
   char temp[128];
   int counter, len2, len3, len4, move, ris, tlen;
   unsigned char class;
+  struct node_tail_cache_st tcache = {NULL, NULL};
   unsigned long tag;
   int indefinite, result;
   const unsigned char *der = ider;
@@ -1299,15 +1300,18 @@ asn1_internal_der_decoding (asn1_node *
 		    {		/* indefinite length method */
 		      if (!HAVE_TWO(ider_len) || ((der[counter]) || der[counter + 1]))
 			{
-			  _asn1_append_sequence_set (p);
+			  _asn1_append_sequence_set (p, &tcache);
 			  p = p->down;
 			  while (p->right)
 			    p = p->right;
+			  p = tcache.tail;
 			  move = RIGHT;
 			  continue;
 			}
 
 		      _asn1_set_value (p, NULL, 0);
+		      tcache.tail = NULL; /* finished decoding this structure */
+		      tcache.head = NULL;
 		      DECR_LEN(ider_len, 2);
 		      counter += 2;
 		    }
@@ -1315,15 +1319,18 @@ asn1_internal_der_decoding (asn1_node *
 		    {		/* definite length method */
 		      if (len2 > counter)
 			{
-			  _asn1_append_sequence_set (p);
+			  _asn1_append_sequence_set (p, &tcache);
 			  p = p->down;
 			  while (p->right)
 			    p = p->right;
+			  p = tcache.tail;
 			  move = RIGHT;
 			  continue;
 			}
 
 		      _asn1_set_value (p, NULL, 0);
+		      tcache.tail = NULL; /* finished decoding this structure */
+		      tcache.head = NULL;
 		      if (len2 != counter)
 			{
 			  result = ASN1_DER_ERROR;
@@ -1364,7 +1371,7 @@ asn1_internal_der_decoding (asn1_node *
 			     || (type_field (p2->type) == ASN1_ETYPE_SIZE))
 			p2 = p2->right;
 		      if (p2->right == NULL)
-			_asn1_append_sequence_set (p);
+			  	_asn1_append_sequence_set (p, &tcache);
 		      p = p2;
 		    }
 		}
@@ -1528,6 +1535,7 @@ asn1_internal_der_decoding_element (asn1
   int nameLen = ASN1_MAX_NAME_SIZE * 10 - 1, state;
   int counter, len2, len3, len4, move, ris, tlen;
   unsigned char class;
+  struct node_tail_cache_st tcache = {NULL, NULL};
   unsigned long tag;
   int indefinite, result;
   const unsigned char *der = ider;
@@ -2000,14 +2008,17 @@ asn1_internal_der_decoding_element (asn1
 		  len2 = _asn1_strtol (p->value, NULL, 10);
 		  if (len2 > counter)
 		    {
-		      _asn1_append_sequence_set (p);
+			  _asn1_append_sequence_set (p, &tcache);
 		      p = p->down;
 		      while (p->right)
 			p = p->right;
+		      p = tcache.tail;
 		      move = RIGHT;
 		      continue;
 		    }
 		  _asn1_set_value (p, NULL, 0);
+		  tcache.tail = NULL;	/* finished decoding this structure */
+		  tcache.head = NULL;
 		  if (len2 != counter)
 		    {
 		      result = ASN1_DER_ERROR;
@@ -2056,7 +2067,8 @@ asn1_internal_der_decoding_element (asn1
 				     ASN1_ETYPE_SIZE))
 			    p2 = p2->right;
 			  if (p2->right == NULL)
-			    _asn1_append_sequence_set (p);
+			  	_asn1_append_sequence_set (p, &tcache);
+		      p = p->down;
 			  p = p2;
 			  state = FOUND;
 			}
