// Copyright 2013 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifdef UNSAFE_BUFFERS_BUILD // TODO(crbug.com/40284755): Remove this and spanify to fix the errors. #pragma allow_unsafe_buffers #endif #include "base/strings/safe_sprintf.h" #include <errno.h> #include <string.h> #include <algorithm> #include <limits> #include "base/memory/raw_ptr.h" #include "build/build_config.h" #if !defined(NDEBUG) // In debug builds, we use RAW_CHECK() to print useful error messages, if // SafeSPrintf() is called with broken arguments. // As our contract promises that SafeSPrintf() can be called from any // restricted run-time context, it is not actually safe to call logging // functions from it; and we only ever do so for debug builds and hope for the // best. We should _never_ call any logging function other than RAW_CHECK(), // and we should _never_ include any logging code that is active in production // builds. Most notably, we should not include these logging functions in // unofficial release builds, even though those builds would otherwise have // DCHECKS() enabled. // In other words; please do not remove the #ifdef around this #include. // Instead, in production builds we opt for returning a degraded result, // whenever an error is encountered. // E.g. The broken function call // SafeSPrintf("errno = %d (%x)", errno, strerror(errno)) // will print something like // errno = 13, (%x) // instead of // errno = 13 (Access denied) // In most of the anticipated use cases, that's probably the preferred // behavior. #include "base/check.h" #define DEBUG_CHECK … #else #define DEBUG_CHECK … #endif namespace base { namespace strings { // The code in this file is extremely careful to be async-signal-safe. // // Most obviously, we avoid calling any code that could dynamically allocate // memory. Doing so would almost certainly result in bugs and dead-locks. // We also avoid calling any other STL functions that could have unintended // side-effects involving memory allocation or access to other shared // resources. // // But on top of that, we also avoid calling other library functions, as many // of them have the side-effect of calling getenv() (in order to deal with // localization) or accessing errno. The latter sounds benign, but there are // several execution contexts where it isn't even possible to safely read let // alone write errno. // // The stated design goal of the SafeSPrintf() function is that it can be // called from any context that can safely call C or C++ code (i.e. anything // that doesn't require assembly code). // // For a brief overview of some but not all of the issues with async-signal- // safety, refer to: // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html namespace { const size_t kSSizeMaxConst = …; const char kUpCaseHexDigits[] = …; const char kDownCaseHexDigits[] = …; } #if defined(NDEBUG) // We would like to define kSSizeMax as std::numeric_limits<ssize_t>::max(), // but C++ doesn't allow us to do that for constants. Instead, we have to // use careful casting and shifting. We later use a static_assert to // verify that this worked correctly. namespace { const size_t kSSizeMax = kSSizeMaxConst; } #else // defined(NDEBUG) // For efficiency, we really need kSSizeMax to be a constant. But for unit // tests, it should be adjustable. This allows us to verify edge cases without // having to fill the entire available address space. As a compromise, we make // kSSizeMax adjustable in debug builds, and then only compile that particular // part of the unit test in debug builds. namespace { static size_t kSSizeMax = …; } namespace internal { void SetSafeSPrintfSSizeMaxForTest(size_t max) { … } size_t GetSafeSPrintfSSizeMaxForTest() { … } } #endif // defined(NDEBUG) namespace { class Buffer { … }; bool Buffer::IToASCII(bool sign, bool upcase, int64_t i, size_t base, char pad, size_t padding, const char* prefix) { … } } // anonymous namespace namespace internal { ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt, const Arg* args, const size_t max_args) { … } } // namespace internal ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt) { … } } // namespace strings } // namespace base