// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_SANDBOX_CHECK_H_
#define V8_SANDBOX_CHECK_H_
#include <optional>
#include "src/sandbox/hardware-support.h"
// When the sandbox is enabled, a SBXCHECK behaves exactly like a CHECK, but
// indicates that the check is required for the sandbox, i.e. prevents a
// sandbox bypass. When the sandbox is off, it becomes a DCHECK.
//
// As an example, consider a scenario where an in-sandbox object stores an
// index into an out-of-sandbox array (or a similar data structure). While
// under normal circumstances it can be guaranteed that the index will always
// be in bounds, with the sandbox attacker model, we have to assume that the
// in-sandbox object can be corrupted by an attacker and so the access can go
// out-of-bounds. In that case, a SBXCHECK can be used to both prevent memory
// corruption outside of the sandbox and document that there is a
// security-critical invariant that may be violated when an attacker can
// corrupt memory inside the sandbox, but otherwise holds true.
#ifdef V8_ENABLE_SANDBOX
// It's unsafe to access sandbox memory during a SBXCHECK since such an access
// will be inherently racy as we need to assume an attacker can modify the value
// inside the sandbox right before and after the check. If you run into this,
// you might want to read the value outside of the SBXCHECK first to ensure that
// the SBXCHECK and the code that relies on it use the same value. And if in
// doubt, feel free to add someone from the security team as a reviewer. If
// sandbox hardware support is enabled, we'll block these accesses temporarily
// in debug builds.
// Wrap DisallowSandboxAccess into optional to allow the CHECK to be used in
// constexpr contexts.
// TODO: Remove it after switching to C++23.
#define SBXCHECK(condition) \
do { \
std::optional<v8::internal::DisallowSandboxAccess> no_sandbox_access; \
if (!std::is_constant_evaluated()) { \
no_sandbox_access.emplace(); \
} \
CHECK(condition); \
} while (false)
#define SBXCHECK_WRAPPED(CONDITION, lhs, rhs) \
do { \
std::optional<v8::internal::DisallowSandboxAccess> no_sandbox_access; \
if (!std::is_constant_evaluated()) { \
no_sandbox_access.emplace(); \
} \
CHECK_##CONDITION(lhs, rhs); \
} while (false)
#define SBXCHECK_EQ(lhs, rhs) SBXCHECK_WRAPPED(EQ, lhs, rhs)
#define SBXCHECK_NE(lhs, rhs) SBXCHECK_WRAPPED(NE, lhs, rhs)
#define SBXCHECK_GT(lhs, rhs) SBXCHECK_WRAPPED(GT, lhs, rhs)
#define SBXCHECK_GE(lhs, rhs) SBXCHECK_WRAPPED(GE, lhs, rhs)
#define SBXCHECK_LT(lhs, rhs) SBXCHECK_WRAPPED(LT, lhs, rhs)
#define SBXCHECK_LE(lhs, rhs) SBXCHECK_WRAPPED(LE, lhs, rhs)
#define SBXCHECK_BOUNDS(index, limit) SBXCHECK_WRAPPED(BOUNDS, index, limit)
#define SBXCHECK_IMPLIES(when, then) SBXCHECK_WRAPPED(IMPLIES, when, then)
#else // V8_ENABLE_SANDBOX
#define SBXCHECK(condition) CHECK(condition)
#define SBXCHECK_EQ(lhs, rhs) CHECK_EQ(lhs, rhs)
#define SBXCHECK_NE(lhs, rhs) CHECK_NE(lhs, rhs)
#define SBXCHECK_GT(lhs, rhs) CHECK_GT(lhs, rhs)
#define SBXCHECK_GE(lhs, rhs) CHECK_GE(lhs, rhs)
#define SBXCHECK_LT(lhs, rhs) CHECK_LT(lhs, rhs)
#define SBXCHECK_LE(lhs, rhs) CHECK_LE(lhs, rhs)
#define SBXCHECK_BOUNDS(index, limit) CHECK_BOUNDS(index, limit)
#define SBXCHECK_IMPLIES(when, then) CHECK_IMPLIES(when, then)
#endif // V8_ENABLE_SANDBOX
#endif // V8_SANDBOX_CHECK_H_