From 4082ca2220b5ba910b546afddf7780fc4a51f75a Mon Sep 17 00:00:00 2001
From: Daiki Ueno <ueno@gnu.org>
Date: Sat, 19 Oct 2024 02:47:04 +0900
Subject: [PATCH] asn1_der_decoding2: optimize _asn1_find_up call with node
 cache

If we are parsing a sequence or set and the current node is a direct
child of it, there is no need to traverse the list back to the
leftmost one as we have a node cache.

Signed-off-by: Daiki Ueno <ueno@gnu.org>
Signed-off-by: Simon Josefsson <simon@josefsson.org>
---
 lib/decoding.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

Index: libtasn1-3.6/lib/decoding.c
===================================================================
--- libtasn1-3.6.orig/lib/decoding.c
+++ libtasn1-3.6/lib/decoding.c
@@ -1300,10 +1300,12 @@ asn1_internal_der_decoding (asn1_node *
 		    {		/* indefinite length method */
 		      if (!HAVE_TWO(ider_len) || ((der[counter]) || der[counter + 1]))
 			{
-			  _asn1_append_sequence_set (p, &tcache);
-			  p = p->down;
-			  while (p->right)
-			    p = p->right;
+			  result = _asn1_append_sequence_set (p, &tcache);
+			  if (result != 0)
+			  {
+				warn ();
+				goto cleanup;
+			  }
 			  p = tcache.tail;
 			  move = RIGHT;
 			  continue;
@@ -1319,10 +1321,12 @@ asn1_internal_der_decoding (asn1_node *
 		    {		/* definite length method */
 		      if (len2 > counter)
 			{
-			  _asn1_append_sequence_set (p, &tcache);
-			  p = p->down;
-			  while (p->right)
-			    p = p->right;
+			  result = _asn1_append_sequence_set (p, &tcache);
+			  if (result != 0)
+			  {
+				warn ();
+				goto cleanup;
+			  }
 			  p = tcache.tail;
 			  move = RIGHT;
 			  continue;
@@ -1478,7 +1482,14 @@ asn1_internal_der_decoding (asn1_node *
 	    move = UP;
 	}
       if (move == UP)
-	p = _asn1_find_up (p);
+	{
+	  /* If we are parsing a sequence or set and p is a direct
+	     child of it, no need to traverse the list back to the leftmost node. */
+	  if (tcache.tail == p)
+	    p = tcache.head;
+	  else
+	    p = _asn1_find_up (p);
+	}
     }
 
   _asn1_delete_not_used (*element);
@@ -2008,10 +2019,12 @@ asn1_internal_der_decoding_element (asn1
 		  len2 = _asn1_strtol (p->value, NULL, 10);
 		  if (len2 > counter)
 		    {
-			  _asn1_append_sequence_set (p, &tcache);
-		      p = p->down;
-		      while (p->right)
-			p = p->right;
+			  result =_asn1_append_sequence_set (p, &tcache);
+			  if (result != 0)
+			  {
+				warn ();
+				goto cleanup;
+			  }
 		      p = tcache.tail;
 		      move = RIGHT;
 		      continue;
@@ -2066,9 +2079,15 @@ asn1_internal_der_decoding_element (asn1
 				 || (type_field (p2->type) ==
 				     ASN1_ETYPE_SIZE))
 			    p2 = p2->right;
-			  if (p2->right == NULL)
-			  	_asn1_append_sequence_set (p, &tcache);
-		      p = p->down;
+			  if (p2->right == NULL) 
+			  {
+				result = _asn1_append_sequence_set (p, &tcache);
+				if (result != 0)
+				  {
+					warn ();
+					goto cleanup;
+				  }
+			  }
 			  p = p2;
 			  state = FOUND;
 			}
Index: libtasn1-3.6/lib/element.c
===================================================================
--- libtasn1-3.6.orig/lib/element.c
+++ libtasn1-3.6/lib/element.c
@@ -32,6 +32,8 @@
 #include "structure.h"
 
 #include "element.h"
+#include <limits.h>
+#include "intprops.h"
 
 void
 _asn1_hierarchical_name (asn1_node node, char *name, int name_size)
@@ -128,61 +130,101 @@ _asn1_convert_integer (const unsigned ch
   return ASN1_SUCCESS;
 }
 
+int
+_asn1_node_array_set (struct asn1_node_array_st *array, size_t position,
+                      asn1_node node)
+{
+  if (position >= array->size)
+    {
+      size_t new_size = position, i;
+      asn1_node *new_nodes;
+
+      if (INT_MULTIPLY_OVERFLOW (new_size, 2))
+        return ASN1_GENERIC_ERROR;
+      new_size *= 2;
+
+      if (INT_ADD_OVERFLOW (new_size, 1))
+        return ASN1_GENERIC_ERROR;
+      new_size += 1;
+
+      if (INT_MULTIPLY_OVERFLOW (new_size, sizeof (*new_nodes)))
+        return ASN1_GENERIC_ERROR;
+
+      new_nodes = realloc (array->nodes, new_size * sizeof (*new_nodes));
+      if (!new_nodes)
+        return ASN1_MEM_ALLOC_ERROR;
+
+      for (i = array->size; i < new_size; i++)
+        new_nodes[i] = NULL;
+
+      array->nodes = new_nodes;
+      array->size = new_size;
+    }
+
+  array->nodes[position] = node;
+  return ASN1_SUCCESS;
+}
 
 int
 _asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache)
 {
-	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;
+  asn1_node p, p2;
+  char temp[LTOSTR_MAX_SIZE + 1];
+  long n;
+  int result;
+
+  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;
+    }
+
+  n = 0;
+  if (p->name[0] != 0)
+    {
+      n = strtol (p->name + 1, NULL, 10);
+      if (n <= 0 || n >= LONG_MAX - 1)
+        return ASN1_GENERIC_ERROR;
+    }
+  temp[0] = '?';
+  _asn1_ltostr (n + 1, temp + 1);
+  _asn1_set_name (p2, temp);
+  /*  p2->type |= CONST_OPTION; */
+  result = _asn1_node_array_set (&node->numbered_children, n, p2);
+  if (result != ASN1_SUCCESS)
+    return result;
+  p2->parent = node;
+
+  return ASN1_SUCCESS;
 }
 
 
+
 /**
  * asn1_write_value:
  * @node_root: pointer to a structure
@@ -627,7 +669,7 @@ asn1_internal_write_value (asn1_node nod
     case ASN1_ETYPE_SET_OF:
       if (_asn1_strcmp (value, "NEW"))
 	return ASN1_VALUE_NOT_VALID;
-      _asn1_append_sequence_set (node);
+      _asn1_append_sequence_set (node, NULL);
       break;
     default:
       return ASN1_ELEMENT_NOT_FOUND;
Index: libtasn1-3.6/lib/element.h
===================================================================
--- libtasn1-3.6.orig/lib/element.h
+++ libtasn1-3.6/lib/element.h
@@ -22,6 +22,8 @@
 #ifndef _ELEMENT_H
 #define _ELEMENT_H
 
+typedef const asn1_node_st *asn1_node_const;
+
 struct node_tail_cache_st
 {
 	asn1_node head; /* the first element of the sequence */
@@ -36,4 +38,14 @@ int _asn1_convert_integer (const unsigne
 
 void _asn1_hierarchical_name (asn1_node node, char *name, int name_size);
 
+static inline asn1_node_const
+_asn1_node_array_get (const struct asn1_node_array_st *array, size_t position)
+{
+  return position < array->size ? array->nodes[position] : NULL;
+}
+
+int
+_asn1_node_array_set (struct asn1_node_array_st *array, size_t position,
+                      asn1_node node);
+
 #endif
Index: libtasn1-3.6/lib/int.h
===================================================================
--- libtasn1-3.6.orig/lib/int.h
+++ libtasn1-3.6/lib/int.h
@@ -40,6 +40,12 @@
 
 #define ASN1_SMALL_VALUE_SIZE 16
 
+struct asn1_node_array_st
+{
+  asn1_node *nodes;
+  size_t size;
+};
+
 /* This structure is also in libtasn1.h, but then contains less
    fields.  You cannot make any modifications to these first fields
    without breaking ABI.  */
@@ -56,6 +62,8 @@ struct asn1_node_st
   asn1_node left;		/* Pointer to the next list element */
   /* private fields: */
   unsigned char small_value[ASN1_SMALL_VALUE_SIZE];	/* For small values */
+  asn1_node parent;		/* Pointer to the parent node */
+  struct asn1_node_array_st numbered_children; /* Array of unnamed child nodes for caching */ 
 };
 
 typedef struct tag_and_class_st
Index: libtasn1-3.6/lib/parser_aux.c
===================================================================
--- libtasn1-3.6.orig/lib/parser_aux.c
+++ libtasn1-3.6/lib/parser_aux.c
@@ -20,6 +20,7 @@
  */
 
 #include <int.h>
+#include <limits.h>
 #include <hash-pjw-bare.h>
 #include "parser_aux.h"
 #include "gstr.h"
@@ -98,6 +99,7 @@ asn1_internal_find_node (asn1_node point
   const char *n_start;
   unsigned int nsize;
   unsigned int nhash;
+  const struct asn1_node_array_st *numbered_children;
 
   if (pointer == NULL)
     return NULL;
@@ -173,6 +175,7 @@ asn1_internal_find_node (asn1_node point
       if (p->down == NULL)
 	return NULL;
 
+      numbered_children = &p->numbered_children;
       p = p->down;
 
       /* The identifier "?LAST" indicates the last element
@@ -186,6 +189,12 @@ asn1_internal_find_node (asn1_node point
 	}
       else
 	{			/* no "?LAST" */
+         if (n[0] == '?' && isdigit (n[1]))
+           {
+             long position = strtol (n + 1, NULL, 10);
+             if (position > 0 && position < LONG_MAX)
+               p = _asn1_node_array_get (numbered_children, position - 1);
+           }
 	  while (p)
 	    {
 	      if (p->name_hash == nhash && !strcmp (p->name, n))
@@ -472,6 +481,8 @@ _asn1_remove_node (asn1_node node, unsig
       if (node->value != node->small_value)
         free (node->value);
     }
+
+  free (node->numbered_children.nodes);
   free (node);
 }
 
Index: libtasn1-3.6/lib/structure.c
===================================================================
--- libtasn1-3.6.orig/lib/structure.c
+++ libtasn1-3.6/lib/structure.c
@@ -31,7 +31,9 @@
 #include <structure.h>
 #include "parser_aux.h"
 #include <gstr.h>
-
+#include "element.h"
+#include <limits.h>
+#include <ctype.h>
 
 extern char _asn1_identifierMissing[];
 
@@ -375,6 +377,16 @@ asn1_internal_delete_element (asn1_node
   if (source_node == NULL)
     return ASN1_ELEMENT_NOT_FOUND;
 
+  if (source_node->parent
+      && source_node->name[0] == '?'
+      && isdigit (source_node->name[1]))
+    {
+      long position = strtol (source_node->name + 1, NULL, 10);
+      if (position > 0 && position < LONG_MAX)
+        _asn1_node_array_set (&source_node->parent->numbered_children,
+                              position - 1, NULL);
+    }
+
   p2 = source_node->right;
   p3 = _asn1_find_left (source_node);
   if (!p3)
Index: libtasn1-3.6/lib/libtasn1.h
===================================================================
--- libtasn1-3.6.orig/lib/libtasn1.h
+++ libtasn1-3.6/lib/libtasn1.h
@@ -52,11 +52,12 @@ extern "C"
 #endif
 #endif
 
-#define LIBTASN1_VERSION "1.5"
+#define LIBTASN1_VERSION "3.6"
 
 #include <sys/types.h>
 #include <time.h>
 
+
 #define MAX_NAME_SIZE 128	/* maximum number of characters of a name */
   /* inside a file with ASN1 definitons     */
 #define MAX_ERROR_DESCRIPTION_SIZE 128	/* maximum number of characters */
