Bug Summary

File:home/maarten/src/libreoffice/core/test/source/diff/diff.cxx
Warning:line 177, column 24
Access to field 'name' results in a dereference of a null pointer (loaded from variable 'root1')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name diff.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /usr/include/libxml2 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D OOO_DLLIMPLEMENTATION_TEST -D SYSTEM_LIBXML -D HAVE_VALGRIND_HEADERS -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/cppunit/include -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/test/source/diff/diff.cxx
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10#define USE_CPPUNIT1 1
11
12#include <test/xmldiff.hxx>
13
14#include <libxml/xpath.h>
15#include <libxml/parser.h>
16
17#include <set>
18#include <sstream>
19#include <cassert>
20#include <vector>
21
22#if USE_CPPUNIT1
23#include <cppunit/TestAssert.h>
24#endif
25
26#include <rtl/math.hxx>
27
28namespace {
29
30struct tolerance
31{
32 ~tolerance()
33 {
34 xmlFree(elementName);
35 xmlFree(attribName);
36 }
37
38 tolerance()
39 : elementName(nullptr)
40 , attribName(nullptr)
41 , relative(false)
42 , value(0.0)
43 {
44 }
45
46 tolerance(const tolerance& tol)
47 {
48 elementName = xmlStrdup(tol.elementName);
49 attribName = xmlStrdup(tol.attribName);
50 relative = tol.relative;
51 value = tol.value;
52 }
53
54 xmlChar* elementName;
55 xmlChar* attribName;
56 bool relative;
57 double value;
58 bool operator<(const tolerance& rTol) const
59 {
60 int cmp = xmlStrcmp(elementName, rTol.elementName);
61 if(cmp == 0)
62 {
63 cmp = xmlStrcmp(attribName, rTol.attribName);
64 }
65
66 return cmp < 0;
67 }
68};
69
70class XMLDiff
71{
72public:
73 XMLDiff(const char* pFileName, const char* pContent, int size, const char* pToleranceFileName);
74 ~XMLDiff();
75
76 bool compare();
77private:
78 typedef std::set<tolerance> ToleranceContainer;
79
80 void loadToleranceFile(xmlDocPtr xmlTolerance);
81 bool compareAttributes(xmlNodePtr node1, xmlNodePtr node2);
82 bool compareElements(xmlNode* node1, xmlNode* node2);
83
84 /// Error message for cppunit that prints out when expected and found are not equal.
85 void cppunitAssertEqual(const xmlChar *expected, const xmlChar *found);
86
87 /// Error message for cppunit that prints out when expected and found are not equal - for doubles.
88 void cppunitAssertEqualDouble(const xmlNodePtr node, const xmlAttrPtr attr, double expected, double found, double delta);
89
90 ToleranceContainer toleranceContainer;
91 xmlDocPtr xmlFile1;
92 xmlDocPtr xmlFile2;
93 std::string fileName;
94};
95
96}
97
98XMLDiff::XMLDiff( const char* pFileName, const char* pContent, int size, const char* pToleranceFile)
99 : xmlFile1(xmlParseFile(pFileName))
100 , xmlFile2(xmlParseMemory(pContent, size))
101 , fileName(pFileName)
102{
103 if(pToleranceFile)
104 {
105 xmlDocPtr xmlToleranceFile = xmlParseFile(pToleranceFile);
106 loadToleranceFile(xmlToleranceFile);
107 xmlFreeDoc(xmlToleranceFile);
108 }
109}
110
111XMLDiff::~XMLDiff()
112{
113 xmlFreeDoc(xmlFile1);
114 xmlFreeDoc(xmlFile2);
115}
116
117namespace {
118
119void readAttributesForTolerance(xmlNodePtr node, tolerance& tol)
120{
121 xmlChar* elementName = xmlGetProp(node, BAD_CAST(xmlChar *)("elementName"));
122 tol.elementName = elementName;
123
124 xmlChar* attribName = xmlGetProp(node, BAD_CAST(xmlChar *)("attribName"));
125 tol.attribName = attribName;
126
127 xmlChar* value = xmlGetProp(node, BAD_CAST(xmlChar *)("value"));
128 double val = xmlXPathCastStringToNumber(value);
129 xmlFree(value);
130 tol.value = val;
131
132 xmlChar* relative = xmlGetProp(node, BAD_CAST(xmlChar *)("relative"));
133 bool rel = false;
134 if(xmlStrEqual(relative, BAD_CAST(xmlChar *)("true")))
135 rel = true;
136 xmlFree(relative);
137 tol.relative = rel;
138}
139
140}
141
142void XMLDiff::loadToleranceFile(xmlDocPtr xmlToleranceFile)
143{
144 xmlNodePtr root = xmlDocGetRootElement(xmlToleranceFile);
145#if USE_CPPUNIT1
146 CPPUNIT_ASSERT_MESSAGE("did not find correct tolerance file", xmlStrEqual( root->name, BAD_CAST("tolerances") ))( CppUnit::Asserter::failIf( !(xmlStrEqual( root->name, (xmlChar
*)("tolerances") )), CppUnit::Message( "assertion failed", "Expression: "
"xmlStrEqual( root->name, BAD_CAST(\"tolerances\") )", CppUnit
::message_to_string("did not find correct tolerance file") ),
CppUnit::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx"
, 146 ) ) )
;
147#else
148 if(!xmlStrEqual( root->name, BAD_CAST(xmlChar *)("tolerances") ))
149 {
150 assert(false)(static_cast <bool> (false) ? void (0) : __assert_fail (
"false", "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx"
, 150, __extension__ __PRETTY_FUNCTION__))
;
151 return;
152 }
153#endif
154 xmlNodePtr child = nullptr;
155 for (child = root->children; child != nullptr; child = child->next)
156 {
157 // assume a valid xml file
158 if(child->type != XML_ELEMENT_NODE)
159 continue;
160
161 assert(xmlStrEqual(child->name, BAD_CAST("tolerance")))(static_cast <bool> (xmlStrEqual(child->name, (xmlChar
*)("tolerance"))) ? void (0) : __assert_fail ("xmlStrEqual(child->name, BAD_CAST(\"tolerance\"))"
, "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx"
, 161, __extension__ __PRETTY_FUNCTION__))
;
162
163 tolerance tol;
164 readAttributesForTolerance(child, tol);
165 toleranceContainer.insert(tol);
166 }
167}
168
169bool XMLDiff::compare()
170{
171 xmlNode* root1 = xmlDocGetRootElement(xmlFile1);
2
'root1' initialized here
172 xmlNode* root2 = xmlDocGetRootElement(xmlFile2);
173
174#if USE_CPPUNIT1
175 CPPUNIT_ASSERT(root1)( CppUnit::Asserter::failIf( !(root1), CppUnit::Message( "assertion failed"
, "Expression: " "root1"), CppUnit::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx"
, 175 ) ) )
;
3
Assuming 'root1' is null
4
Assuming pointer value is null
176 CPPUNIT_ASSERT(root2)( CppUnit::Asserter::failIf( !(root2), CppUnit::Message( "assertion failed"
, "Expression: " "root2"), CppUnit::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx"
, 176 ) ) )
;
5
Assuming 'root2' is non-null
177 cppunitAssertEqual(root1->name, root2->name);
6
Access to field 'name' results in a dereference of a null pointer (loaded from variable 'root1')
178#else
179 if (!root1 || !root2)
180 return false;
181 if(!xmlStrEqual(root1->name, root2->name))
182 return false;
183#endif
184 return compareElements(root1, root2);
185}
186
187namespace {
188
189bool checkForEmptyChildren(xmlNodePtr node)
190{
191 if(!node)
192 return true;
193
194 for(; node != nullptr; node = node->next)
195 {
196 if (node->type == XML_ELEMENT_NODE)
197 return false;
198 }
199 return true;
200}
201
202}
203
204bool XMLDiff::compareElements(xmlNode* node1, xmlNode* node2)
205{
206#if USE_CPPUNIT1
207 cppunitAssertEqual(node1->name, node2->name);
208#else
209 if (!xmlStrEqual( node1->name, node2->name ))
210 return false;
211#endif
212
213 //compare attributes
214 bool sameAttribs = compareAttributes(node1, node2);
215#if USE_CPPUNIT1
216 CPPUNIT_ASSERT(sameAttribs)( CppUnit::Asserter::failIf( !(sameAttribs), CppUnit::Message
( "assertion failed", "Expression: " "sameAttribs"), CppUnit::
SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx"
, 216 ) ) )
;
217#else
218 if (!sameAttribs)
219 return false;
220#endif
221
222 // compare children
223 xmlNode* child2 = nullptr;
224 xmlNode* child1 = nullptr;
225 for(child1 = node1->children, child2 = node2->children; child1 != nullptr && child2 != nullptr; child1 = child1->next, child2 = child2->next)
226 {
227 if (child1->type == XML_ELEMENT_NODE)
228 {
229 bool bCompare = compareElements(child1, child2);
230 if(!bCompare)
231 {
232 return false;
233 }
234 }
235 }
236
237#if USE_CPPUNIT1
238 CPPUNIT_ASSERT(checkForEmptyChildren(child1))( CppUnit::Asserter::failIf( !(checkForEmptyChildren(child1))
, CppUnit::Message( "assertion failed", "Expression: " "checkForEmptyChildren(child1)"
), CppUnit::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx"
, 238 ) ) )
;
239 CPPUNIT_ASSERT(checkForEmptyChildren(child2))( CppUnit::Asserter::failIf( !(checkForEmptyChildren(child2))
, CppUnit::Message( "assertion failed", "Expression: " "checkForEmptyChildren(child2)"
), CppUnit::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx"
, 239 ) ) )
;
240#else
241 if(!checkForEmptyChildren(child1) || !checkForEmptyChildren(child2))
242 return false;
243#endif
244
245 return true;
246}
247
248void XMLDiff::cppunitAssertEqual(const xmlChar *expected, const xmlChar *found)
249{
250#if USE_CPPUNIT1
251 std::stringstream stringStream;
252 stringStream << "Reference: " << fileName << "\n- Expected: " << reinterpret_cast<const char*>(expected) << "\n- Found: " << reinterpret_cast<const char*>(found);
253
254 CPPUNIT_ASSERT_MESSAGE(stringStream.str(), xmlStrEqual(expected, found))( CppUnit::Asserter::failIf( !(xmlStrEqual(expected, found)),
CppUnit::Message( "assertion failed", "Expression: " "xmlStrEqual(expected, found)"
, CppUnit::message_to_string(stringStream.str()) ), CppUnit::
SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx"
, 254 ) ) )
;
255#endif
256}
257
258void XMLDiff::cppunitAssertEqualDouble(const xmlNodePtr node, const xmlAttrPtr attr, double expected, double found, double delta)
259{
260#if USE_CPPUNIT1
261 xmlChar * path = xmlGetNodePath(node);
262 std::stringstream stringStream;
263 stringStream << "Reference: " << fileName << "\n- Node: " << reinterpret_cast<const char*>(path) << "\n- Attr: " << reinterpret_cast<const char*>(attr->name);
264 xmlFree(path);
265
266 CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(stringStream.str(), expected, found, delta)( CppUnit::assertDoubleEquals( (expected), (found), (delta), CppUnit
::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx"
, 266 ), CppUnit::message_to_string(stringStream.str()) ) )
;
267#endif
268}
269
270namespace {
271
272bool compareValuesWithTolerance(double val1, double val2, double tolerance, bool relative)
273{
274 if(relative)
275 {
276 return (val1/tolerance) <= val2 && val2 <= (val1*tolerance);
277 }
278 else
279 {
280 return (val1 - tolerance) <= val2 && val2 <= (val1 + tolerance);
281 }
282}
283
284}
285
286bool XMLDiff::compareAttributes(xmlNodePtr node1, xmlNodePtr node2)
287{
288 xmlAttrPtr attr1 = nullptr;
289 xmlAttrPtr attr2 = nullptr;
290 for(attr1 = node1->properties, attr2 = node2->properties; attr1 != nullptr && attr2 != nullptr; attr1 = attr1->next, attr2 = attr2->next)
291 {
292#if USE_CPPUNIT1
293 cppunitAssertEqual(attr1->name, attr2->name);
294#else
295 if (!xmlStrEqual( attr1->name, attr2->name ))
296 return false;
297#endif
298
299 xmlChar* val1 = xmlGetProp(node1, attr1->name);
300 xmlChar* val2 = xmlGetProp(node2, attr2->name);
301
302 double dVal1 = xmlXPathCastStringToNumber(val1);
303 double dVal2 = xmlXPathCastStringToNumber(val2);
304
305 if(!std::isnan(dVal1) || !std::isnan(dVal2))
306 {
307 //compare by value and respect tolerance
308 tolerance tol;
309 tol.elementName = xmlStrdup(node1->name);
310 tol.attribName = xmlStrdup(attr1->name);
311 ToleranceContainer::iterator itr = toleranceContainer.find( tol );
312 bool useTolerance = false;
313 if (itr != toleranceContainer.end())
314 {
315 useTolerance = true;
316 }
317
318 if (useTolerance)
319 {
320 bool valInTolerance = compareValuesWithTolerance(dVal1, dVal2, itr->value, itr->relative);
321#if USE_CPPUNIT1
322 std::stringstream stringStream("Expected Value: ");
323 stringStream << dVal1 << "; Found Value: " << dVal2 << "; Tolerance: " << itr->value;
324 stringStream << "; Relative: " << itr->relative;
325 CPPUNIT_ASSERT_MESSAGE(stringStream.str(), valInTolerance)( CppUnit::Asserter::failIf( !(valInTolerance), CppUnit::Message
( "assertion failed", "Expression: " "valInTolerance", CppUnit
::message_to_string(stringStream.str()) ), CppUnit::SourceLine
( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx"
, 325 ) ) )
;
326#else
327 if (!valInTolerance)
328 return false;
329#endif
330 }
331 else
332 {
333#if USE_CPPUNIT1
334 cppunitAssertEqualDouble(node1, attr1, dVal1, dVal2, 1e-08);
335#else
336 if (dVal1 != dVal2)
337 return false;
338#endif
339 }
340 }
341 else
342 {
343
344#if USE_CPPUNIT1
345 cppunitAssertEqual(val1, val2);
346#else
347 if(!xmlStrEqual( val1, val2 ))
348 return false;
349#endif
350 }
351
352 xmlFree(val1);
353 xmlFree(val2);
354 }
355
356 // unequal number of attributes
357#ifdef CPPUNIT_ASSERT
358 if (attr1 || attr2)
359 {
360 std::stringstream failStream;
361 failStream << "Unequal number of attributes in ";
362 // print chain from document root
363 std::vector<std::string> parents;
364 auto n = node1;
365 while (n)
366 {
367 if (n->name)
368 parents.push_back(std::string(reinterpret_cast<const char *>(n->name)));
369 n = n->parent;
370 }
371 bool first = true;
372 for (auto it = parents.rbegin(); it != parents.rend(); ++it)
373 {
374 if (!first)
375 failStream << "->";
376 first = false;
377 failStream << *it;
378 }
379 failStream << " Attr1: ";
380 attr1 = node1->properties;
381 while (attr1 != nullptr)
382 {
383 xmlChar* val1 = xmlGetProp(node1, attr1->name);
384 failStream << BAD_CAST(xmlChar *)(attr1->name) << "=" << BAD_CAST(xmlChar *)(val1) << ", ";
385 xmlFree(val1);
386 attr1 = attr1->next;
387 }
388
389 failStream << " Attr2: ";
390 attr2 = node2->properties;
391 while (attr2 != nullptr)
392 {
393 xmlChar* val2 = xmlGetProp(node2, attr2->name);
394 failStream << BAD_CAST(xmlChar *)(attr2->name) << "=" << BAD_CAST(xmlChar *)(val2) << ", ";
395 xmlFree(val2);
396 attr2 = attr2->next;
397 }
398 CPPUNIT_ASSERT_MESSAGE(failStream.str(), false)( CppUnit::Asserter::failIf( !(false), CppUnit::Message( "assertion failed"
, "Expression: " "false", CppUnit::message_to_string(failStream
.str()) ), CppUnit::SourceLine( "/home/maarten/src/libreoffice/core/test/source/diff/diff.cxx"
, 398 ) ) )
;
399 }
400#else
401 if (attr1 || attr2)
402 return false;
403#endif
404
405 return true;
406}
407
408
409bool
410doXMLDiff(char const*const pFileName, char const*const pContent, int const size,
411 char const*const pToleranceFileName)
412{
413 XMLDiff aDiff(pFileName, pContent, size, pToleranceFileName);
414 return aDiff.compare();
1
Calling 'XMLDiff::compare'
415}
416
417/* vim:set shiftwidth=4 softtabstop=4 expandtab: */