1 // Copyright 2014 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "fxjs/cfx_globaldata.h"
8
9 #include <utility>
10
11 #include "core/fdrm/fx_crypt.h"
12 #include "core/fxcrt/fx_memcpy_wrappers.h"
13 #include "core/fxcrt/numerics/safe_conversions.h"
14 #include "core/fxcrt/stl_util.h"
15
16 namespace {
17
18 constexpr size_t kMinGlobalDataBytes = 12;
19 constexpr size_t kMaxGlobalDataBytes = 4 * 1024 - 8;
20 constexpr uint16_t kMagic = ('X' << 8) | 'F';
21 constexpr uint16_t kMaxVersion = 2;
22
23 const uint8_t kRC4KEY[] = {
24 0x19, 0xa8, 0xe8, 0x01, 0xf6, 0xa8, 0xb6, 0x4d, 0x82, 0x04, 0x45, 0x6d,
25 0xb4, 0xcf, 0xd7, 0x77, 0x67, 0xf9, 0x75, 0x9f, 0xf0, 0xe0, 0x1e, 0x51,
26 0xee, 0x46, 0xfd, 0x0b, 0xc9, 0x93, 0x25, 0x55, 0x4a, 0xee, 0xe0, 0x16,
27 0xd0, 0xdf, 0x8c, 0xfa, 0x2a, 0xa9, 0x49, 0xfd, 0x97, 0x1c, 0x0e, 0x22,
28 0x13, 0x28, 0x7c, 0xaf, 0xc4, 0xfc, 0x9c, 0x12, 0x65, 0x8c, 0x4e, 0x5b,
29 0x04, 0x75, 0x89, 0xc9, 0xb1, 0xed, 0x50, 0xca, 0x96, 0x6f, 0x1a, 0x7a,
30 0xfe, 0x58, 0x5d, 0xec, 0x19, 0x4a, 0xf6, 0x35, 0x6a, 0x97, 0x14, 0x00,
31 0x0e, 0xd0, 0x6b, 0xbb, 0xd5, 0x75, 0x55, 0x8b, 0x6e, 0x6b, 0x19, 0xa0,
32 0xf8, 0x77, 0xd5, 0xa3};
33
34 CFX_GlobalData* g_pInstance = nullptr;
35
36 // Returns true if non-empty, setting sPropName
TrimPropName(ByteString * sPropName)37 bool TrimPropName(ByteString* sPropName) {
38 sPropName->TrimWhitespace();
39 return sPropName->GetLength() != 0;
40 }
41
MakeNameTypeString(const ByteString & name,CFX_Value::DataType eType,BinaryBuffer * result)42 void MakeNameTypeString(const ByteString& name,
43 CFX_Value::DataType eType,
44 BinaryBuffer* result) {
45 uint32_t dwNameLen = pdfium::checked_cast<uint32_t>(name.GetLength());
46 result->AppendUint32(dwNameLen);
47 result->AppendString(name);
48 result->AppendUint16(static_cast<uint16_t>(eType));
49 }
50
MakeByteString(const ByteString & name,const CFX_KeyValue & pData,BinaryBuffer * result)51 bool MakeByteString(const ByteString& name,
52 const CFX_KeyValue& pData,
53 BinaryBuffer* result) {
54 switch (pData.nType) {
55 case CFX_Value::DataType::kNumber: {
56 MakeNameTypeString(name, pData.nType, result);
57 result->AppendDouble(pData.dData);
58 return true;
59 }
60 case CFX_Value::DataType::kBoolean: {
61 MakeNameTypeString(name, pData.nType, result);
62 result->AppendUint16(static_cast<uint16_t>(pData.bData));
63 return true;
64 }
65 case CFX_Value::DataType::kString: {
66 MakeNameTypeString(name, pData.nType, result);
67 uint32_t dwDataLen =
68 pdfium::checked_cast<uint32_t>(pData.sData.GetLength());
69 result->AppendUint32(dwDataLen);
70 result->AppendString(pData.sData);
71 return true;
72 }
73 case CFX_Value::DataType::kNull: {
74 MakeNameTypeString(name, pData.nType, result);
75 return true;
76 }
77 // Arrays don't get persisted per JS spec page 484.
78 case CFX_Value::DataType::kObject:
79 break;
80 }
81 return false;
82 }
83
84 } // namespace
85
86 // static
GetRetainedInstance(Delegate * pDelegate)87 CFX_GlobalData* CFX_GlobalData::GetRetainedInstance(Delegate* pDelegate) {
88 if (!g_pInstance) {
89 g_pInstance = new CFX_GlobalData(pDelegate);
90 }
91 ++g_pInstance->m_RefCount;
92 return g_pInstance;
93 }
94
Release()95 bool CFX_GlobalData::Release() {
96 if (--m_RefCount)
97 return false;
98
99 delete g_pInstance;
100 g_pInstance = nullptr;
101 return true;
102 }
103
CFX_GlobalData(Delegate * pDelegate)104 CFX_GlobalData::CFX_GlobalData(Delegate* pDelegate) : m_pDelegate(pDelegate) {
105 LoadGlobalPersistentVariables();
106 }
107
~CFX_GlobalData()108 CFX_GlobalData::~CFX_GlobalData() {
109 SaveGlobalPersisitentVariables();
110 }
111
FindGlobalVariable(const ByteString & propname)112 CFX_GlobalData::iterator CFX_GlobalData::FindGlobalVariable(
113 const ByteString& propname) {
114 for (auto it = m_arrayGlobalData.begin(); it != m_arrayGlobalData.end();
115 ++it) {
116 if ((*it)->data.sKey == propname)
117 return it;
118 }
119 return m_arrayGlobalData.end();
120 }
121
GetGlobalVariable(const ByteString & propname)122 CFX_GlobalData::Element* CFX_GlobalData::GetGlobalVariable(
123 const ByteString& propname) {
124 auto iter = FindGlobalVariable(propname);
125 return iter != m_arrayGlobalData.end() ? iter->get() : nullptr;
126 }
127
SetGlobalVariableNumber(ByteString sPropName,double dData)128 void CFX_GlobalData::SetGlobalVariableNumber(ByteString sPropName,
129 double dData) {
130 if (!TrimPropName(&sPropName))
131 return;
132
133 CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
134 if (pData) {
135 pData->data.nType = CFX_Value::DataType::kNumber;
136 pData->data.dData = dData;
137 return;
138 }
139 auto pNewData = std::make_unique<CFX_GlobalData::Element>();
140 pNewData->data.sKey = std::move(sPropName);
141 pNewData->data.nType = CFX_Value::DataType::kNumber;
142 pNewData->data.dData = dData;
143 m_arrayGlobalData.push_back(std::move(pNewData));
144 }
145
SetGlobalVariableBoolean(ByteString sPropName,bool bData)146 void CFX_GlobalData::SetGlobalVariableBoolean(ByteString sPropName,
147 bool bData) {
148 if (!TrimPropName(&sPropName))
149 return;
150
151 CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
152 if (pData) {
153 pData->data.nType = CFX_Value::DataType::kBoolean;
154 pData->data.bData = bData;
155 return;
156 }
157 auto pNewData = std::make_unique<CFX_GlobalData::Element>();
158 pNewData->data.sKey = std::move(sPropName);
159 pNewData->data.nType = CFX_Value::DataType::kBoolean;
160 pNewData->data.bData = bData;
161 m_arrayGlobalData.push_back(std::move(pNewData));
162 }
163
SetGlobalVariableString(ByteString sPropName,const ByteString & sData)164 void CFX_GlobalData::SetGlobalVariableString(ByteString sPropName,
165 const ByteString& sData) {
166 if (!TrimPropName(&sPropName))
167 return;
168
169 CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
170 if (pData) {
171 pData->data.nType = CFX_Value::DataType::kString;
172 pData->data.sData = sData;
173 return;
174 }
175 auto pNewData = std::make_unique<CFX_GlobalData::Element>();
176 pNewData->data.sKey = std::move(sPropName);
177 pNewData->data.nType = CFX_Value::DataType::kString;
178 pNewData->data.sData = sData;
179 m_arrayGlobalData.push_back(std::move(pNewData));
180 }
181
SetGlobalVariableObject(ByteString sPropName,std::vector<std::unique_ptr<CFX_KeyValue>> array)182 void CFX_GlobalData::SetGlobalVariableObject(
183 ByteString sPropName,
184 std::vector<std::unique_ptr<CFX_KeyValue>> array) {
185 if (!TrimPropName(&sPropName))
186 return;
187
188 CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
189 if (pData) {
190 pData->data.nType = CFX_Value::DataType::kObject;
191 pData->data.objData = std::move(array);
192 return;
193 }
194 auto pNewData = std::make_unique<CFX_GlobalData::Element>();
195 pNewData->data.sKey = std::move(sPropName);
196 pNewData->data.nType = CFX_Value::DataType::kObject;
197 pNewData->data.objData = std::move(array);
198 m_arrayGlobalData.push_back(std::move(pNewData));
199 }
200
SetGlobalVariableNull(ByteString sPropName)201 void CFX_GlobalData::SetGlobalVariableNull(ByteString sPropName) {
202 if (!TrimPropName(&sPropName))
203 return;
204
205 CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
206 if (pData) {
207 pData->data.nType = CFX_Value::DataType::kNull;
208 return;
209 }
210 auto pNewData = std::make_unique<CFX_GlobalData::Element>();
211 pNewData->data.sKey = std::move(sPropName);
212 pNewData->data.nType = CFX_Value::DataType::kNull;
213 m_arrayGlobalData.push_back(std::move(pNewData));
214 }
215
SetGlobalVariablePersistent(ByteString sPropName,bool bPersistent)216 bool CFX_GlobalData::SetGlobalVariablePersistent(ByteString sPropName,
217 bool bPersistent) {
218 if (!TrimPropName(&sPropName))
219 return false;
220
221 CFX_GlobalData::Element* pData = GetGlobalVariable(sPropName);
222 if (!pData)
223 return false;
224
225 pData->bPersistent = bPersistent;
226 return true;
227 }
228
DeleteGlobalVariable(ByteString sPropName)229 bool CFX_GlobalData::DeleteGlobalVariable(ByteString sPropName) {
230 if (!TrimPropName(&sPropName))
231 return false;
232
233 auto iter = FindGlobalVariable(sPropName);
234 if (iter == m_arrayGlobalData.end())
235 return false;
236
237 m_arrayGlobalData.erase(iter);
238 return true;
239 }
240
GetSize() const241 int32_t CFX_GlobalData::GetSize() const {
242 return fxcrt::CollectionSize<int32_t>(m_arrayGlobalData);
243 }
244
GetAt(int index)245 CFX_GlobalData::Element* CFX_GlobalData::GetAt(int index) {
246 if (index < 0 || index >= GetSize())
247 return nullptr;
248 return m_arrayGlobalData[index].get();
249 }
250
LoadGlobalPersistentVariables()251 bool CFX_GlobalData::LoadGlobalPersistentVariables() {
252 if (!m_pDelegate)
253 return false;
254
255 bool ret;
256 {
257 // Span can't outlive call to BufferDone().
258 std::optional<pdfium::span<uint8_t>> buffer = m_pDelegate->LoadBuffer();
259 if (!buffer.has_value() || buffer.value().empty())
260 return false;
261
262 ret = LoadGlobalPersistentVariablesFromBuffer(buffer.value());
263 }
264 m_pDelegate->BufferDone();
265 return ret;
266 }
267
LoadGlobalPersistentVariablesFromBuffer(pdfium::span<uint8_t> buffer)268 bool CFX_GlobalData::LoadGlobalPersistentVariablesFromBuffer(
269 pdfium::span<uint8_t> buffer) {
270 if (buffer.size() < kMinGlobalDataBytes)
271 return false;
272
273 CRYPT_ArcFourCryptBlock(buffer, kRC4KEY);
274
275 UNSAFE_TODO({
276 uint8_t* p = buffer.data();
277 uint16_t wType = *((uint16_t*)p);
278 p += sizeof(uint16_t);
279 if (wType != kMagic) {
280 return false;
281 }
282
283 uint16_t wVersion = *((uint16_t*)p);
284 p += sizeof(uint16_t);
285 if (wVersion > kMaxVersion) {
286 return false;
287 }
288
289 uint32_t dwCount = *((uint32_t*)p);
290 p += sizeof(uint32_t);
291
292 uint32_t dwSize = *((uint32_t*)p);
293 p += sizeof(uint32_t);
294
295 if (dwSize != buffer.size() - sizeof(uint16_t) * 2 - sizeof(uint32_t) * 2) {
296 return false;
297 }
298
299 for (int32_t i = 0, sz = dwCount; i < sz; i++) {
300 if (p + sizeof(uint32_t) >= buffer.end()) {
301 break;
302 }
303
304 uint32_t dwNameLen = 0;
305 FXSYS_memcpy(&dwNameLen, p, sizeof(uint32_t));
306 p += sizeof(uint32_t);
307 if (p + dwNameLen > buffer.end()) {
308 break;
309 }
310
311 ByteString sEntry = ByteString(p, dwNameLen);
312 p += sizeof(char) * dwNameLen;
313
314 uint16_t wDataType = 0;
315 FXSYS_memcpy(&wDataType, p, sizeof(uint16_t));
316 p += sizeof(uint16_t);
317
318 CFX_Value::DataType eDataType =
319 static_cast<CFX_Value::DataType>(wDataType);
320
321 switch (eDataType) {
322 case CFX_Value::DataType::kNumber: {
323 double dData = 0;
324 switch (wVersion) {
325 case 1: {
326 uint32_t dwData = 0;
327 FXSYS_memcpy(&dwData, p, sizeof(uint32_t));
328 p += sizeof(uint32_t);
329 dData = dwData;
330 } break;
331 case 2: {
332 dData = 0;
333 FXSYS_memcpy(&dData, p, sizeof(double));
334 p += sizeof(double);
335 } break;
336 }
337 SetGlobalVariableNumber(sEntry, dData);
338 SetGlobalVariablePersistent(sEntry, true);
339 } break;
340 case CFX_Value::DataType::kBoolean: {
341 uint16_t wData = 0;
342 FXSYS_memcpy(&wData, p, sizeof(uint16_t));
343 p += sizeof(uint16_t);
344 SetGlobalVariableBoolean(sEntry, (bool)(wData == 1));
345 SetGlobalVariablePersistent(sEntry, true);
346 } break;
347 case CFX_Value::DataType::kString: {
348 uint32_t dwLength = 0;
349 FXSYS_memcpy(&dwLength, p, sizeof(uint32_t));
350 p += sizeof(uint32_t);
351 if (p + dwLength > buffer.end()) {
352 break;
353 }
354 SetGlobalVariableString(sEntry, ByteString(p, dwLength));
355 SetGlobalVariablePersistent(sEntry, true);
356 p += sizeof(char) * dwLength;
357 } break;
358 case CFX_Value::DataType::kNull: {
359 SetGlobalVariableNull(sEntry);
360 SetGlobalVariablePersistent(sEntry, true);
361 } break;
362 case CFX_Value::DataType::kObject:
363 // Arrays aren't allowed in these buffers, nor are unrecognized tags.
364 return false;
365 }
366 }
367 });
368 return true;
369 }
370
SaveGlobalPersisitentVariables()371 bool CFX_GlobalData::SaveGlobalPersisitentVariables() {
372 if (!m_pDelegate)
373 return false;
374
375 uint32_t nCount = 0;
376 BinaryBuffer sData;
377 for (const auto& pElement : m_arrayGlobalData) {
378 if (!pElement->bPersistent)
379 continue;
380
381 BinaryBuffer sElement;
382 if (!MakeByteString(pElement->data.sKey, pElement->data, &sElement))
383 continue;
384
385 if (sData.GetSize() + sElement.GetSize() > kMaxGlobalDataBytes)
386 break;
387
388 sData.AppendSpan(sElement.GetSpan());
389 nCount++;
390 }
391
392 BinaryBuffer sFile;
393 sFile.AppendUint16(kMagic);
394 sFile.AppendUint16(kMaxVersion);
395 sFile.AppendUint32(nCount);
396
397 uint32_t dwSize = pdfium::checked_cast<uint32_t>(sData.GetSize());
398 sFile.AppendUint32(dwSize);
399 sFile.AppendSpan(sData.GetSpan());
400
401 CRYPT_ArcFourCryptBlock(sFile.GetMutableSpan(), kRC4KEY);
402 return m_pDelegate->StoreBuffer(sFile.GetSpan());
403 }
404
405 CFX_GlobalData::Element::Element() = default;
406
407 CFX_GlobalData::Element::~Element() = default;
408