From db0c693b762ea412c9eaa3a2dc2708444dfd4f0a Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 3 Aug 2016 17:46:07 -0400 Subject: [PATCH] Add an API-CONVENTIONS.md document. We're starting to get quite a lot of these ALL-CAPS.md documents. There's been enough questions around how to properly use types like EVP_MD_CTX that we probably should write down some of these common rules. Change-Id: I125f4e82efb168a071b54ff76c5af34c42ff4800 Reviewed-on: https://boringssl-review.googlesource.com/9115 Reviewed-by: Adam Langley --- API-CONVENTIONS.md | 189 +++++++++++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 190 insertions(+) create mode 100644 API-CONVENTIONS.md diff --git a/API-CONVENTIONS.md b/API-CONVENTIONS.md new file mode 100644 index 00000000..11296009 --- /dev/null +++ b/API-CONVENTIONS.md @@ -0,0 +1,189 @@ +# BoringSSL API Conventions + +This document describes conventions for BoringSSL APIs. The [style +guide](/STYLE.md) also includes guidelines, but this document is targeted at +both API consumers and developers. + + +## Documentation + +All supported public APIs are documented in the public header files, found in +`include/openssl`. The API documentation is also available +[online](https://commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html). + +Some headers lack documention comments. These are functions and structures from +OpenSSL's legacy ASN.1, X.509, and PEM implementation. If possible, avoid using +them. These are left largely unmodified from upstream and are retained only for +compatibilty with existing OpenSSL consumers. + + +## Error-handling + +Most functions in BoringSSL may fail, either due to allocation failures or input +errors. Functions which return an `int` typically return one on success and zero +on failure. Functions which return a pointer typically return `NULL` on failure. +However, due to legacy constraints, some functions are more complex. Consult the +API documentation before using a function. + +On error, most functions also push errors on the error queue, an `errno`-like +mechanism. See the documentation for +[err.h](https://commondatastorage.googleapis.com/chromium-boringssl-docs/err.h.html) +for more details. + +As with `errno`, callers must test the function's return value, not the error +queue to determine whether an operation failed. Some codepaths may not interact +with the error queue, and the error queue may have state from a previous failed +operation. + +When ignoring a failed operation, it is recommended to call `ERR_clear_error` to +avoid the state interacting with future operations. Failing to do so should not +affect the actual behavior of any functions, but may result in errors from both +operations being mixed in error logging. We hope to +[improve](https://bugs.chromium.org/p/boringssl/issues/detail?id=38) this +situation in the future. + +Where possible, avoid conditioning on specific reason codes and limit usage to +logging. The reason codes are very specific and may change over time. + + +## Memory allocation + +BoringSSL allocates memory via `OPENSSL_malloc`, found in `mem.h`. Use +`OPENSSL_free`, found in the same header file, to release it. BoringSSL +functions will fail gracefully on allocation error, but it is recommended to use +a `malloc` implementation that `abort`s on failure. + + +## Object initialization and cleanup + +BoringSSL defines a number of structs for use in its APIs. It is a C library, +so the caller is responsible for ensuring these structs are properly +initialized and released. Consult the documentation for a module for the +proper use of its types. Some general conventions are listed below. + + +### Heap-allocated types + +Some types, such as `RSA`, are heap-allocated. All instances will be allocated +and returned from BoringSSL's APIs. It is an error to instantiate a heap- +allocated type on the stack or embedded within another object. + +Heap-allocated types may have functioned named like `RSA_new` which allocates a +fresh blank `RSA`. Other functions may also return newly-allocated instances. +For example, `RSA_parse_public_key` is documented to return a newly-allocated +`RSA` object. + +Heap-allocated objects must be released by the corresponding free function, +named like `RSA_free`. Like C's `free` and C++'s `delete`, all free functions +internally check for `NULL`. Consumers are not required to check for `NULL` +before calling. + +A heap-allocated type may be reference-counted. In this case, a function named +like `RSA_up_ref` will be available to take an additional reference count. The +free function must be called to decrement the reference count. It will only +release resources when the final reference is released. For OpenSSL +compatibility, these functions return `int`, but callers may assume they always +successfully return one because reference counts use saturating arithmetic. + +C++ consumers are recommended to use `std:unique_ptr` with a custom deallocator +to manage heap-allocated objects. + + +### Stack-allocated types + +Other types in BoringSSL are stack-allocated, such as `EVP_MD_CTX`. These +types may be allocated on the stack or embedded within another object. +However, they must still be initialized before use. + +Every stack-allocated object in BoringSSL has a *zero state*, analogous to +initializing a pointer to `NULL`. In this state, the object may not be +completely initialized, but it is safe to call cleanup functions. Entering the +zero state cannot fail. (It is usually `memset(0)`.) + +The function to enter the zero state is named like `EVP_MD_CTX_init` or +`CBB_zero` and will always return `void`. To release resources associated with +the type, call the cleanup function, named like `EVP_MD_CTX_cleanup`. The +cleanup function must be called on all codepaths, regardless of success or +failure. For example: + + uint8_t md[EVP_MAX_MD_SIZE]; + unsigned md_len; + EVP_MD_CTX ctx; + EVP_MD_CTX_init(&ctx); /* Enter the zero state. */ + int ok = EVP_DigestInit_ex(&ctx, EVP_sha256(), NULL) && + EVP_DigestUpdate(&ctx, "hello ", 6) && + EVP_DigestUpdate(&ctx, "world", 5) && + EVP_DigestFinal_ex(&ctx, md, &md_len); + EVP_MD_CTX_cleanup(&ctx); /* Release |ctx|. */ + +Note that `EVP_MD_CTX_cleanup` is called whether or not the `EVP_Digest*` +operations succeeded. More complex C functions may use the `goto err` pattern: + + int ret = 0; + EVP_MD_CTX ctx; + EVP_MD_CTX_init(&ctx); + + if (!some_other_operation()) { + goto err; + } + + uint8_t md[EVP_MAX_MD_SIZE]; + unsigned md_len; + if (!EVP_DigestInit_ex(&ctx, EVP_sha256(), NULL) || + !EVP_DigestUpdate(&ctx, "hello ", 6) || + !EVP_DigestUpdate(&ctx, "world", 5) || + !EVP_DigestFinal_ex(&ctx, md, &md_len) { + goto err; + } + + ret = 1; + + err: + EVP_MD_CTX_cleanup(&ctx); + return ret; + +Note that, because `ctx` is set to the zero state before any failures, +`EVP_MD_CTX_cleanup` is safe to call even if the first operation fails before +`EVP_DigestInit_ex`. However, it would be illegal to move the `EVP_MD_CTX_init` +below the `some_other_operation` call. + +As a rule of thumb, enter the zero state of stack-allocated structs in the +same place they are declared. + +C++ consumers are recommended to implement a type which enters the zero state in +its constructor and calls the cleanup function in the destructor. For example: + + class ScopedEVP_MD_CTX { + public: + ScopedEVP_MD_CTX() { + EVP_MD_CTX_init(&ctx_); + } + + ~ScopedEVP_MD_CTX() { + EVP_MD_CTX_cleanup(&ctx_); + } + + EVP_MD_CTX *get() { return &ctx_; } + + ScopedEVP_MD_CTX(const EVP_MD_CTX &) = delete; + EVP_MD_CTX &operator=(const EVP_MD_CTX &) = delete; + + private: + EVP_MD_CTX ctx_; + }; + + +### Data-only types + +A few types, such as `SHA_CTX`, are data-only types and do not require cleanup. +These are usually for low-level cryptographic operations. These types may be +used freely without special cleanup conventions. + + +## Thread safety + +BoringSSL is internally aware of the platform threading library and calls into +it as needed. Consult the API documentation for the threading guarantees of +particular objects. In general, stateless reference-counted objects like `RSA` +or `EVP_PKEY` which represent keys may typically be used from multiple threads +simultaneously, provided no thread mutates the key. diff --git a/README.md b/README.md index 4caedb76..b28e721c 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ There are other files in this directory which might be helpful: * [PORTING.md](/PORTING.md): how to port OpenSSL-using code to BoringSSL. * [BUILDING.md](/BUILDING.md): how to build BoringSSL * [INCORPORATING.md](/INCORPORATING.md): how to incorporate BoringSSL into a project. + * [API-CONVENTIONS.md](/API-CONVENTIONS.md): general API conventions for BoringSSL consumers and developers. * [STYLE.md](/STYLE.md): rules and guidelines for coding style. * include/openssl: public headers with API documentation in comments. Also [available online](https://commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html). * [FUZZING.md](/FUZZING.md): information about fuzzing BoringSSL.