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 * This structure is shared between 64-bit and 32-bit processes. Therefore it is imperative 32 * that the structure not use any datatypes that are architecture-dependent (like size_t). 33 * Additionally, care must be taken to avoid unexpected padding in the structure. 34 */ 35 class alignas(8) NonceStore { 36 protected: 37 // A convenient typedef. The jbyteArray element type is jbyte, which the compiler treats as 38 // signed char. 39 typedef signed char block_t; 40 41 // The nonce type. 42 typedef std::atomic<int64_t> nonce_t; 43 44 // Atomics should be safe to use across processes if they are lock free. 45 static_assert(nonce_t::is_always_lock_free == true); 46 47 // The value of an unset field. 48 static constexpr int UNSET = 0; 49 50 // The size of the nonce array. This and the following sizes are int32_t to 51 // be ABI independent. 52 const int32_t kMaxNonce; 53 54 // The size of the byte array. 55 const int32_t kMaxByte; 56 57 // The offset to the nonce array. 58 const int32_t mNonceOffset; 59 60 // The offset to the byte array. 61 const int32_t mByteOffset; 62 63 // The byte block hash. This is fixed and at a known offset, so leave it in the base class. 64 volatile std::atomic<int32_t> mByteHash; 65 66 // A 4-byte padd that makes the size of this structure a multiple of 8 bytes. 67 const int32_t _pad = 0; 68 69 // The constructor is protected! It only makes sense when called from a subclass. NonceStore(int kMaxNonce,int kMaxByte,volatile nonce_t * nonce,volatile block_t * block)70 NonceStore(int kMaxNonce, int kMaxByte, volatile nonce_t* nonce, volatile block_t* block) : 71 kMaxNonce(kMaxNonce), 72 kMaxByte(kMaxByte), 73 mNonceOffset(offset(this, const_cast<nonce_t*>(nonce))), 74 mByteOffset(offset(this, const_cast<block_t*>(block))) { 75 } 76 77 public: 78 79 // These provide run-time access to the sizing parameters. 80 int getMaxNonce() const; 81 int getMaxByte() const; 82 83 // Fetch a nonce, returning UNSET if the index is out of range. This method specifically 84 // does not throw or generate an error if the index is out of range; this allows the method 85 // to be called in a CriticalNative JNI API. 86 int64_t getNonce(int index) const; 87 88 // Set a nonce and return true. Return false if the index is out of range. This method 89 // specifically does not throw or generate an error if the index is out of range; this 90 // allows the method to be called in a CriticalNative JNI API. 91 bool setNonce(int index, int64_t value); 92 93 // Fetch just the byte-block hash 94 int32_t getHash() const; 95 96 // Copy the byte block to the target and return the current hash. 97 int32_t getByteBlock(block_t* block, int32_t len) const; 98 99 // Set the byte block and the hash. 100 void setByteBlock(int hash, const block_t* block, int32_t len); 101 102 private: 103 104 // A convenience function to compute the offset between two unlike pointers. offset(void const * base,void const * member)105 static size_t offset(void const* base, void const* member) { 106 return reinterpret_cast<uintptr_t>(member) - reinterpret_cast<std::uintptr_t>(base); 107 } 108 109 // Return the address of the nonce array. nonce()110 volatile nonce_t* nonce() const { 111 // The array is located at an offset from <this>. 112 return reinterpret_cast<nonce_t*>( 113 reinterpret_cast<std::uintptr_t>(this) + mNonceOffset); 114 } 115 116 // Return the address of the byte block array. byteBlock()117 volatile block_t* byteBlock() const { 118 // The array is located at an offset from <this>. 119 return reinterpret_cast<block_t*>( 120 reinterpret_cast<std::uintptr_t>(this) + mByteOffset); 121 } 122 }; 123 124 // Assert that the size of the object is fixed, independent of the CPU architecture. There are 125 // four int32_t fields and one atomic<int32_t>, which sums to 20 bytes total. This assertion 126 // uses a constant instead of computing the size of the objects in the compiler, to avoid 127 // different answers on different architectures. 128 static_assert(sizeof(NonceStore) == 24); 129 130 /** 131 * A cache nonce block contains an array of std::atomic<int64_t> and an array of bytes. The 132 * byte array has an associated hash. This class provides methods to read and write the fields 133 * of the block but it does not interpret the fields. 134 * 135 * On initialization, all fields are set to zero. 136 * 137 * In general, methods do not report errors. This allows the methods to be used in 138 * CriticalNative JNI APIs. 139 * 140 * The template is parameterized by the number of nonces it supports and the number of bytes in 141 * the string block. 142 */ 143 template<int MAX_NONCE, int MAX_BYTE> class CacheNonce : public NonceStore { 144 145 // The array of nonces 146 volatile nonce_t mNonce[MAX_NONCE]; 147 148 // The byte array. This is not atomic but it is guarded by the mByteHash. 149 volatile block_t mByteBlock[MAX_BYTE]; 150 151 public: 152 // Export the parameters for use in compiler assertions. 153 static constexpr int kMaxNonceCount = MAX_NONCE; 154 static constexpr int kMaxByteCount = MAX_BYTE; 155 156 // Construct and initialize the memory. CacheNonce()157 CacheNonce() : NonceStore(MAX_NONCE, MAX_BYTE, &mNonce[0], &mByteBlock[0]) { 158 for (int i = 0; i < MAX_NONCE; i++) { 159 mNonce[i] = UNSET; 160 } 161 mByteHash = UNSET; 162 memset((void*) mByteBlock, UNSET, sizeof(mByteBlock)); 163 } 164 }; 165 166 // The CacheNonce for system server. The configuration values are not defined as visible 167 // constants. Clients should use the accessors on the SystemCacheNonce instance if they need 168 // the sizing parameters. 169 170 // LINT.IfChange(system_nonce_config) 171 typedef CacheNonce</* max nonce */ 128, /* byte block size */ 8192> SystemCacheNonce; 172 // LINT.ThenChange(/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java:system_nonce_config) 173 174 // Verify that there is no padding in the final class. 175 static_assert(sizeof(SystemCacheNonce) == 176 sizeof(NonceStore) 177 + SystemCacheNonce::kMaxNonceCount*8 178 + SystemCacheNonce::kMaxByteCount); 179 180 } // namespace android.app.PropertyInvalidatedCache 181