1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_CONTEXT_H_ 6 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_CONTEXT_H_ 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 #include "base/compiler_specific.h" 12 #include "base/macros.h" 13 #include "base/strings/string_piece.h" 14 #include "mojo/public/cpp/bindings/bindings_export.h" 15 #include "mojo/public/cpp/bindings/lib/bindings_internal.h" 16 17 static const int kMaxRecursionDepth = 100; 18 19 namespace mojo { 20 21 class Message; 22 23 namespace internal { 24 25 // ValidationContext is used when validating object sizes, pointers and handle 26 // indices in the payload of incoming messages. 27 class MOJO_CPP_BINDINGS_EXPORT ValidationContext { 28 public: 29 // [data, data + data_num_bytes) specifies the initial valid memory range. 30 // [0, num_handles) specifies the initial valid range of handle indices. 31 // [0, num_associated_endpoint_handles) specifies the initial valid range of 32 // associated endpoint handle indices. 33 // 34 // If provided, |message| and |description| provide additional information 35 // to use when reporting validation errors. In addition if |message| is 36 // provided, the MojoNotifyBadMessage API will be used to notify the system of 37 // such errors. 38 ValidationContext(const void* data, 39 size_t data_num_bytes, 40 size_t num_handles, 41 size_t num_associated_endpoint_handles, 42 Message* message = nullptr, 43 const base::StringPiece& description = "", 44 int stack_depth = 0); 45 46 ~ValidationContext(); 47 48 // Claims the specified memory range. 49 // The method succeeds if the range is valid to claim. (Please see 50 // the comments for IsValidRange().) 51 // On success, the valid memory range is shrinked to begin right after the end 52 // of the claimed range. 53 bool ClaimMemory(const void* position, uint32_t num_bytes) { 54 uintptr_t begin = reinterpret_cast<uintptr_t>(position); 55 uintptr_t end = begin + num_bytes; 56 57 if (!InternalIsValidRange(begin, end)) 58 return false; 59 60 data_begin_ = end; 61 return true; 62 } 63 64 // Claims the specified encoded handle (which is basically a handle index). 65 // The method succeeds if: 66 // - |encoded_handle|'s value is |kEncodedInvalidHandleValue|. 67 // - the handle is contained inside the valid range of handle indices. In this 68 // case, the valid range is shinked to begin right after the claimed handle. 69 bool ClaimHandle(const Handle_Data& encoded_handle) { 70 uint32_t index = encoded_handle.value; 71 if (index == kEncodedInvalidHandleValue) 72 return true; 73 74 if (index < handle_begin_ || index >= handle_end_) 75 return false; 76 77 // |index| + 1 shouldn't overflow, because |index| is not the max value of 78 // uint32_t (it is less than |handle_end_|). 79 handle_begin_ = index + 1; 80 return true; 81 } 82 83 // Claims the specified encoded associated endpoint handle. 84 // The method succeeds if: 85 // - |encoded_handle|'s value is |kEncodedInvalidHandleValue|. 86 // - the handle is contained inside the valid range of associated endpoint 87 // handle indices. In this case, the valid range is shinked to begin right 88 // after the claimed handle. 89 bool ClaimAssociatedEndpointHandle( 90 const AssociatedEndpointHandle_Data& encoded_handle) { 91 uint32_t index = encoded_handle.value; 92 if (index == kEncodedInvalidHandleValue) 93 return true; 94 95 if (index < associated_endpoint_handle_begin_ || 96 index >= associated_endpoint_handle_end_) 97 return false; 98 99 // |index| + 1 shouldn't overflow, because |index| is not the max value of 100 // uint32_t (it is less than |associated_endpoint_handle_end_|). 101 associated_endpoint_handle_begin_ = index + 1; 102 return true; 103 } 104 105 // Returns true if the specified range is not empty, and the range is 106 // contained inside the valid memory range. 107 bool IsValidRange(const void* position, uint32_t num_bytes) const { 108 uintptr_t begin = reinterpret_cast<uintptr_t>(position); 109 uintptr_t end = begin + num_bytes; 110 111 return InternalIsValidRange(begin, end); 112 } 113 114 // This object should be created on the stack once every time we recurse down 115 // into a subfield during validation to make sure we don't recurse too deep 116 // and blow the stack. 117 class ScopedDepthTracker { 118 public: 119 // |ctx| must outlive this object. 120 explicit ScopedDepthTracker(ValidationContext* ctx) : ctx_(ctx) { 121 ++ctx_->stack_depth_; 122 } 123 124 ~ScopedDepthTracker() { --ctx_->stack_depth_; } 125 126 private: 127 ValidationContext* ctx_; 128 129 DISALLOW_COPY_AND_ASSIGN(ScopedDepthTracker); 130 }; 131 132 // Returns true if the recursion depth limit has been reached. 133 bool ExceedsMaxDepth() WARN_UNUSED_RESULT { 134 return stack_depth_ > kMaxRecursionDepth; 135 } 136 137 Message* message() const { return message_; } 138 const base::StringPiece& description() const { return description_; } 139 140 private: 141 bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const { 142 return end > begin && begin >= data_begin_ && end <= data_end_; 143 } 144 145 Message* const message_; 146 const base::StringPiece description_; 147 148 // [data_begin_, data_end_) is the valid memory range. 149 uintptr_t data_begin_; 150 uintptr_t data_end_; 151 152 // [handle_begin_, handle_end_) is the valid handle index range. 153 uint32_t handle_begin_; 154 uint32_t handle_end_; 155 156 // [associated_endpoint_handle_begin_, associated_endpoint_handle_end_) is the 157 // valid associated endpoint handle index range. 158 uint32_t associated_endpoint_handle_begin_; 159 uint32_t associated_endpoint_handle_end_; 160 161 int stack_depth_; 162 163 DISALLOW_COPY_AND_ASSIGN(ValidationContext); 164 }; 165 166 } // namespace internal 167 } // namespace mojo 168 169 #endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_CONTEXT_H_ 170