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