diff --git a/crypto/err/err.c b/crypto/err/err.c index 4aab75bce..9b6d23816 100644 --- a/crypto/err/err.c +++ b/crypto/err/err.c @@ -745,6 +745,22 @@ void ERR_add_error_dataf(const char *format, ...) { err_set_error_data(buf); } +void ERR_set_error_data(char *data, int flags) { + if (!(flags & ERR_FLAG_STRING)) { + // We do not support non-string error data. + assert(0); + return; + } + if (flags & ERR_FLAG_MALLOCED) { + err_set_error_data(data); + } else { + char *copy = OPENSSL_strdup(data); + if (copy != NULL) { + err_set_error_data(copy); + } + } +} + int ERR_set_mark(void) { ERR_STATE *const state = err_get_state(); diff --git a/crypto/err/err_test.cc b/crypto/err/err_test.cc index b41f8ddfe..5dbe77654 100644 --- a/crypto/err/err_test.cc +++ b/crypto/err/err_test.cc @@ -75,6 +75,17 @@ TEST(ErrTest, PutError) { EXPECT_EQ(1, ERR_GET_LIB(packed_error)); EXPECT_EQ(2, ERR_GET_REASON(packed_error)); EXPECT_STREQ("testing", data); + + ERR_put_error(1, 0 /* unused */, 2, "test", 4); + ERR_set_error_data(const_cast("testing"), ERR_FLAG_STRING); + packed_error = ERR_get_error_line_data(&file, &line, &data, &flags); + EXPECT_STREQ("testing", data); + + ERR_put_error(1, 0 /* unused */, 2, "test", 4); + bssl::UniquePtr str(OPENSSL_strdup("testing")); + ERR_set_error_data(str.release(), ERR_FLAG_STRING | ERR_FLAG_MALLOCED); + packed_error = ERR_get_error_line_data(&file, &line, &data, &flags); + EXPECT_STREQ("testing", data); } TEST(ErrTest, ClearError) { diff --git a/include/openssl/err.h b/include/openssl/err.h index 572340c2d..28ba25070 100644 --- a/include/openssl/err.h +++ b/include/openssl/err.h @@ -183,6 +183,11 @@ OPENSSL_EXPORT uint32_t ERR_get_error_line(const char **file, int *line); // can be printed. This is always set if |data| is non-NULL. #define ERR_FLAG_STRING 1 +// ERR_FLAG_MALLOCED is passed into |ERR_set_error_data| to indicate that |data| +// was allocated with |OPENSSL_malloc|. It is never returned from +// |ERR_get_error_line_data|. +#define ERR_FLAG_MALLOCED 2 + // ERR_get_error_line_data acts like |ERR_get_error_line|, but also returns the // error-specific data pointer and flags. The flags are a bitwise-OR of // |ERR_FLAG_*| values. The error-specific data is owned by the error queue @@ -408,9 +413,10 @@ OPENSSL_EXPORT char *ERR_error_string(uint32_t packed_error, char *buf); // ERR_GET_FUNC returns zero. BoringSSL errors do not report a function code. #define ERR_GET_FUNC(packed_error) 0 -// ERR_TXT_STRING is provided for compatibility with code that assumes that -// it's using OpenSSL. +// ERR_TXT_* are provided for compatibility with code that assumes that it's +// using OpenSSL. #define ERR_TXT_STRING ERR_FLAG_STRING +#define ERR_TXT_MALLOCED ERR_FLAG_MALLOCED // Private functions. @@ -444,6 +450,17 @@ 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); +// ERR_set_error_data sets the data on the most recent error to |data|, which +// must be a NUL-terminated string. |flags| must contain |ERR_FLAG_STRING|. If +// |flags| contains |ERR_FLAG_MALLOCED|, this function takes ownership of +// |data|, which must have been allocated with |OPENSSL_malloc|. Otherwise, it +// saves a copy of |data|. +// +// Note this differs from OpenSSL which, when |ERR_FLAG_MALLOCED| is unset, +// saves the pointer as-is and requires it remain valid for the lifetime of the +// address space. +OPENSSL_EXPORT void ERR_set_error_data(char *data, int flags); + // ERR_NUM_ERRORS is one more than the limit of the number of errors in the // queue. #define ERR_NUM_ERRORS 16