current commit
261007.21 · deps: update V8 to 15.5.35.20
// Copyright 2023 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_OBJECTS_TRUSTED_OBJECT_H_
#define V8_OBJECTS_TRUSTED_OBJECT_H_

#include "src/objects/heap-object.h"

// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"

namespace v8 {
namespace internal {

// An object that is trusted to not have been modified in a malicious way.
//
// Typical examples of trusted objects are containers for bytecode or code
// metadata, which often allow an attacker to corrupt (for example) stack
// memory when manipulated. When the sandbox is enabled, trusted objects are
// located outside of the sandbox (in one of the trusted heap spaces) so that
// attackers cannot corrupt these objects and use them to escape from the
// sandbox. When the sandbox is disabled, trusted objects are treated like any
// other objects since in that case, many other types of objects (for example
// ArrayBuffers) can be used to corrupt memory outside of V8's heaps as well.
//
// Trusted objects cannot directly be referenced from untrusted objects as this
// would be unsafe: an attacker could corrupt any (direct) pointer to these
// objects stored inside the sandbox. However, ExposedTrustedObject can be
// referenced via indirect pointers, which guarantee memory-safe access.
V8_OBJECT class TrustedObject : public HeapObject {
  V8_IT_ABSTRACT;

 public:
  DECL_VERIFIER(TrustedObject)

  // Protected pointers.
  //
  // These are pointers for which it is guaranteed that neither the pointer-to
  // object nor the pointer itself can be modified by an attacker. In practice,
  // this means that they must be pointers between objects in trusted space,
  // outside of the sandbox, where they are protected from an attacker. As
  // such, the slot accessors for these slots only exist on TrustedObjects but
  // not on other HeapObjects.
  template <typename T = TrustedObject>
  inline Tagged<T> ReadProtectedPointerField(int offset) const;
  template <typename T = TrustedObject>
  inline Tagged<T> ReadProtectedPointerField(int offset, AcquireLoadTag) const;
  inline void WriteProtectedPointerField(int offset,
                                         Tagged<TrustedObject> value);
  inline void WriteProtectedPointerField(int offset,
                                         Tagged<TrustedObject> value,
                                         ReleaseStoreTag);
  inline bool IsProtectedPointerFieldEmpty(int offset) const;
  inline bool IsProtectedPointerFieldEmpty(int offset, AcquireLoadTag) const;
  inline void ClearProtectedPointerField(int offset);
  inline void ClearProtectedPointerField(int offset, ReleaseStoreTag);

  inline ProtectedPointerSlot RawProtectedPointerField(int byte_offset) const;
  inline ProtectedMaybeObjectSlot RawProtectedMaybeObjectField(
      int byte_offset) const;

#ifdef VERIFY_HEAP
  inline void VerifyProtectedPointerField(Isolate* isolate, int offset);
#endif

  // Back-compat alias: TrustedObject adds no fields on top of HeapObject.
  static constexpr int kHeaderSize = sizeof(HeapObject);
} V8_OBJECT_END;

// A trusted object that can safely be referenced from untrusted objects.
//
// These objects live in trusted space but are "exposed" to untrusted objects
// living inside the sandbox. They still cannot be referenced through "direct"
// pointers (these can be corrupted by an attacker), but instead they must be
// referenced through "indirect pointers": an index into a pointer table that
// contains the actual pointer as well as a type tag. This mechanism then
// guarantees memory-safe access.
//
// We want to have one pointer table entry per referenced object, *not* per
// reference. As such, there must be a way to obtain an existing table entry
// for a given (exposed) object. This base class provides that table entry in
// the form of the 'self' indirect pointer.
//
// The need to inherit from this base class to make a trusted object accessible
// means that it is not possible to expose existing utility objects such as
// hash tables or fixed arrays. Instead, those would need to be "wrapped" by
// another ExposedTrustedObject. This limitation is by design: if we were to
// create such an exposed utility object, it would likely weaken the
// type-safety mechanism of indirect pointers because indirect pointers are
// (effectively) tagged with the target's instance type. As such, if the same
// object type is used in different contexts, they would both use the same type
// tag, allowing an attacker to perform a "substitution attack". As a concrete
// example, consider the case of a trusted, exposed byte array. If such a byte
// array is used (a) to hold some sort of bytecode for an interpreter and (b)
// some sort of trusted metadata, then an attacker can take a trusted byte
// array from context (a) and use it in context (b) or vice versa. This would
// effectively result in a type confusion and likely lead to an escape from the
// sandbox. This problem goes away if (a) and (b) each use a dedicated object
// with a unique instance type. It is of course still possible to build new
// utility objects on top of this class, but hopefully this comment serves to
// document the potential pitfalls when doing so.
V8_OBJECT class ExposedTrustedObject : public TrustedObject {
  V8_IT_ABSTRACT;

 public:
  // Initializes this object by allocating its pointer table entry. This
  // initializer function immediately makes the trusted object accessible from
  // within the sandbox ("publishes" it). If that is undesirable (e.g. because
  // the object may be in an inconsistent state or require addition
  // validation), use the InitDontPublish variant instead.
  inline void InitAndPublish(Isolate* isolate);
  inline void InitAndPublish(LocalIsolate* isolate);

  // Initializes this object by allocating its pointer table entry, but doesn't
  // yet make the object accessible from within the sandbox. This is achieved
  // by using a special "unpublished" tag for the table entry, which causes any
  // access to the object via the table (so from within the sandbox) to fail
  // the type check. The object will need to be published manually before it
  // can be used by calling the Publish method.
  inline void InitDontPublish(Isolate* isolate);
  inline void InitDontPublish(LocalIsolate* isolate);

  // Publishes this object so it becomes accessible from within the sandbox.
  //
  // This is only needed if the object was initialized without publishing it,
  // in which case its pointer table entry will not be usable (as it uses an
  // invalid type tag) until this method is called.
  inline void Publish(Isolate* isolate);
  inline void Publish(IsolateForSandbox isolate);

  // Undoes earlier publishing of this object, making it inaccessible from
  // within the sandbox.
  // This is recommended when a trusted object is no longer used, e.g. the old
  // backing store of a growable table.
  inline void Unpublish(IsolateForSandbox isolate);

  // Returns true if this trusted object is "published", i.e. accessible from
  // within the sandbox via the trusted pointer table.
  inline bool IsPublished(IsolateForSandbox isolate) const;

  // Returns the 'self' indirect pointer of this object.
  // This indirect pointer references a pointer table entry (either in the
  // trusted pointer table or the code pointer table for Code objects) through
  // which this object can be referenced from inside the sandbox.
  inline IndirectPointerHandle self_indirect_pointer_handle() const;

#if V8_ENABLE_SANDBOX
  inline void InitSelfIndirectPointerField(
      std::atomic<IndirectPointerHandle>* field_ptr, IsolateForSandbox isolate,
      TrustedPointerPublishingScope* opt_publishing_scope);
#endif  // V8_ENABLE_SANDBOX

  DECL_VERIFIER(ExposedTrustedObject)

#ifdef V8_ENABLE_SANDBOX
#endif  // V8_ENABLE_SANDBOX
  static const int kHeaderSize;

 private:
  friend class TorqueGeneratedExposedTrustedObjectAsserts;

 public:
#ifdef V8_ENABLE_SANDBOX
  // The 'self' indirect pointer is only available when the sandbox is enabled.
  // Otherwise, these objects are referenced through direct pointers.
  std::atomic<IndirectPointerHandle> self_indirect_pointer_;
#endif  // V8_ENABLE_SANDBOX
} V8_OBJECT_END;

#ifdef V8_ENABLE_SANDBOX
#endif  // V8_ENABLE_SANDBOX
inline constexpr int ExposedTrustedObject::kHeaderSize =
    sizeof(ExposedTrustedObject);

}  // namespace internal
}  // namespace v8

#include "src/objects/object-macros-undef.h"

#endif  // V8_OBJECTS_TRUSTED_OBJECT_H_