Add the ability to save and restore the error state.

This will be useful for the SSL stack to properly resurface handshake
failures. Leave this in a private header and, along the way, hide the
various types.

(ERR_NUM_ERRORS didn't change in meaning. The old documentation was
wrong.)

Bug: 206
Change-Id: I4c6ca98d162d11ad5e17e4baf439a18fbe371018
Reviewed-on: https://boringssl-review.googlesource.com/21284
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Steven Valdez <svaldez@google.com>
diff --git a/crypto/err/err.c b/crypto/err/err.c
index a620fc7..c7bff16 100644
--- a/crypto/err/err.c
+++ b/crypto/err/err.c
@@ -123,8 +123,39 @@
 #include <openssl/thread.h>
 
 #include "../internal.h"
+#include "./internal.h"
 
 
+struct err_error_st {
+  // file contains the filename where the error occurred.
+  const char *file;
+  // data contains a NUL-terminated string with optional data. It must be freed
+  // with |OPENSSL_free|.
+  char *data;
+  // packed contains the error library and reason, as packed by ERR_PACK.
+  uint32_t packed;
+  // line contains the line number where the error occurred.
+  uint16_t line;
+  // mark indicates a reversion point in the queue. See |ERR_pop_to_mark|.
+  unsigned mark : 1;
+};
+
+// ERR_STATE contains the per-thread, error queue.
+typedef struct err_state_st {
+  // errors contains the ERR_NUM_ERRORS most recent errors, organised as a ring
+  // buffer.
+  struct err_error_st errors[ERR_NUM_ERRORS];
+  // top contains the index one past the most recent error. If |top| equals
+  // |bottom| then the queue is empty.
+  unsigned top;
+  // bottom contains the index of the last error in the queue.
+  unsigned bottom;
+
+  // to_free, if not NULL, contains a pointer owned by this structure that was
+  // previously a |data| pointer of one of the elements of |errors|.
+  void *to_free;
+} ERR_STATE;
+
 extern const uint32_t kOpenSSLReasonValues[];
 extern const size_t kOpenSSLReasonValuesLen;
 extern const char kOpenSSLReasonStringData[];
@@ -135,6 +166,16 @@
   OPENSSL_memset(error, 0, sizeof(struct err_error_st));
 }
 
+static void err_copy(struct err_error_st *dst, const struct err_error_st *src) {
+  err_clear(dst);
+  dst->file = src->file;
+  if (src->data != NULL) {
+    dst->data = OPENSSL_strdup(src->data);
+  }
+  dst->packed = src->packed;
+  dst->line = src->line;
+}
+
 // global_next_library contains the next custom library value to return.
 static int global_next_library = ERR_NUM_LIBS;
 
@@ -150,8 +191,7 @@
     return;
   }
 
-  unsigned i;
-  for (i = 0; i < ERR_NUM_ERRORS; i++) {
+  for (unsigned i = 0; i < ERR_NUM_ERRORS; i++) {
     err_clear(&state->errors[i]);
   }
   OPENSSL_free(state->to_free);
@@ -740,3 +780,68 @@
 void ERR_load_BIO_strings(void) {}
 
 void ERR_load_ERR_strings(void) {}
+
+struct err_save_state_st {
+  struct err_error_st *errors;
+  size_t num_errors;
+};
+
+void ERR_SAVE_STATE_free(ERR_SAVE_STATE *state) {
+  if (state == NULL) {
+    return;
+  }
+  for (size_t i = 0; i < state->num_errors; i++) {
+    err_clear(&state->errors[i]);
+  }
+  OPENSSL_free(state->errors);
+  OPENSSL_free(state);
+}
+
+ERR_SAVE_STATE *ERR_save_state(void) {
+  ERR_STATE *const state = err_get_state();
+  if (state == NULL || state->top == state->bottom) {
+    return NULL;
+  }
+
+  ERR_SAVE_STATE *ret = OPENSSL_malloc(sizeof(ERR_SAVE_STATE));
+  if (ret == NULL) {
+    return NULL;
+  }
+
+  // Errors are stored in the range (bottom, top].
+  size_t num_errors = state->top >= state->bottom
+                          ? state->top - state->bottom
+                          : ERR_NUM_ERRORS + state->top - state->bottom;
+  assert(num_errors < ERR_NUM_ERRORS);
+  ret->errors = OPENSSL_malloc(num_errors * sizeof(struct err_error_st));
+  if (ret->errors == NULL) {
+    OPENSSL_free(ret);
+    return NULL;
+  }
+  OPENSSL_memset(ret->errors, 0, num_errors * sizeof(struct err_error_st));
+  ret->num_errors = num_errors;
+
+  for (size_t i = 0; i < num_errors; i++) {
+    size_t j = (state->bottom + i + 1) % ERR_NUM_ERRORS;
+    err_copy(&ret->errors[i], &state->errors[j]);
+  }
+  return ret;
+}
+
+void ERR_restore_state(const ERR_SAVE_STATE *state) {
+  if (state == NULL || state->num_errors == 0) {
+    ERR_clear_error();
+    return;
+  }
+
+  ERR_STATE *const dst = err_get_state();
+  if (dst == NULL) {
+    return;
+  }
+
+  for (size_t i = 0; i < state->num_errors; i++) {
+    err_copy(&dst->errors[i], &state->errors[i]);
+  }
+  dst->top = state->num_errors - 1;
+  dst->bottom = ERR_NUM_ERRORS - 1;
+}
diff --git a/crypto/err/err_test.cc b/crypto/err/err_test.cc
index 5d04ae2..489d248 100644
--- a/crypto/err/err_test.cc
+++ b/crypto/err/err_test.cc
@@ -21,6 +21,8 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
+#include "./internal.h"
+
 
 TEST(ErrTest, Overflow) {
   for (unsigned i = 0; i < ERR_NUM_ERRORS*2; i++) {
@@ -119,3 +121,94 @@
   EXPECT_EQ(ERR_LIB_USER, ERR_GET_LIB(error));
   EXPECT_EQ(ERR_R_INTERNAL_ERROR, ERR_GET_REASON(error));
 }
+
+TEST(ErrTest, SaveAndRestore) {
+  // Restoring no state clears the error queue, including error data.
+  ERR_put_error(1, 0 /* unused */, 1, "test1.c", 1);
+  ERR_put_error(2, 0 /* unused */, 2, "test2.c", 2);
+  ERR_add_error_data(1, "data1");
+  ERR_restore_state(nullptr);
+  EXPECT_EQ(0u, ERR_get_error());
+
+  // Add some entries to the error queue and save it.
+  ERR_put_error(1, 0 /* unused */, 1, "test1.c", 1);
+  ERR_add_error_data(1, "data1");
+  ERR_put_error(2, 0 /* unused */, 2, "test2.c", 2);
+  ERR_put_error(3, 0 /* unused */, 3, "test3.c", 3);
+  ERR_add_error_data(1, "data3");
+  bssl::UniquePtr<ERR_SAVE_STATE> saved(ERR_save_state());
+  ASSERT_TRUE(saved);
+
+  // The existing error queue entries still exist.
+  int line, flags;
+  const char *file, *data;
+  uint32_t packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
+  EXPECT_EQ(ERR_GET_LIB(packed_error), 1);
+  EXPECT_EQ(ERR_GET_REASON(packed_error), 1);
+  EXPECT_STREQ("test1.c", file);
+  EXPECT_EQ(line, 1);
+  EXPECT_STREQ(data, "data1");
+  EXPECT_EQ(flags, ERR_FLAG_STRING);
+
+  // The state may be restored, both over an empty and non-empty state.
+  for (unsigned i = 0; i < 2; i++) {
+    SCOPED_TRACE(i);
+    ERR_restore_state(saved.get());
+
+    packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
+    EXPECT_EQ(ERR_GET_LIB(packed_error), 1);
+    EXPECT_EQ(ERR_GET_REASON(packed_error), 1);
+    EXPECT_STREQ("test1.c", file);
+    EXPECT_EQ(line, 1);
+    EXPECT_STREQ(data, "data1");
+    EXPECT_EQ(flags, ERR_FLAG_STRING);
+
+    packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
+    EXPECT_EQ(ERR_GET_LIB(packed_error), 2);
+    EXPECT_EQ(ERR_GET_REASON(packed_error), 2);
+    EXPECT_STREQ("test2.c", file);
+    EXPECT_EQ(line, 2);
+    EXPECT_STREQ(data, "");  // No error data is reported as the empty string.
+    EXPECT_EQ(flags, 0);
+
+    packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
+    EXPECT_EQ(ERR_GET_LIB(packed_error), 3);
+    EXPECT_EQ(ERR_GET_REASON(packed_error), 3);
+    EXPECT_STREQ("test3.c", file);
+    EXPECT_EQ(line, 3);
+    EXPECT_STREQ(data, "data3");
+    EXPECT_EQ(flags, ERR_FLAG_STRING);
+
+    // The error queue is now empty for the next iteration.
+    EXPECT_EQ(0u, ERR_get_error());
+  }
+
+  // Test a case where the error queue wraps around. The first set of errors
+  // will all be discarded, but result in wrapping the list around.
+  ERR_clear_error();
+  for (unsigned i = 0; i < ERR_NUM_ERRORS / 2; i++) {
+    ERR_put_error(0, 0 /* unused */, 0, "invalid", 0);
+  }
+  for (unsigned i = 1; i < ERR_NUM_ERRORS; i++) {
+    ERR_put_error(i, 0 /* unused */, i, "test", i);
+  }
+  saved.reset(ERR_save_state());
+
+  // The state may be restored, both over an empty and non-empty state. Pop one
+  // error off so the first iteration is tested to not be a no-op.
+  ERR_get_error();
+  for (int i = 0; i < 2; i++) {
+    SCOPED_TRACE(i);
+    ERR_restore_state(saved.get());
+    for (int j = 1; j < ERR_NUM_ERRORS; j++) {
+      SCOPED_TRACE(j);
+      packed_error = ERR_get_error_line_data(&file, &line, &data, &flags);
+      EXPECT_EQ(ERR_GET_LIB(packed_error), j);
+      EXPECT_EQ(ERR_GET_REASON(packed_error), j);
+      EXPECT_STREQ("test", file);
+      EXPECT_EQ(line, j);
+    }
+    // The error queue is now empty for the next iteration.
+    EXPECT_EQ(0u, ERR_get_error());
+  }
+}
diff --git a/crypto/err/internal.h b/crypto/err/internal.h
new file mode 100644
index 0000000..3f2397c
--- /dev/null
+++ b/crypto/err/internal.h
@@ -0,0 +1,58 @@
+/* Copyright (c) 2017, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef OPENSSL_HEADER_CRYPTO_ERR_INTERNAL_H
+#define OPENSSL_HEADER_CRYPTO_ERR_INTERNAL_H
+
+#include <openssl/err.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+// Private error queue functions.
+
+// ERR_SAVE_STATE contains a saved representation of the error queue. It is
+// slightly more compact than |ERR_STATE| as the error queue will typically not
+// contain |ERR_NUM_ERRORS| entries.
+typedef struct err_save_state_st ERR_SAVE_STATE;
+
+// ERR_SAVE_STATE_free releases all memory associated with |state|.
+OPENSSL_EXPORT void ERR_SAVE_STATE_free(ERR_SAVE_STATE *state);
+
+// ERR_save_state returns a newly-allocated |ERR_SAVE_STATE| structure
+// containing the current state of the error queue or NULL on allocation
+// error. It should be released with |ERR_SAVE_STATE_free|.
+OPENSSL_EXPORT ERR_SAVE_STATE *ERR_save_state(void);
+
+// ERR_restore_state clears the error queue and replaces it with |state|.
+OPENSSL_EXPORT void ERR_restore_state(const ERR_SAVE_STATE *state);
+
+
+#if defined(__cplusplus)
+}  // extern C
+
+extern "C++" {
+
+namespace bssl {
+
+BORINGSSL_MAKE_DELETER(ERR_SAVE_STATE, ERR_SAVE_STATE_free)
+
+}  // namespace bssl
+
+}  // extern C++
+#endif
+
+#endif  // OPENSSL_HEADER_CRYPTO_ERR_INTERNAL_H
diff --git a/include/openssl/err.h b/include/openssl/err.h
index 5de3cbd..9a65ffb 100644
--- a/include/openssl/err.h
+++ b/include/openssl/err.h
@@ -436,39 +436,10 @@
 OPENSSL_EXPORT void ERR_add_error_dataf(const char *format, ...)
     OPENSSL_PRINTF_FORMAT_FUNC(1, 2);
 
-struct err_error_st {
-  // file contains the filename where the error occurred.
-  const char *file;
-  // data contains a NUL-terminated string with optional data. It must be freed
-  // with |OPENSSL_free|.
-  char *data;
-  // packed contains the error library and reason, as packed by ERR_PACK.
-  uint32_t packed;
-  // line contains the line number where the error occurred.
-  uint16_t line;
-  // mark indicates a reversion point in the queue. See |ERR_pop_to_mark|.
-  unsigned mark : 1;
-};
-
-// ERR_NUM_ERRORS is the limit of the number of errors in the queue.
+// ERR_NUM_ERRORS is one more than the limit of the number of errors in the
+// queue.
 #define ERR_NUM_ERRORS 16
 
-// err_state_st (aka |ERR_STATE|) contains the per-thread, error queue.
-typedef struct err_state_st {
-  // errors contains the ERR_NUM_ERRORS most recent errors, organised as a ring
-  // buffer.
-  struct err_error_st errors[ERR_NUM_ERRORS];
-  // top contains the index one past the most recent error. If |top| equals
-  // |bottom| then the queue is empty.
-  unsigned top;
-  // bottom contains the index of the last error in the queue.
-  unsigned bottom;
-
-  // to_free, if not NULL, contains a pointer owned by this structure that was
-  // previously a |data| pointer of one of the elements of |errors|.
-  void *to_free;
-} ERR_STATE;
-
 #define ERR_PACK(lib, reason)                                              \
   (((((uint32_t)(lib)) & 0xff) << 24) | ((((uint32_t)(reason)) & 0xfff)))