Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GML Reader
4 : * Purpose: Convenience functions for parsing with Xerces-C library
5 : * Functions for translating back and forth between XMLCh and char.
6 : * We assume that XMLCh is a simple numeric type that we can
7 : * correspond 1:1 with char values, but that it likely is larger
8 : * than a char.
9 : * Author: Frank Warmerdam, warmerdam@pobox.com
10 : * Author: Even Rouault, <even.rouault at spatialys.com>
11 : *
12 : ******************************************************************************
13 : * Copyright (c) 2002, Frank Warmerdam
14 : * Copyright (c) 2016, Even Rouault <even.rouault at spatialys.com>
15 : *
16 : * SPDX-License-Identifier: MIT
17 : ****************************************************************************/
18 :
19 : #include "ogr_xerces.h"
20 :
21 : #include "cpl_port.h"
22 : #include "cpl_error.h"
23 : #include "cpl_multiproc.h"
24 : #include "cpl_string.h"
25 :
26 : #include <algorithm>
27 : #include <limits>
28 : #include <map>
29 :
30 : #ifdef HAVE_XERCES
31 :
32 : class OGRXercesStandardMemoryManager;
33 : class OGRXercesInstrumentedMemoryManager;
34 :
35 : /************************************************************************/
36 : /* CPLGettimeofday() */
37 : /************************************************************************/
38 :
39 : #if defined(_WIN32) && !defined(__CYGWIN__)
40 : #include <sys/timeb.h>
41 :
42 : namespace
43 : {
44 : struct CPLTimeVal
45 : {
46 : time_t tv_sec; /* seconds */
47 : long tv_usec; /* and microseconds */
48 : };
49 : } // namespace
50 :
51 : static int CPLGettimeofday(struct CPLTimeVal *tp, void * /* timezonep*/)
52 : {
53 : struct _timeb theTime;
54 :
55 : _ftime(&theTime);
56 : tp->tv_sec = static_cast<time_t>(theTime.time);
57 : tp->tv_usec = theTime.millitm * 1000;
58 : return 0;
59 : }
60 : #else
61 : #include <sys/time.h> /* for gettimeofday() */
62 : #define CPLTimeVal timeval
63 : #define CPLGettimeofday(t, u) gettimeofday(t, u)
64 : #endif
65 :
66 : namespace
67 : {
68 : struct LimitationStruct
69 : {
70 : size_t maxMemAlloc = 0;
71 : std::string osMsgMaxMemAlloc{};
72 : double timeOut = 0;
73 : std::string osMsgTimeout{};
74 :
75 : CPLTimeVal initTV{0, 0};
76 : CPLTimeVal lastTV{0, 0};
77 : size_t totalAllocSize = 0;
78 : size_t allocCount = 0;
79 : };
80 : } // namespace
81 :
82 : static CPLMutex *hOGRXercesMutex = nullptr;
83 : static int nCounter = 0;
84 : static bool bXercesWasAlreadyInitializedBeforeUs = false;
85 : static OGRXercesStandardMemoryManager *gpExceptionMemoryManager = nullptr;
86 : static OGRXercesInstrumentedMemoryManager *gpMemoryManager = nullptr;
87 : static std::map<GIntBig, LimitationStruct> *gpoMapThreadTimeout = nullptr;
88 :
89 : /************************************************************************/
90 : /* OGRXercesStandardMemoryManager */
91 : /************************************************************************/
92 :
93 : class OGRXercesStandardMemoryManager final : public MemoryManager
94 : {
95 : public:
96 161 : OGRXercesStandardMemoryManager() = default;
97 :
98 0 : MemoryManager *getExceptionMemoryManager() override
99 : {
100 0 : return this;
101 : }
102 :
103 : void *allocate(XMLSize_t size) override;
104 :
105 : void deallocate(void *p) override;
106 : };
107 :
108 777 : void *OGRXercesStandardMemoryManager::allocate(XMLSize_t size)
109 : {
110 777 : void *memptr = VSIMalloc(size);
111 777 : if (memptr == nullptr && size != 0)
112 0 : throw OutOfMemoryException();
113 777 : return memptr;
114 : }
115 :
116 777 : void OGRXercesStandardMemoryManager::deallocate(void *p)
117 : {
118 777 : if (p)
119 777 : VSIFree(p);
120 777 : }
121 :
122 : /************************************************************************/
123 : /* OGRXercesInstrumentedMemoryManager */
124 : /************************************************************************/
125 :
126 : class OGRXercesInstrumentedMemoryManager final : public MemoryManager
127 : {
128 : public:
129 161 : OGRXercesInstrumentedMemoryManager() = default;
130 :
131 262 : MemoryManager *getExceptionMemoryManager() override
132 : {
133 262 : return gpExceptionMemoryManager;
134 : }
135 :
136 : void *allocate(XMLSize_t size) override;
137 :
138 : void deallocate(void *p) override;
139 : };
140 :
141 22360000 : void *OGRXercesInstrumentedMemoryManager::allocate(XMLSize_t size)
142 : {
143 22360000 : if (size > std::numeric_limits<size_t>::max() - 8U)
144 0 : throw OutOfMemoryException();
145 22360000 : void *memptr = VSIMalloc(size + 8);
146 22360000 : if (memptr == nullptr)
147 0 : throw OutOfMemoryException();
148 22360000 : memcpy(memptr, &size, sizeof(XMLSize_t));
149 :
150 22360000 : LimitationStruct *pLimitation = nullptr;
151 : {
152 44720000 : CPLMutexHolderD(&hOGRXercesMutex);
153 :
154 22360000 : if (gpoMapThreadTimeout)
155 : {
156 13890100 : auto iter = gpoMapThreadTimeout->find(CPLGetPID());
157 13890100 : if (iter != gpoMapThreadTimeout->end())
158 : {
159 13890100 : pLimitation = &(iter->second);
160 : }
161 : }
162 : }
163 :
164 : // Big memory allocation can happen in cases like
165 : // https://issues.apache.org/jira/browse/XERCESC-1051
166 22360000 : if (pLimitation && pLimitation->maxMemAlloc > 0)
167 : {
168 13890100 : if (pLimitation->totalAllocSize + size > pLimitation->maxMemAlloc)
169 : {
170 1 : pLimitation->maxMemAlloc = 0;
171 1 : VSIFree(memptr);
172 1 : if (!pLimitation->osMsgMaxMemAlloc.empty())
173 : {
174 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
175 : pLimitation->osMsgMaxMemAlloc.c_str());
176 : }
177 1 : throw OutOfMemoryException();
178 : }
179 : }
180 :
181 : // Quite a hack, but some pathologic schema can cause excessive
182 : // processing time. As memory allocations are regularly done, we
183 : // measure the time of those consecutive allocations and check it
184 : // does not exceed a threshold set by OGRStartXercesTimeoutForThisThread()
185 : // Can happen in cases like
186 : // https://issues.apache.org/jira/browse/XERCESC-1051
187 22360000 : if (pLimitation && pLimitation->timeOut > 0)
188 : {
189 6609120 : ++pLimitation->allocCount;
190 6609120 : if (pLimitation->allocCount == 1000)
191 : {
192 6490 : pLimitation->allocCount = 0;
193 :
194 : CPLTimeVal tv;
195 6490 : CPLGettimeofday(&tv, nullptr);
196 12765 : if (pLimitation->initTV.tv_sec == 0 ||
197 : // Reset the counter if the delay between the last 1000 memory
198 : // allocations is too large. This enables being tolerant to
199 : // network requests.
200 6275 : tv.tv_sec + tv.tv_usec * 1e-6 -
201 6275 : (pLimitation->lastTV.tv_sec +
202 6275 : pLimitation->lastTV.tv_usec * 1e-6) >
203 12765 : std::min(0.1, pLimitation->timeOut / 10))
204 : {
205 220 : pLimitation->initTV = tv;
206 : }
207 6270 : else if (tv.tv_sec + tv.tv_usec * 1e-6 -
208 6270 : (pLimitation->initTV.tv_sec +
209 6270 : pLimitation->initTV.tv_usec * 1e-6) >
210 6270 : pLimitation->timeOut)
211 : {
212 1 : pLimitation->timeOut = 0;
213 1 : VSIFree(memptr);
214 1 : if (!pLimitation->osMsgTimeout.empty())
215 : {
216 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
217 : pLimitation->osMsgTimeout.c_str());
218 : }
219 1 : throw OutOfMemoryException();
220 : }
221 6489 : pLimitation->lastTV = tv;
222 : }
223 : }
224 :
225 22360000 : if (pLimitation && pLimitation->maxMemAlloc > 0)
226 : {
227 13890100 : pLimitation->totalAllocSize += size;
228 : }
229 :
230 22360000 : return static_cast<char *>(memptr) + 8;
231 : }
232 :
233 18087500 : void OGRXercesInstrumentedMemoryManager::deallocate(void *p)
234 : {
235 18087500 : if (p)
236 : {
237 14934700 : void *rawptr =
238 14934700 : reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(p) - 8);
239 : XMLSize_t size;
240 14934700 : memcpy(&size, rawptr, sizeof(XMLSize_t));
241 14934700 : VSIFree(rawptr);
242 :
243 14934700 : LimitationStruct *pLimitation = nullptr;
244 : {
245 29869500 : CPLMutexHolderD(&hOGRXercesMutex);
246 :
247 14934700 : if (gpoMapThreadTimeout)
248 : {
249 3275040 : auto iter = gpoMapThreadTimeout->find(CPLGetPID());
250 3275040 : if (iter != gpoMapThreadTimeout->end())
251 : {
252 3275040 : pLimitation = &(iter->second);
253 : }
254 : }
255 : }
256 14934700 : if (pLimitation && pLimitation->maxMemAlloc > 0)
257 : {
258 : // Memory allocations aren't necessarily paired within
259 : // a OGRStartXercesLimitsForThisThread() /
260 : // OGRStopXercesLimitsForThisThread() session. Probably due to
261 : // some caching with Xerces. So handle this gracefully to avoid
262 : // unsigned integer underflow.
263 3274790 : if (pLimitation->totalAllocSize >= size)
264 3274790 : pLimitation->totalAllocSize -= size;
265 : else
266 0 : pLimitation->totalAllocSize = 0;
267 : }
268 : }
269 18087500 : }
270 :
271 : /************************************************************************/
272 : /* OGRStartXercesLimitsForThisThread() */
273 : /************************************************************************/
274 :
275 259 : void OGRStartXercesLimitsForThisThread(size_t nMaxMemAlloc,
276 : const char *pszMsgMaxMemAlloc,
277 : double dfTimeoutSecond,
278 : const char *pszMsgTimeout)
279 : {
280 518 : CPLMutexHolderD(&hOGRXercesMutex);
281 259 : if (gpoMapThreadTimeout == nullptr)
282 : {
283 259 : gpoMapThreadTimeout = new std::map<GIntBig, LimitationStruct>();
284 : }
285 259 : LimitationStruct limitation;
286 259 : limitation.maxMemAlloc = nMaxMemAlloc;
287 259 : if (pszMsgMaxMemAlloc)
288 259 : limitation.osMsgMaxMemAlloc = pszMsgMaxMemAlloc;
289 259 : limitation.timeOut = dfTimeoutSecond;
290 259 : if (pszMsgTimeout)
291 259 : limitation.osMsgTimeout = pszMsgTimeout;
292 259 : (*gpoMapThreadTimeout)[CPLGetPID()] = std::move(limitation);
293 259 : }
294 :
295 : /************************************************************************/
296 : /* OGRStopXercesLimitsForThisThread() */
297 : /************************************************************************/
298 :
299 259 : void OGRStopXercesLimitsForThisThread()
300 : {
301 518 : CPLMutexHolderD(&hOGRXercesMutex);
302 259 : (*gpoMapThreadTimeout).erase(CPLGetPID());
303 259 : if (gpoMapThreadTimeout->empty())
304 : {
305 259 : delete gpoMapThreadTimeout;
306 259 : gpoMapThreadTimeout = nullptr;
307 : }
308 259 : }
309 :
310 : /************************************************************************/
311 : /* OGRXercesBinInputStream */
312 : /************************************************************************/
313 :
314 : class OGRXercesBinInputStream final : public BinInputStream
315 : {
316 : CPL_DISALLOW_COPY_ASSIGN(OGRXercesBinInputStream)
317 :
318 : VSILFILE *fp = nullptr;
319 : bool bOwnFP = false;
320 : XMLCh emptyString = 0;
321 :
322 : public:
323 : explicit OGRXercesBinInputStream(VSILFILE *fpIn, bool bOwnFPIn);
324 : ~OGRXercesBinInputStream() override;
325 :
326 : XMLFilePos curPos() const override;
327 : XMLSize_t readBytes(XMLByte *const toFill,
328 : const XMLSize_t maxToRead) override;
329 :
330 0 : const XMLCh *getContentType() const override
331 : {
332 0 : return &emptyString;
333 : }
334 : };
335 :
336 : /************************************************************************/
337 : /* OGRXercesNetAccessor */
338 : /************************************************************************/
339 :
340 : class OGRXercesNetAccessor final : public XMLNetAccessor
341 : {
342 : public:
343 161 : OGRXercesNetAccessor() = default;
344 :
345 : BinInputStream *makeNew(const XMLURL &urlSource,
346 : const XMLNetHTTPInfo *httpInfo) override;
347 :
348 0 : const XMLCh *getId() const override
349 : {
350 0 : return fgMyName;
351 : }
352 :
353 : private:
354 : static const XMLCh fgMyName[];
355 :
356 : OGRXercesNetAccessor(const OGRXercesNetAccessor &);
357 : OGRXercesNetAccessor &operator=(const OGRXercesNetAccessor &);
358 : };
359 :
360 : const XMLCh OGRXercesNetAccessor::fgMyName[] = {
361 : chLatin_O, chLatin_G, chLatin_R, chLatin_X, chLatin_e, chLatin_r, chLatin_c,
362 : chLatin_e, chLatin_s, chLatin_N, chLatin_e, chLatin_t, chLatin_A, chLatin_c,
363 : chLatin_c, chLatin_e, chLatin_s, chLatin_s, chLatin_o, chLatin_r, chNull};
364 :
365 : BinInputStream *
366 0 : OGRXercesNetAccessor::makeNew(const XMLURL &urlSource,
367 : const XMLNetHTTPInfo * /*httpInfo*/)
368 : {
369 : const std::string osURL =
370 0 : "/vsicurl_streaming/" + transcode(urlSource.getURLText());
371 0 : VSILFILE *fp = VSIFOpenL(osURL.c_str(), "rb");
372 0 : if (!fp)
373 0 : return nullptr;
374 0 : return new OGRXercesBinInputStream(fp, true);
375 : }
376 :
377 : /************************************************************************/
378 : /* OGRInitializeXerces() */
379 : /************************************************************************/
380 :
381 200 : bool OGRInitializeXerces()
382 : {
383 400 : CPLMutexHolderD(&hOGRXercesMutex);
384 :
385 200 : if (nCounter > 0)
386 : {
387 39 : nCounter++;
388 39 : return true;
389 : }
390 :
391 161 : if (XMLPlatformUtils::fgMemoryManager != nullptr)
392 : {
393 0 : CPLDebug("OGR", "Xerces-C already initialized before GDAL");
394 0 : bXercesWasAlreadyInitializedBeforeUs = true;
395 0 : nCounter = 1;
396 0 : return true;
397 : }
398 : else
399 : {
400 161 : gpExceptionMemoryManager = new OGRXercesStandardMemoryManager();
401 161 : gpMemoryManager = new OGRXercesInstrumentedMemoryManager();
402 :
403 : try
404 : {
405 161 : CPLDebug("OGR", "XMLPlatformUtils::Initialize()");
406 161 : XMLPlatformUtils::Initialize(XMLUni::fgXercescDefaultLocale,
407 : nullptr, /* nlsHome */
408 : nullptr, /* panicHandler */
409 : gpMemoryManager);
410 :
411 : // Install our own network accessor instead of the default Xerces-C
412 : // one This enables us in particular to honour GDAL_HTTP_TIMEOUT
413 161 : if (CPLTestBool(CPLGetConfigOption(
414 : "OGR_XERCES_USE_OGR_NET_ACCESSOR", "YES")))
415 : {
416 161 : auto oldNetAccessor = XMLPlatformUtils::fgNetAccessor;
417 161 : XMLPlatformUtils::fgNetAccessor = new OGRXercesNetAccessor();
418 161 : delete oldNetAccessor;
419 : }
420 :
421 161 : nCounter = 1;
422 161 : return true;
423 : }
424 0 : catch (const XMLException &toCatch)
425 : {
426 0 : CPLError(CE_Failure, CPLE_AppDefined,
427 : "Exception initializing Xerces: %s",
428 0 : transcode(toCatch.getMessage()).c_str());
429 0 : return false;
430 : }
431 : }
432 : }
433 :
434 : /************************************************************************/
435 : /* OGRDeinitializeXerces() */
436 : /************************************************************************/
437 :
438 200 : void OGRDeinitializeXerces()
439 : {
440 200 : CPLMutexHolderD(&hOGRXercesMutex);
441 200 : if (nCounter == 0)
442 : {
443 0 : CPLError(CE_Failure, CPLE_AppDefined,
444 : "Unpaired OGRInitializeXerces / OGRDeinitializeXerces calls");
445 0 : return;
446 : }
447 200 : nCounter--;
448 200 : if (nCounter == 0)
449 : {
450 322 : if (!bXercesWasAlreadyInitializedBeforeUs &&
451 161 : CPLTestBool(CPLGetConfigOption("OGR_XERCES_TERMINATE", "YES")))
452 : {
453 161 : CPLDebug("OGR", "XMLPlatformUtils::Terminate()");
454 161 : XMLPlatformUtils::Terminate();
455 :
456 161 : delete gpMemoryManager;
457 161 : gpMemoryManager = nullptr;
458 161 : delete gpExceptionMemoryManager;
459 161 : gpExceptionMemoryManager = nullptr;
460 : }
461 : }
462 : }
463 :
464 : /************************************************************************/
465 : /* OGRCleanupXercesMutex() */
466 : /************************************************************************/
467 :
468 933 : void OGRCleanupXercesMutex()
469 : {
470 933 : if (hOGRXercesMutex != nullptr)
471 3 : CPLDestroyMutex(hOGRXercesMutex);
472 933 : hOGRXercesMutex = nullptr;
473 933 : }
474 :
475 : namespace OGR
476 : {
477 :
478 : /************************************************************************/
479 : /* transcode() */
480 : /************************************************************************/
481 :
482 3790250 : CPLString transcode(const XMLCh *panXMLString, int nLimitingChars)
483 : {
484 3790250 : CPLString osRet;
485 3790250 : transcode(panXMLString, osRet, nLimitingChars);
486 3790250 : return osRet;
487 : }
488 :
489 4323200 : CPLString &transcode(const XMLCh *panXMLString, CPLString &osRet,
490 : int nLimitingChars)
491 : {
492 4323200 : if (panXMLString == nullptr)
493 : {
494 0 : osRet = "(null)";
495 0 : return osRet;
496 : }
497 :
498 4323200 : osRet.clear();
499 4323200 : if (nLimitingChars > 0)
500 106306 : osRet.reserve(nLimitingChars);
501 :
502 4323200 : bool bSimpleASCII = true;
503 4323200 : int nChars = 0;
504 4323200 : for (int i = 0;
505 144855000 : panXMLString[i] != 0 && (nLimitingChars < 0 || i < nLimitingChars);
506 : i++)
507 : {
508 140532000 : if (panXMLString[i] > 127)
509 : {
510 2416 : bSimpleASCII = false;
511 : }
512 140532000 : osRet += static_cast<char>(panXMLString[i]);
513 140532000 : nChars++;
514 : }
515 :
516 4323200 : if (bSimpleASCII)
517 4322000 : return osRet;
518 :
519 : /* -------------------------------------------------------------------- */
520 : /* The simple translation was wrong, because the source is not */
521 : /* all simple ASCII characters. Redo using the more expensive */
522 : /* recoding API. */
523 : /* -------------------------------------------------------------------- */
524 : wchar_t *pwszSource =
525 1199 : static_cast<wchar_t *>(CPLMalloc(sizeof(wchar_t) * (nChars + 1)));
526 664922 : for (int i = 0; i < nChars; i++)
527 663723 : pwszSource[i] = panXMLString[i];
528 1199 : pwszSource[nChars] = 0;
529 :
530 1199 : char *pszResult = CPLRecodeFromWChar(pwszSource, "WCHAR_T", CPL_ENC_UTF8);
531 :
532 1199 : osRet = pszResult;
533 :
534 1199 : CPLFree(pwszSource);
535 1199 : CPLFree(pszResult);
536 :
537 1199 : return osRet;
538 : }
539 :
540 : } // namespace OGR
541 :
542 : /************************************************************************/
543 : /* OGRXercesInputSource */
544 : /************************************************************************/
545 :
546 28 : class OGRXercesInputSource : public InputSource
547 : {
548 : OGRXercesBinInputStream *pBinInputStream;
549 :
550 : CPL_DISALLOW_COPY_ASSIGN(OGRXercesInputSource)
551 :
552 : public:
553 : explicit OGRXercesInputSource(
554 : VSILFILE *fp,
555 : MemoryManager *const manager = XMLPlatformUtils::fgMemoryManager);
556 : ~OGRXercesInputSource() override;
557 :
558 14 : BinInputStream *makeStream() const override
559 : {
560 14 : return pBinInputStream;
561 : }
562 : };
563 :
564 : /************************************************************************/
565 : /* OGRXercesBinInputStream() */
566 : /************************************************************************/
567 :
568 14 : OGRXercesBinInputStream::OGRXercesBinInputStream(VSILFILE *fpIn, bool bOwnFPIn)
569 14 : : fp(fpIn), bOwnFP(bOwnFPIn)
570 : {
571 14 : }
572 :
573 : /************************************************************************/
574 : /* ~OGRXercesBinInputStream() */
575 : /************************************************************************/
576 :
577 28 : OGRXercesBinInputStream::~OGRXercesBinInputStream()
578 : {
579 14 : if (bOwnFP)
580 0 : VSIFCloseL(fp);
581 28 : }
582 :
583 : /************************************************************************/
584 : /* curPos() */
585 : /************************************************************************/
586 :
587 0 : XMLFilePos OGRXercesBinInputStream::curPos() const
588 : {
589 0 : return static_cast<XMLFilePos>(VSIFTellL(fp));
590 : }
591 :
592 : /************************************************************************/
593 : /* readBytes() */
594 : /************************************************************************/
595 :
596 47 : XMLSize_t OGRXercesBinInputStream::readBytes(XMLByte *const toFill,
597 : const XMLSize_t maxToRead)
598 : {
599 47 : return static_cast<XMLSize_t>(VSIFReadL(toFill, 1, maxToRead, fp));
600 : }
601 :
602 : /************************************************************************/
603 : /* OGRXercesInputSource() */
604 : /************************************************************************/
605 :
606 14 : OGRXercesInputSource::OGRXercesInputSource(VSILFILE *fp,
607 14 : MemoryManager *const manager)
608 : : InputSource(manager),
609 14 : pBinInputStream(new OGRXercesBinInputStream(fp, false))
610 : {
611 14 : }
612 :
613 : /************************************************************************/
614 : /* ~OGRXercesInputSource() */
615 : /************************************************************************/
616 :
617 : OGRXercesInputSource::~OGRXercesInputSource() = default;
618 :
619 : /************************************************************************/
620 : /* OGRCreateXercesInputSource() */
621 : /************************************************************************/
622 :
623 14 : InputSource *OGRCreateXercesInputSource(VSILFILE *fp)
624 : {
625 14 : return new OGRXercesInputSource(fp);
626 : }
627 :
628 : /************************************************************************/
629 : /* OGRDestroyXercesInputSource() */
630 : /************************************************************************/
631 :
632 531 : void OGRDestroyXercesInputSource(InputSource *is)
633 : {
634 531 : delete is;
635 531 : }
636 :
637 : #endif // HAVE_XERCES
|