• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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