mirror of
https://github.com/Tencent/MMKV.git
synced 2026-10-17 04:30:56 +08:00
de70a23783
Keys longer than 65531 bytes cause keySize and computedKVSize (both uint16_t) to wrap around, silently corrupting the internal offset so that subsequent reads return garbage. Reject keys exceeding the safe threshold (UINT16_MAX - 4) at all write and parse entry points.
134 lines
4.3 KiB
C++
134 lines
4.3 KiB
C++
/*
|
|
* Tencent is pleased to support the open source community by making
|
|
* MMKV available.
|
|
*
|
|
* Copyright (C) 2020 THL A29 Limited, a Tencent company.
|
|
* All rights reserved.
|
|
*
|
|
* Licensed under the BSD 3-Clause License (the "License"); you may not use
|
|
* this file except in compliance with the License. You may obtain a copy of
|
|
* the License at
|
|
*
|
|
* https://opensource.org/licenses/BSD-3-Clause
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef KeyValueHolder_hpp
|
|
#define KeyValueHolder_hpp
|
|
#ifdef __cplusplus
|
|
|
|
#include "MMBuffer.h"
|
|
#include "aes/AESCrypt.h"
|
|
|
|
namespace mmkv {
|
|
|
|
// The maximum key length that can be safely stored without overflowing internal uint16_t fields.
|
|
// keySize (uint16) must hold the key length, and computedKVSize (uint16) must hold
|
|
// keySize + pbRawVarint32Size(keySize) + pbRawVarint32Size(valueSize).
|
|
// With keySize up to 65531, the worst-case computedKVSize = 65531 + 3 + 5 = 65539 can still overflow,
|
|
// but in practice computedKVSize only needs keySize + varint(keySize); the valueSize varint is added
|
|
// separately. The critical constraint is keySize + pbRawVarint32Size(keySize) <= UINT16_MAX,
|
|
// i.e. keySize + 3 <= 65535, so keySize <= 65532. However empirical testing shows 65532 already fails
|
|
// because the full computedKVSize includes pbRawVarint32Size(valueSize) (>= 1), pushing it to >= 65536.
|
|
// Therefore the safe limit is 65531 (UINT16_MAX - 4).
|
|
static constexpr uint32_t KeySizeLimit = UINT16_MAX - 4;
|
|
|
|
#pragma pack(push, 1)
|
|
|
|
struct KeyValueHolder {
|
|
uint16_t computedKVSize; // internal use only
|
|
uint16_t keySize;
|
|
uint32_t valueSize;
|
|
uint32_t offset;
|
|
|
|
KeyValueHolder() = default;
|
|
KeyValueHolder(uint32_t keyLength, uint32_t valueLength, uint32_t offset);
|
|
|
|
MMBuffer toMMBuffer(const void *basePtr) const;
|
|
};
|
|
|
|
#ifndef MMKV_DISABLE_CRYPT
|
|
|
|
enum KeyValueHolderType : uint8_t {
|
|
KeyValueHolderType_Direct, // store value directly
|
|
KeyValueHolderType_Memory, // store value in the heap memory
|
|
KeyValueHolderType_Offset, // store value by offset
|
|
};
|
|
|
|
// kv holder for encrypted mmkv
|
|
struct KeyValueHolderCrypt {
|
|
KeyValueHolderType type = KeyValueHolderType_Direct;
|
|
|
|
union {
|
|
// store value by offset
|
|
struct {
|
|
uint8_t pbKeyValueSize; // size needed to encode keySize & valueSize
|
|
uint16_t keySize;
|
|
uint32_t valueSize;
|
|
uint32_t offset;
|
|
AESCryptStatus cryptStatus;
|
|
};
|
|
// store value directly
|
|
struct {
|
|
uint8_t paddedSize;
|
|
uint8_t paddedValue[1];
|
|
};
|
|
// store value in the heap memory
|
|
struct {
|
|
uint32_t memSize;
|
|
void *memPtr;
|
|
};
|
|
};
|
|
|
|
static constexpr size_t SmallBufferSize() {
|
|
return sizeof(KeyValueHolderCrypt) - offsetof(KeyValueHolderCrypt, paddedValue);
|
|
}
|
|
|
|
static constexpr size_t MediumBufferSize() {
|
|
return 256;
|
|
}
|
|
|
|
static bool isValueStoredAsOffset(size_t valueSize) { return valueSize > MediumBufferSize(); }
|
|
|
|
KeyValueHolderCrypt() = default;
|
|
KeyValueHolderCrypt(const void *valuePtr, size_t valueLength);
|
|
explicit KeyValueHolderCrypt(MMBuffer &&data);
|
|
KeyValueHolderCrypt(uint32_t keyLength, uint32_t valueLength, uint32_t offset);
|
|
|
|
KeyValueHolderCrypt(KeyValueHolderCrypt &&other) noexcept;
|
|
KeyValueHolderCrypt &operator=(KeyValueHolderCrypt &&other) noexcept;
|
|
void move(KeyValueHolderCrypt &&other) noexcept;
|
|
|
|
~KeyValueHolderCrypt();
|
|
|
|
uint32_t realValueSize() const;
|
|
|
|
MMBuffer toMMBuffer(const void *basePtr, const AESCrypt *crypter) const;
|
|
|
|
std::tuple<uint32_t, uint32_t, AESCryptStatus *> toTuple() {
|
|
return std::make_tuple(offset, pbKeyValueSize + keySize + valueSize, &cryptStatus);
|
|
}
|
|
|
|
// those are expensive, just forbid it for possibly misuse
|
|
explicit KeyValueHolderCrypt(const KeyValueHolderCrypt &other) = delete;
|
|
KeyValueHolderCrypt &operator=(const KeyValueHolderCrypt &other) = delete;
|
|
|
|
#ifdef MMKV_DEBUG
|
|
static void testAESToMMBuffer();
|
|
#endif
|
|
};
|
|
|
|
#endif // MMKV_DISABLE_CRYPT
|
|
|
|
#pragma pack(pop)
|
|
|
|
} // namespace mmkv
|
|
|
|
#endif
|
|
#endif /* KeyValueHolder_hpp */
|