소스 검색

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>
kris/onging/CECPQ3_patch15
David Benjamin 7 년 전
committed by CQ bot account: commit-bot@chromium.org
부모
커밋
b25a8999be
4개의 변경된 파일260개의 추가작업 그리고 33개의 파일을 삭제
  1. +107
    -2
      crypto/err/err.c
  2. +93
    -0
      crypto/err/err_test.cc
  3. +58
    -0
      crypto/err/internal.h
  4. +2
    -31
      include/openssl/err.h

+ 107
- 2
crypto/err/err.c 파일 보기

@@ -123,7 +123,38 @@ OPENSSL_MSVC_PRAGMA(warning(pop))
#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;
@@ -135,6 +166,16 @@ static void err_clear(struct err_error_st *error) {
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 @@ static void err_state_free(void *statep) {
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_free_strings(void) {}
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;
}

+ 93
- 0
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 @@ TEST(ErrTest, PutMacro) {
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());
}
}

+ 58
- 0
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

+ 2
- 31
include/openssl/err.h 파일 보기

@@ -436,39 +436,10 @@ OPENSSL_EXPORT void ERR_add_error_data(unsigned count, ...);
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)))



불러오는 중...
취소
저장