1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <string.h> 18 #include <memory.h> 19 20 #include <atomic> 21 #include <cstdint> 22 23 namespace android::app::PropertyInvalidatedCache { 24 25 /** 26 * A head of a CacheNonce object. This contains all the fields that have a fixed size and 27 * location. Fields with a variable location are found via offsets. The offsets make this 28 * object position-independent, which is required because it is in shared memory and would be 29 * mapped into different virtual addresses for different processes. 30 */ 31 class NonceStore { 32 protected: 33 // A convenient typedef. The jbyteArray element type is jbyte, which the compiler treats as 34 // signed char. 35 typedef signed char block_t; 36 37 // The nonce type. 38 typedef std::atomic<int64_t> nonce_t; 39 40 // Atomics should be safe to use across processes if they are lock free. 41 static_assert(nonce_t::is_always_lock_free == true); 42 43 // The value of an unset field. 44 static constexpr int UNSET = 0; 45 46 // The size of the nonce array. 47 const int32_t kMaxNonce; 48 49 // The size of the byte array. 50 const size_t kMaxByte; 51 52 // The offset to the nonce array. 53 const size_t mNonceOffset; 54 55 // The offset to the byte array. 56 const size_t mByteOffset; 57 58 // The byte block hash. This is fixed and at a known offset, so leave it in the base class. 59 volatile std::atomic<int32_t> mByteHash; 60 61 // The constructor is protected! It only makes sense when called from a subclass. NonceStore(int kMaxNonce,size_t kMaxByte,volatile nonce_t * nonce,volatile block_t * block)62 NonceStore(int kMaxNonce, size_t kMaxByte, volatile nonce_t* nonce, volatile block_t* block) : 63 kMaxNonce(kMaxNonce), 64 kMaxByte(kMaxByte), 65 mNonceOffset(offset(this, const_cast<nonce_t*>(nonce))), 66 mByteOffset(offset(this, const_cast<block_t*>(block))) { 67 } 68 69 public: 70 71 // These provide run-time access to the sizing parameters. 72 int getMaxNonce() const; 73 size_t getMaxByte() const; 74 75 // Fetch a nonce, returning UNSET if the index is out of range. This method specifically 76 // does not throw or generate an error if the index is out of range; this allows the method 77 // to be called in a CriticalNative JNI API. 78 int64_t getNonce(int index) const; 79 80 // Set a nonce and return true. Return false if the index is out of range. This method 81 // specifically does not throw or generate an error if the index is out of range; this 82 // allows the method to be called in a CriticalNative JNI API. 83 bool setNonce(int index, int64_t value); 84 85 // Fetch just the byte-block hash 86 int32_t getHash() const; 87 88 // Copy the byte block to the target and return the current hash. 89 int32_t getByteBlock(block_t* block, size_t len) const; 90 91 // Set the byte block and the hash. 92 void setByteBlock(int hash, const block_t* block, size_t len); 93 94 private: 95 96 // A convenience function to compute the offset between two unlike pointers. offset(void const * base,void const * member)97 static size_t offset(void const* base, void const* member) { 98 return reinterpret_cast<uintptr_t>(member) - reinterpret_cast<std::uintptr_t>(base); 99 } 100 101 // Return the address of the nonce array. nonce()102 volatile nonce_t* nonce() const { 103 // The array is located at an offset from <this>. 104 return reinterpret_cast<nonce_t*>( 105 reinterpret_cast<std::uintptr_t>(this) + mNonceOffset); 106 } 107 108 // Return the address of the byte block array. byteBlock()109 volatile block_t* byteBlock() const { 110 // The array is located at an offset from <this>. 111 return reinterpret_cast<block_t*>( 112 reinterpret_cast<std::uintptr_t>(this) + mByteOffset); 113 } 114 }; 115 116 /** 117 * A cache nonce block contains an array of std::atomic<int64_t> and an array of bytes. The 118 * byte array has an associated hash. This class provides methods to read and write the fields 119 * of the block but it does not interpret the fields. 120 * 121 * On initialization, all fields are set to zero. 122 * 123 * In general, methods do not report errors. This allows the methods to be used in 124 * CriticalNative JNI APIs. 125 * 126 * The template is parameterized by the number of nonces it supports and the number of bytes in 127 * the string block. 128 */ 129 template<int maxNonce, size_t maxByte> class CacheNonce : public NonceStore { 130 131 // The array of nonces 132 volatile nonce_t mNonce[maxNonce]; 133 134 // The byte array. This is not atomic but it is guarded by the mByteHash. 135 volatile block_t mByteBlock[maxByte]; 136 137 public: 138 // Construct and initialize the memory. CacheNonce()139 CacheNonce() : 140 NonceStore(maxNonce, maxByte, &mNonce[0], &mByteBlock[0]) 141 { 142 for (int i = 0; i < maxNonce; i++) { 143 mNonce[i] = UNSET; 144 } 145 mByteHash = UNSET; 146 memset((void*) mByteBlock, UNSET, sizeof(mByteBlock)); 147 } 148 }; 149 150 // The CacheNonce for system server holds 64 nonces with a string block of 8192 bytes. This is 151 // more than enough for system_server PropertyInvalidatedCache support. The configuration 152 // values are not defined as visible constants. Clients should use the accessors on the 153 // SystemCacheNonce instance if they need the sizing parameters. 154 typedef CacheNonce</* max nonce */ 64, /* byte block size */ 8192> SystemCacheNonce; 155 156 } // namespace android.app.PropertyInvalidatedCache 157