xref: /aosp_15_r20/external/skia/src/xml/SkXMLParser.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2006 The Android Open Source Project
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/xml/SkXMLParser.h"
14*c8dee2aaSAndroid Build Coastguard Worker 
15*c8dee2aaSAndroid Build Coastguard Worker #include <expat.h>
16*c8dee2aaSAndroid Build Coastguard Worker 
17*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
18*c8dee2aaSAndroid Build Coastguard Worker 
19*c8dee2aaSAndroid Build Coastguard Worker static char const* const gErrorStrings[] = {
20*c8dee2aaSAndroid Build Coastguard Worker     "empty or missing file ",
21*c8dee2aaSAndroid Build Coastguard Worker     "unknown element ",
22*c8dee2aaSAndroid Build Coastguard Worker     "unknown attribute name ",
23*c8dee2aaSAndroid Build Coastguard Worker     "error in attribute value ",
24*c8dee2aaSAndroid Build Coastguard Worker     "duplicate ID ",
25*c8dee2aaSAndroid Build Coastguard Worker     "unknown error "
26*c8dee2aaSAndroid Build Coastguard Worker };
27*c8dee2aaSAndroid Build Coastguard Worker 
SkXMLParserError()28*c8dee2aaSAndroid Build Coastguard Worker SkXMLParserError::SkXMLParserError() : fCode(kNoError), fLineNumber(-1),
29*c8dee2aaSAndroid Build Coastguard Worker     fNativeCode(-1)
30*c8dee2aaSAndroid Build Coastguard Worker {
31*c8dee2aaSAndroid Build Coastguard Worker     reset();
32*c8dee2aaSAndroid Build Coastguard Worker }
33*c8dee2aaSAndroid Build Coastguard Worker 
~SkXMLParserError()34*c8dee2aaSAndroid Build Coastguard Worker SkXMLParserError::~SkXMLParserError()
35*c8dee2aaSAndroid Build Coastguard Worker {
36*c8dee2aaSAndroid Build Coastguard Worker     // need a virtual destructor for our subclasses
37*c8dee2aaSAndroid Build Coastguard Worker }
38*c8dee2aaSAndroid Build Coastguard Worker 
getErrorString(SkString * str) const39*c8dee2aaSAndroid Build Coastguard Worker void SkXMLParserError::getErrorString(SkString* str) const
40*c8dee2aaSAndroid Build Coastguard Worker {
41*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(str);
42*c8dee2aaSAndroid Build Coastguard Worker     SkString temp;
43*c8dee2aaSAndroid Build Coastguard Worker     if (fCode != kNoError) {
44*c8dee2aaSAndroid Build Coastguard Worker         if ((unsigned)fCode < std::size(gErrorStrings))
45*c8dee2aaSAndroid Build Coastguard Worker             temp.set(gErrorStrings[fCode - 1]);
46*c8dee2aaSAndroid Build Coastguard Worker         temp.append(fNoun);
47*c8dee2aaSAndroid Build Coastguard Worker     } else
48*c8dee2aaSAndroid Build Coastguard Worker         SkXMLParser::GetNativeErrorString(fNativeCode, &temp);
49*c8dee2aaSAndroid Build Coastguard Worker     str->append(temp);
50*c8dee2aaSAndroid Build Coastguard Worker }
51*c8dee2aaSAndroid Build Coastguard Worker 
reset()52*c8dee2aaSAndroid Build Coastguard Worker void SkXMLParserError::reset() {
53*c8dee2aaSAndroid Build Coastguard Worker     fCode = kNoError;
54*c8dee2aaSAndroid Build Coastguard Worker     fLineNumber = -1;
55*c8dee2aaSAndroid Build Coastguard Worker     fNativeCode = -1;
56*c8dee2aaSAndroid Build Coastguard Worker }
57*c8dee2aaSAndroid Build Coastguard Worker 
58*c8dee2aaSAndroid Build Coastguard Worker ////////////////
59*c8dee2aaSAndroid Build Coastguard Worker 
60*c8dee2aaSAndroid Build Coastguard Worker namespace {
61*c8dee2aaSAndroid Build Coastguard Worker 
62*c8dee2aaSAndroid Build Coastguard Worker constexpr const void* kHashSeed = &kHashSeed;
63*c8dee2aaSAndroid Build Coastguard Worker 
64*c8dee2aaSAndroid Build Coastguard Worker const XML_Memory_Handling_Suite sk_XML_alloc = {
65*c8dee2aaSAndroid Build Coastguard Worker     sk_malloc_throw,
66*c8dee2aaSAndroid Build Coastguard Worker     sk_realloc_throw,
67*c8dee2aaSAndroid Build Coastguard Worker     sk_free
68*c8dee2aaSAndroid Build Coastguard Worker };
69*c8dee2aaSAndroid Build Coastguard Worker 
70*c8dee2aaSAndroid Build Coastguard Worker struct ParsingContext {
ParsingContext__anonbcbf8b420111::ParsingContext71*c8dee2aaSAndroid Build Coastguard Worker     ParsingContext(SkXMLParser* parser)
72*c8dee2aaSAndroid Build Coastguard Worker         : fParser(parser)
73*c8dee2aaSAndroid Build Coastguard Worker         , fXMLParser(XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr)) { }
74*c8dee2aaSAndroid Build Coastguard Worker 
flushText__anonbcbf8b420111::ParsingContext75*c8dee2aaSAndroid Build Coastguard Worker     void flushText() {
76*c8dee2aaSAndroid Build Coastguard Worker         if (!fBufferedText.empty()) {
77*c8dee2aaSAndroid Build Coastguard Worker             fParser->text(fBufferedText.data(), SkTo<int>(fBufferedText.size()));
78*c8dee2aaSAndroid Build Coastguard Worker             fBufferedText.clear();
79*c8dee2aaSAndroid Build Coastguard Worker         }
80*c8dee2aaSAndroid Build Coastguard Worker     }
81*c8dee2aaSAndroid Build Coastguard Worker 
appendText__anonbcbf8b420111::ParsingContext82*c8dee2aaSAndroid Build Coastguard Worker     void appendText(const char* txt, size_t len) {
83*c8dee2aaSAndroid Build Coastguard Worker         fBufferedText.insert(fBufferedText.end(), txt, &txt[len]);
84*c8dee2aaSAndroid Build Coastguard Worker     }
85*c8dee2aaSAndroid Build Coastguard Worker 
86*c8dee2aaSAndroid Build Coastguard Worker     SkXMLParser* fParser;
87*c8dee2aaSAndroid Build Coastguard Worker     SkAutoTCallVProc<std::remove_pointer_t<XML_Parser>, XML_ParserFree> fXMLParser;
88*c8dee2aaSAndroid Build Coastguard Worker 
89*c8dee2aaSAndroid Build Coastguard Worker private:
90*c8dee2aaSAndroid Build Coastguard Worker     std::vector<char> fBufferedText;
91*c8dee2aaSAndroid Build Coastguard Worker };
92*c8dee2aaSAndroid Build Coastguard Worker 
93*c8dee2aaSAndroid Build Coastguard Worker #define HANDLER_CONTEXT(arg, name) ParsingContext* name = static_cast<ParsingContext*>(arg)
94*c8dee2aaSAndroid Build Coastguard Worker 
start_element_handler(void * data,const char * tag,const char ** attributes)95*c8dee2aaSAndroid Build Coastguard Worker void XMLCALL start_element_handler(void *data, const char* tag, const char** attributes) {
96*c8dee2aaSAndroid Build Coastguard Worker     HANDLER_CONTEXT(data, ctx);
97*c8dee2aaSAndroid Build Coastguard Worker     ctx->flushText();
98*c8dee2aaSAndroid Build Coastguard Worker 
99*c8dee2aaSAndroid Build Coastguard Worker     ctx->fParser->startElement(tag);
100*c8dee2aaSAndroid Build Coastguard Worker 
101*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 0; attributes[i]; i += 2) {
102*c8dee2aaSAndroid Build Coastguard Worker         ctx->fParser->addAttribute(attributes[i], attributes[i + 1]);
103*c8dee2aaSAndroid Build Coastguard Worker     }
104*c8dee2aaSAndroid Build Coastguard Worker }
105*c8dee2aaSAndroid Build Coastguard Worker 
end_element_handler(void * data,const char * tag)106*c8dee2aaSAndroid Build Coastguard Worker void XMLCALL end_element_handler(void* data, const char* tag) {
107*c8dee2aaSAndroid Build Coastguard Worker     HANDLER_CONTEXT(data, ctx);
108*c8dee2aaSAndroid Build Coastguard Worker     ctx->flushText();
109*c8dee2aaSAndroid Build Coastguard Worker 
110*c8dee2aaSAndroid Build Coastguard Worker     ctx->fParser->endElement(tag);
111*c8dee2aaSAndroid Build Coastguard Worker }
112*c8dee2aaSAndroid Build Coastguard Worker 
text_handler(void * data,const char * txt,int len)113*c8dee2aaSAndroid Build Coastguard Worker void XMLCALL text_handler(void *data, const char* txt, int len) {
114*c8dee2aaSAndroid Build Coastguard Worker     HANDLER_CONTEXT(data, ctx);
115*c8dee2aaSAndroid Build Coastguard Worker 
116*c8dee2aaSAndroid Build Coastguard Worker     ctx->appendText(txt, SkTo<size_t>(len));
117*c8dee2aaSAndroid Build Coastguard Worker }
118*c8dee2aaSAndroid Build Coastguard Worker 
entity_decl_handler(void * data,const XML_Char * entityName,int is_parameter_entity,const XML_Char * value,int value_length,const XML_Char * base,const XML_Char * systemId,const XML_Char * publicId,const XML_Char * notationName)119*c8dee2aaSAndroid Build Coastguard Worker void XMLCALL entity_decl_handler(void *data,
120*c8dee2aaSAndroid Build Coastguard Worker                                  const XML_Char *entityName,
121*c8dee2aaSAndroid Build Coastguard Worker                                  int is_parameter_entity,
122*c8dee2aaSAndroid Build Coastguard Worker                                  const XML_Char *value,
123*c8dee2aaSAndroid Build Coastguard Worker                                  int value_length,
124*c8dee2aaSAndroid Build Coastguard Worker                                  const XML_Char *base,
125*c8dee2aaSAndroid Build Coastguard Worker                                  const XML_Char *systemId,
126*c8dee2aaSAndroid Build Coastguard Worker                                  const XML_Char *publicId,
127*c8dee2aaSAndroid Build Coastguard Worker                                  const XML_Char *notationName) {
128*c8dee2aaSAndroid Build Coastguard Worker     HANDLER_CONTEXT(data, ctx);
129*c8dee2aaSAndroid Build Coastguard Worker 
130*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGF("'%s' entity declaration found, stopping processing", entityName);
131*c8dee2aaSAndroid Build Coastguard Worker     XML_StopParser(ctx->fXMLParser, XML_FALSE);
132*c8dee2aaSAndroid Build Coastguard Worker }
133*c8dee2aaSAndroid Build Coastguard Worker 
134*c8dee2aaSAndroid Build Coastguard Worker } // anonymous namespace
135*c8dee2aaSAndroid Build Coastguard Worker 
SkXMLParser(SkXMLParserError * parserError)136*c8dee2aaSAndroid Build Coastguard Worker SkXMLParser::SkXMLParser(SkXMLParserError* parserError) : fParser(nullptr), fError(parserError)
137*c8dee2aaSAndroid Build Coastguard Worker {
138*c8dee2aaSAndroid Build Coastguard Worker }
139*c8dee2aaSAndroid Build Coastguard Worker 
~SkXMLParser()140*c8dee2aaSAndroid Build Coastguard Worker SkXMLParser::~SkXMLParser()
141*c8dee2aaSAndroid Build Coastguard Worker {
142*c8dee2aaSAndroid Build Coastguard Worker }
143*c8dee2aaSAndroid Build Coastguard Worker 
parse(SkStream & docStream)144*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::parse(SkStream& docStream)
145*c8dee2aaSAndroid Build Coastguard Worker {
146*c8dee2aaSAndroid Build Coastguard Worker     ParsingContext ctx(this);
147*c8dee2aaSAndroid Build Coastguard Worker     if (!ctx.fXMLParser) {
148*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGF("could not create XML parser\n");
149*c8dee2aaSAndroid Build Coastguard Worker         return false;
150*c8dee2aaSAndroid Build Coastguard Worker     }
151*c8dee2aaSAndroid Build Coastguard Worker 
152*c8dee2aaSAndroid Build Coastguard Worker     // Avoid calls to rand_s if this is not set. This seed helps prevent DOS
153*c8dee2aaSAndroid Build Coastguard Worker     // with a known hash sequence so an address is sufficient. The provided
154*c8dee2aaSAndroid Build Coastguard Worker     // seed should not be zero as that results in a call to rand_s.
155*c8dee2aaSAndroid Build Coastguard Worker     unsigned long seed = static_cast<unsigned long>(
156*c8dee2aaSAndroid Build Coastguard Worker         reinterpret_cast<size_t>(kHashSeed) & 0xFFFFFFFF);
157*c8dee2aaSAndroid Build Coastguard Worker     XML_SetHashSalt(ctx.fXMLParser, seed ? seed : 1);
158*c8dee2aaSAndroid Build Coastguard Worker 
159*c8dee2aaSAndroid Build Coastguard Worker     XML_SetUserData(ctx.fXMLParser, &ctx);
160*c8dee2aaSAndroid Build Coastguard Worker     XML_SetElementHandler(ctx.fXMLParser, start_element_handler, end_element_handler);
161*c8dee2aaSAndroid Build Coastguard Worker     XML_SetCharacterDataHandler(ctx.fXMLParser, text_handler);
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker     // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340.
164*c8dee2aaSAndroid Build Coastguard Worker     XML_SetEntityDeclHandler(ctx.fXMLParser, entity_decl_handler);
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker     XML_Status status = XML_STATUS_OK;
167*c8dee2aaSAndroid Build Coastguard Worker     if (docStream.getMemoryBase() && docStream.hasLength()) {
168*c8dee2aaSAndroid Build Coastguard Worker         const char* base = reinterpret_cast<const char*>(docStream.getMemoryBase());
169*c8dee2aaSAndroid Build Coastguard Worker         status = XML_Parse(ctx.fXMLParser,
170*c8dee2aaSAndroid Build Coastguard Worker                            base + docStream.getPosition(),
171*c8dee2aaSAndroid Build Coastguard Worker                            docStream.getLength() - docStream.getPosition(),
172*c8dee2aaSAndroid Build Coastguard Worker                            true);
173*c8dee2aaSAndroid Build Coastguard Worker     } else {
174*c8dee2aaSAndroid Build Coastguard Worker         static constexpr int kBufferSize = 4096;
175*c8dee2aaSAndroid Build Coastguard Worker         bool done = false;
176*c8dee2aaSAndroid Build Coastguard Worker         do {
177*c8dee2aaSAndroid Build Coastguard Worker             void* buffer = XML_GetBuffer(ctx.fXMLParser, kBufferSize);
178*c8dee2aaSAndroid Build Coastguard Worker             if (!buffer) {
179*c8dee2aaSAndroid Build Coastguard Worker                 SkDEBUGF("could not buffer enough to continue\n");
180*c8dee2aaSAndroid Build Coastguard Worker                 return false;
181*c8dee2aaSAndroid Build Coastguard Worker             }
182*c8dee2aaSAndroid Build Coastguard Worker 
183*c8dee2aaSAndroid Build Coastguard Worker             size_t len = docStream.read(buffer, kBufferSize);
184*c8dee2aaSAndroid Build Coastguard Worker             done = docStream.isAtEnd();
185*c8dee2aaSAndroid Build Coastguard Worker             status = XML_ParseBuffer(ctx.fXMLParser, SkToS32(len), done);
186*c8dee2aaSAndroid Build Coastguard Worker             if (XML_STATUS_ERROR == status) {
187*c8dee2aaSAndroid Build Coastguard Worker                 break;
188*c8dee2aaSAndroid Build Coastguard Worker             }
189*c8dee2aaSAndroid Build Coastguard Worker         } while (!done);
190*c8dee2aaSAndroid Build Coastguard Worker     }
191*c8dee2aaSAndroid Build Coastguard Worker     if (XML_STATUS_ERROR == status) {
192*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_DEBUG)
193*c8dee2aaSAndroid Build Coastguard Worker         XML_Error error = XML_GetErrorCode(ctx.fXMLParser);
194*c8dee2aaSAndroid Build Coastguard Worker         int line = XML_GetCurrentLineNumber(ctx.fXMLParser);
195*c8dee2aaSAndroid Build Coastguard Worker         int column = XML_GetCurrentColumnNumber(ctx.fXMLParser);
196*c8dee2aaSAndroid Build Coastguard Worker         const XML_LChar* errorString = XML_ErrorString(error);
197*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGF("parse error @%d:%d: %d (%s).\n", line, column, error, errorString);
198*c8dee2aaSAndroid Build Coastguard Worker #endif
199*c8dee2aaSAndroid Build Coastguard Worker         return false;
200*c8dee2aaSAndroid Build Coastguard Worker     }
201*c8dee2aaSAndroid Build Coastguard Worker 
202*c8dee2aaSAndroid Build Coastguard Worker     return true;
203*c8dee2aaSAndroid Build Coastguard Worker }
204*c8dee2aaSAndroid Build Coastguard Worker 
parse(const char doc[],size_t len)205*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::parse(const char doc[], size_t len)
206*c8dee2aaSAndroid Build Coastguard Worker {
207*c8dee2aaSAndroid Build Coastguard Worker     SkMemoryStream docStream(doc, len);
208*c8dee2aaSAndroid Build Coastguard Worker     return this->parse(docStream);
209*c8dee2aaSAndroid Build Coastguard Worker }
210*c8dee2aaSAndroid Build Coastguard Worker 
GetNativeErrorString(int error,SkString * str)211*c8dee2aaSAndroid Build Coastguard Worker void SkXMLParser::GetNativeErrorString(int error, SkString* str)
212*c8dee2aaSAndroid Build Coastguard Worker {
213*c8dee2aaSAndroid Build Coastguard Worker 
214*c8dee2aaSAndroid Build Coastguard Worker }
215*c8dee2aaSAndroid Build Coastguard Worker 
startElement(const char elem[])216*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::startElement(const char elem[])
217*c8dee2aaSAndroid Build Coastguard Worker {
218*c8dee2aaSAndroid Build Coastguard Worker     return this->onStartElement(elem);
219*c8dee2aaSAndroid Build Coastguard Worker }
220*c8dee2aaSAndroid Build Coastguard Worker 
addAttribute(const char name[],const char value[])221*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::addAttribute(const char name[], const char value[])
222*c8dee2aaSAndroid Build Coastguard Worker {
223*c8dee2aaSAndroid Build Coastguard Worker     return this->onAddAttribute(name, value);
224*c8dee2aaSAndroid Build Coastguard Worker }
225*c8dee2aaSAndroid Build Coastguard Worker 
endElement(const char elem[])226*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::endElement(const char elem[])
227*c8dee2aaSAndroid Build Coastguard Worker {
228*c8dee2aaSAndroid Build Coastguard Worker     return this->onEndElement(elem);
229*c8dee2aaSAndroid Build Coastguard Worker }
230*c8dee2aaSAndroid Build Coastguard Worker 
text(const char text[],int len)231*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::text(const char text[], int len)
232*c8dee2aaSAndroid Build Coastguard Worker {
233*c8dee2aaSAndroid Build Coastguard Worker     return this->onText(text, len);
234*c8dee2aaSAndroid Build Coastguard Worker }
235*c8dee2aaSAndroid Build Coastguard Worker 
236*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
237*c8dee2aaSAndroid Build Coastguard Worker 
onStartElement(const char elem[])238*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::onStartElement(const char elem[]) {return false; }
onAddAttribute(const char name[],const char value[])239*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::onAddAttribute(const char name[], const char value[]) {return false; }
onEndElement(const char elem[])240*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::onEndElement(const char elem[]) { return false; }
onText(const char text[],int len)241*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::onText(const char text[], int len) {return false; }
242