Avoid modifying stack in sk_find.

Bug: 828680
Change-Id: Iae5d0a9bf938a67bfd69a720126ab431d79e43ec
Reviewed-on: https://boringssl-review.googlesource.com/27304
Commit-Queue: Steven Valdez <svaldez@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/asn1/a_strnid.c b/crypto/asn1/a_strnid.c
index 379a79f..efbf0fa 100644
--- a/crypto/asn1/a_strnid.c
+++ b/crypto/asn1/a_strnid.c
@@ -223,6 +223,7 @@
         return ttmp;
     if (!stable)
         return NULL;
+    sk_ASN1_STRING_TABLE_sort(stable);
     found = sk_ASN1_STRING_TABLE_find(stable, &idx, &fnd);
     if (!found)
         return NULL;
diff --git a/crypto/stack/stack.c b/crypto/stack/stack.c
index f6b4412..7aa3218 100644
--- a/crypto/stack/stack.c
+++ b/crypto/stack/stack.c
@@ -223,7 +223,7 @@
   return NULL;
 }
 
-int sk_find(_STACK *sk, size_t *out_index, void *p) {
+int sk_find(const _STACK *sk, size_t *out_index, void *p) {
   if (sk == NULL) {
     return 0;
   }
@@ -245,7 +245,17 @@
     return 0;
   }
 
-  sk_sort(sk);
+  if (!sk_is_sorted(sk)) {
+    for (size_t i = 0; i < sk->num; i++) {
+      if (sk->comp((const void **)&p, (const void **)&sk->data[i]) == 0) {
+        if (out_index) {
+          *out_index = i;
+        }
+        return 1;
+      }
+    }
+    return 0;
+  }
 
   // sk->comp is a function that takes pointers to pointers to elements, but
   // qsort and bsearch take a comparison function that just takes pointers to
diff --git a/crypto/x509/by_dir.c b/crypto/x509/by_dir.c
index 635b851..b3bfffe 100644
--- a/crypto/x509/by_dir.c
+++ b/crypto/x509/by_dir.c
@@ -387,6 +387,7 @@
              */
             CRYPTO_MUTEX_lock_write(&xl->store_ctx->objs_lock);
             tmp = NULL;
+            sk_X509_OBJECT_sort(xl->store_ctx->objs);
             if (sk_X509_OBJECT_find(xl->store_ctx->objs, &idx, &stmp)) {
                 tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, idx);
             }
@@ -404,6 +405,7 @@
                  */
                 if (!hent) {
                     htmp.hash = h;
+                    sk_BY_DIR_HASH_sort(ent->hashes);
                     if (sk_BY_DIR_HASH_find(ent->hashes, &idx, &htmp))
                         hent = sk_BY_DIR_HASH_value(ent->hashes, idx);
                 }
@@ -422,6 +424,7 @@
                         ok = 0;
                         goto finish;
                     }
+                    sk_BY_DIR_HASH_sort(ent->hashes);
                 } else if (hent->suffix < k)
                     hent->suffix = k;
 
diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c
index 1a841db..ea01427 100644
--- a/crypto/x509/x509_lu.c
+++ b/crypto/x509/x509_lu.c
@@ -473,6 +473,7 @@
     }
 
     size_t idx;
+    sk_X509_OBJECT_sort(h);
     if (!sk_X509_OBJECT_find(h, &idx, &stmp))
         return -1;
 
@@ -604,6 +605,7 @@
     size_t idx, i;
     X509_OBJECT *obj;
 
+    sk_X509_OBJECT_sort(h);
     if (!sk_X509_OBJECT_find(h, &idx, x)) {
         return NULL;
     }
diff --git a/crypto/x509/x509_trs.c b/crypto/x509/x509_trs.c
index c7dfcad..f899424 100644
--- a/crypto/x509/x509_trs.c
+++ b/crypto/x509/x509_trs.c
@@ -158,6 +158,7 @@
     tmp.trust = id;
     if (!trtable)
         return -1;
+    sk_X509_TRUST_sort(trtable);
     if (!sk_X509_TRUST_find(trtable, &idx, &tmp)) {
         return -1;
     }
diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c
index 43353c6..84ec838 100644
--- a/crypto/x509/x509_vpm.c
+++ b/crypto/x509/x509_vpm.c
@@ -614,6 +614,7 @@
     } else {
         size_t idx;
 
+        sk_X509_VERIFY_PARAM_sort(param_table);
         if (sk_X509_VERIFY_PARAM_find(param_table, &idx, param)) {
             ptmp = sk_X509_VERIFY_PARAM_value(param_table, idx);
             X509_VERIFY_PARAM_free(ptmp);
@@ -649,6 +650,7 @@
     pm.name = (char *)name;
     if (param_table) {
         size_t idx;
+        sk_X509_VERIFY_PARAM_sort(param_table);
         if (sk_X509_VERIFY_PARAM_find(param_table, &idx, &pm))
             return sk_X509_VERIFY_PARAM_value(param_table, idx);
     }
diff --git a/crypto/x509v3/pcy_cache.c b/crypto/x509v3/pcy_cache.c
index b8a4be2..755c079 100644
--- a/crypto/x509v3/pcy_cache.c
+++ b/crypto/x509v3/pcy_cache.c
@@ -93,6 +93,7 @@
         /*
          * Duplicate policy OIDs are illegal: reject if matches found.
          */
+        sk_X509_POLICY_DATA_sort(cache->data);
         if (OBJ_obj2nid(data->valid_policy) == NID_any_policy) {
             if (cache->anyPolicy) {
                 ret = -1;
@@ -262,6 +263,7 @@
     X509_POLICY_DATA tmp;
 
     tmp.valid_policy = (ASN1_OBJECT *)id;
+    sk_X509_POLICY_DATA_sort(cache->data);
     if (!sk_X509_POLICY_DATA_find(cache->data, &idx, &tmp))
         return NULL;
     return sk_X509_POLICY_DATA_value(cache->data, idx);
diff --git a/crypto/x509v3/pcy_node.c b/crypto/x509v3/pcy_node.c
index b3edfe4..6682282 100644
--- a/crypto/x509v3/pcy_node.c
+++ b/crypto/x509v3/pcy_node.c
@@ -83,6 +83,7 @@
     n.valid_policy = (ASN1_OBJECT *)id;
     l.data = &n;
 
+    sk_X509_POLICY_NODE_sort(nodes);
     if (!sk_X509_POLICY_NODE_find(nodes, &idx, &l))
         return NULL;
 
diff --git a/crypto/x509v3/pcy_tree.c b/crypto/x509v3/pcy_tree.c
index 256fe88..136b45f 100644
--- a/crypto/x509v3/pcy_tree.c
+++ b/crypto/x509v3/pcy_tree.c
@@ -543,9 +543,11 @@
         *pnodes = policy_node_cmp_new();
         if (!*pnodes)
             return 0;
-    } else if (sk_X509_POLICY_NODE_find(*pnodes, NULL, pcy))
+    } else {
+      sk_X509_POLICY_NODE_sort(*pnodes);
+      if (sk_X509_POLICY_NODE_find(*pnodes, NULL, pcy))
         return 1;
-
+    }
     if (!sk_X509_POLICY_NODE_push(*pnodes, pcy))
         return 0;
 
diff --git a/crypto/x509v3/v3_lib.c b/crypto/x509v3/v3_lib.c
index 8f5435d..d5eda3d 100644
--- a/crypto/x509v3/v3_lib.c
+++ b/crypto/x509v3/v3_lib.c
@@ -116,6 +116,7 @@
     if (!ext_list)
         return NULL;
 
+    sk_X509V3_EXT_METHOD_sort(ext_list);
     if (!sk_X509V3_EXT_METHOD_find(ext_list, &idx, &tmp))
         return NULL;
     return sk_X509V3_EXT_METHOD_value(ext_list, idx);
diff --git a/crypto/x509v3/v3_purp.c b/crypto/x509v3/v3_purp.c
index 324de85..f70a804 100644
--- a/crypto/x509v3/v3_purp.c
+++ b/crypto/x509v3/v3_purp.c
@@ -205,6 +205,7 @@
     if (!xptable)
         return -1;
 
+    sk_X509_PURPOSE_sort(xptable);
     if (!sk_X509_PURPOSE_find(xptable, &idx, &tmp))
         return -1;
     return idx + X509_PURPOSE_COUNT;
diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c
index 7d109ee..589e296 100644
--- a/crypto/x509v3/v3_utl.c
+++ b/crypto/x509v3/v3_utl.c
@@ -650,6 +650,7 @@
     if (!*sk)
         return 0;
     /* Don't add duplicates */
+    sk_OPENSSL_STRING_sort(*sk);
     if (sk_OPENSSL_STRING_find(*sk, NULL, (char *)email->data))
         return 1;
     emtmp = BUF_strdup((char *)email->data);
diff --git a/include/openssl/stack.h b/include/openssl/stack.h
index 625f66e..6975db6 100644
--- a/include/openssl/stack.h
+++ b/include/openssl/stack.h
@@ -163,12 +163,17 @@
 OPENSSL_EXPORT void *sk_delete_ptr(_STACK *sk, void *p);
 
 // sk_find returns the first value in the stack equal to |p|. If a comparison
-// function has been set on the stack, then equality is defined by it and the
-// stack will be sorted if need be so that a binary search can be used.
-// Otherwise pointer equality is used. If a matching element is found, its
-// index is written to |*out_index| (if |out_index| is not NULL) and one is
-// returned. Otherwise zero is returned.
-OPENSSL_EXPORT int sk_find(_STACK *sk, size_t *out_index, void *p);
+// function has been set on the stack, equality is defined by it, otherwise
+// pointer equality is used. If the stack is sorted, then a binary search is
+// used, otherwise a linear search is performed. If a matching element is found,
+// its index is written to
+// |*out_index| (if |out_index| is not NULL) and one is returned. Otherwise zero
+// is returned.
+//
+// Note this differs from OpenSSL. The type signature is slightly different, and
+// OpenSSL's sk_find will implicitly sort |sk| if it has a comparison function
+// defined.
+OPENSSL_EXPORT int sk_find(const _STACK *sk, size_t *out_index, void *p);
 
 // sk_shift removes and returns the first element in the stack, or returns NULL
 // if the stack is empty.
@@ -302,8 +307,8 @@
   }                                                                            \
                                                                                \
   static inline OPENSSL_UNUSED int sk_##name##_find(                           \
-      STACK_OF(name) *sk, size_t *out_index, ptrtype p) {                      \
-    return sk_find((_STACK *)sk, out_index, (void *)p);                        \
+      const STACK_OF(name) *sk, size_t *out_index, ptrtype p) {                \
+    return sk_find((const _STACK *)sk, out_index, (void *)p);                  \
   }                                                                            \
                                                                                \
   static inline OPENSSL_UNUSED ptrtype sk_##name##_shift(STACK_OF(name) *sk) { \
diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc
index 5f2f41f..7ade8fc 100644
--- a/ssl/handshake_server.cc
+++ b/ssl/handshake_server.cc
@@ -334,7 +334,7 @@
     SSL_HANDSHAKE *hs, const SSL_CLIENT_HELLO *client_hello,
     const struct ssl_cipher_preference_list_st *server_pref) {
   SSL *const ssl = hs->ssl;
-  STACK_OF(SSL_CIPHER) *prio, *allow;
+  const STACK_OF(SSL_CIPHER) *prio, *allow;
   // in_group_flags will either be NULL, or will point to an array of bytes
   // which indicate equal-preference groups in the |prio| stack. See the
   // comment about |in_group_flags| in the |ssl_cipher_preference_list_st|
diff --git a/ssl/ssl_file.cc b/ssl/ssl_file.cc
index bafa64a..ca4b0be 100644
--- a/ssl/ssl_file.cc
+++ b/ssl/ssl_file.cc
@@ -165,6 +165,7 @@
     }
 
     // Check for duplicates.
+    sk_X509_NAME_sort(sk);
     if (sk_X509_NAME_find(sk, NULL, xn)) {
       continue;
     }
@@ -223,6 +224,7 @@
     }
 
     // Check for duplicates.
+    sk_X509_NAME_sort(stack);
     if (sk_X509_NAME_find(stack, NULL, xn)) {
       continue;
     }