Bug Summary

File:home/maarten/src/libreoffice/core/sot/source/sdstor/stgio.cxx
Warning:line 215, column 16
Division by zero

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 stgio.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 -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 SOT_DLLIMPLEMENTATION -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/sot/inc -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/sot/source/sdstor/stgio.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 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20
21#include "stgelem.hxx"
22#include "stgcache.hxx"
23#include "stgstrms.hxx"
24#include "stgdir.hxx"
25#include "stgio.hxx"
26#include <o3tl/safeint.hxx>
27#include <sal/log.hxx>
28
29#include <memory>
30
31///////////////////////////// class StgIo
32
33// This class holds the storage header and all internal streams.
34
35StgIo::StgIo() : StgCache()
36{
37 m_pTOC = nullptr;
38 m_pDataFAT = nullptr;
39 m_pDataStrm = nullptr;
40 m_pFAT = nullptr;
41 m_bCopied = false;
42}
43
44StgIo::~StgIo()
45{
46 delete m_pTOC;
47 delete m_pDataFAT;
48 delete m_pDataStrm;
49 delete m_pFAT;
50}
51
52// Load the header. Do not set an error code if the header is invalid.
53
54bool StgIo::Load()
55{
56 if( GetStrm() )
57 {
58 if( m_aHdr.Load( *this ) )
59 {
60 if( m_aHdr.Check() )
61 SetupStreams();
62 else
63 return false;
64 }
65 else
66 return false;
67 }
68 return Good();
69}
70
71// Set up an initial, empty storage
72
73bool StgIo::Init()
74{
75 m_aHdr.Init();
76 SetupStreams();
77 return CommitAll();
78}
79
80void StgIo::SetupStreams()
81{
82 delete m_pTOC;
83 delete m_pDataFAT;
84 delete m_pDataStrm;
85 delete m_pFAT;
86 m_pTOC = nullptr;
87 m_pDataFAT = nullptr;
88 m_pDataStrm = nullptr;
89 m_pFAT = nullptr;
90 ResetError();
91
92 short nPhysPageSize = 1 << m_aHdr.GetPageSize();
93 SetPhysPageSize(nPhysPageSize);
94 sal_Int32 nFatStrmSize;
95 if (o3tl::checked_multiply<sal_Int32>(m_aHdr.GetFATSize(), nPhysPageSize, nFatStrmSize))
96 {
97 SAL_WARN("sot", "Error: " << m_aHdr.GetFATSize() << " * " << nPhysPageSize << " would overflow")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "sot")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Error: " << m_aHdr.GetFATSize() << " * "
<< nPhysPageSize << " would overflow") == 1) { ::
sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgio.cxx"
":" "97" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "Error: " << m_aHdr.GetFATSize()
<< " * " << nPhysPageSize << " would overflow"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Error: " << m_aHdr.GetFATSize() << " * "
<< nPhysPageSize << " would overflow"; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgio.cxx"
":" "97" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "Error: " << m_aHdr.GetFATSize() << " * "
<< nPhysPageSize << " would overflow") == 1) { ::
sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgio.cxx"
":" "97" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "Error: " << m_aHdr.GetFATSize()
<< " * " << nPhysPageSize << " would overflow"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "Error: " << m_aHdr.GetFATSize() << " * "
<< nPhysPageSize << " would overflow"; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgio.cxx"
":" "97" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
98 SetError(SVSTREAM_FILEFORMAT_ERRORErrCode( ErrCodeArea::Io, ErrCodeClass::Format, 21 ));
99 m_pFAT = nullptr;
100 m_pTOC = nullptr;
101 return;
102 }
103
104 m_pFAT = new StgFATStrm(*this, nFatStrmSize);
105 m_pTOC = new StgDirStrm(*this);
106 if( GetError() )
107 return;
108
109 StgDirEntry* pRoot = m_pTOC->GetRoot();
110 if( pRoot )
111 {
112 m_pDataFAT = new StgDataStrm( *this, m_aHdr.GetDataFATStart(), -1 );
113 m_pDataStrm = new StgDataStrm( *this, *pRoot );
114 m_pDataFAT->SetIncrement( 1 << m_aHdr.GetPageSize() );
115 m_pDataStrm->SetIncrement( GetDataPageSize() );
116 m_pDataStrm->SetEntry( *pRoot );
117 }
118 else
119 SetError( SVSTREAM_FILEFORMAT_ERRORErrCode( ErrCodeArea::Io, ErrCodeClass::Format, 21 ) );
120}
121
122// get the logical data page size
123
124short StgIo::GetDataPageSize() const
125{
126 return 1 << m_aHdr.GetDataPageSize();
127}
128
129// Commit everything
130
131bool StgIo::CommitAll()
132{
133 // Store the data (all streams and the TOC)
134 if( m_pTOC && m_pTOC->Store() && m_pDataFAT )
135 {
136 if( Commit() )
137 {
138 m_aHdr.SetDataFATStart( m_pDataFAT->GetStart() );
139 m_aHdr.SetDataFATSize( m_pDataFAT->GetPages() );
140 m_aHdr.SetTOCStart( m_pTOC->GetStart() );
141 if( m_aHdr.Store( *this ) )
142 {
143 GetStrm()->Flush();
144 const ErrCode n = GetStrm()->GetError();
145 SetError( n );
146#ifdef DBG_UTIL
147 if( n==ERRCODE_NONEErrCode(0) ) ValidateFATs();
148#endif
149 return n == ERRCODE_NONEErrCode(0);
150 }
151 }
152 }
153 SetError( SVSTREAM_WRITE_ERRORErrCode( ErrCodeArea::Io, ErrCodeClass::Write, 16 ) );
154 return false;
155}
156
157namespace {
158
159class EasyFat
160{
161 std::unique_ptr<sal_Int32[]> pFat;
162 std::unique_ptr<bool[]> pFree;
163 sal_Int32 nPages;
164 sal_Int32 nPageSize;
165
166public:
167 EasyFat( StgIo & rIo, StgStrm *pFatStream, sal_Int32 nPSize );
168
169 sal_Int32 GetPageSize() const { return nPageSize; }
33
Returning zero
170
171 FatError Mark( sal_Int32 nPage, sal_Int32 nCount, sal_Int32 nExpect );
172 bool HasUnrefChains() const;
173};
174
175}
176
177EasyFat::EasyFat( StgIo& rIo, StgStrm* pFatStream, sal_Int32 nPSize )
178 : nPages(pFatStream->GetSize() >> 2), nPageSize(nPSize)
6
Value assigned to field 'nPageSize'
179{
180 pFat.reset( new sal_Int32[ nPages ] );
181 pFree.reset( new bool[ nPages ] );
182
183 rtl::Reference< StgPage > pPage;
184 sal_Int32 nFatPageSize = (1 << rIo.m_aHdr.GetPageSize()) - 2;
185
186 for( sal_Int32 nPage = 0; nPage < nPages; nPage++ )
7
Assuming 'nPage' is >= field 'nPages'
8
Loop condition is false. Execution continues on line 112
187 {
188 if( ! (nPage % nFatPageSize) )
189 {
190 pFatStream->Pos2Page( nPage << 2 );
191 sal_Int32 nPhysPage = pFatStream->GetPage();
192 pPage = rIo.Get( nPhysPage, true );
193 }
194
195 pFat[ nPage ] = StgCache::GetFromPage( pPage, short( nPage % nFatPageSize ) );
196 pFree[ nPage ] = true;
197 }
198}
199
200bool EasyFat::HasUnrefChains() const
201{
202 for( sal_Int32 nPage = 0; nPage < nPages; nPage++ )
203 {
204 if( pFree[ nPage ] && pFat[ nPage ] != -1 )
205 return true;
206 }
207 return false;
208}
209
210FatError EasyFat::Mark( sal_Int32 nPage, sal_Int32 nCount, sal_Int32 nExpect )
211{
212 if( nCount > 0 )
17
Assuming 'nCount' is <= 0
18
Taking false branch
30
Assuming 'nCount' is > 0
31
Taking true branch
213 {
214 --nCount;
215 nCount /= GetPageSize();
32
Calling 'EasyFat::GetPageSize'
34
Returning from 'EasyFat::GetPageSize'
35
Division by zero
216 ++nCount;
217 }
218
219 sal_Int32 nCurPage = nPage;
220 while( nCount != 0 )
19
Assuming 'nCount' is equal to 0
20
Loop condition is false. Execution continues on line 238
221 {
222 if( nCurPage < 0 || nCurPage >= nPages )
223 return FatError::OutOfBounds;
224 pFree[ nCurPage ] = false;
225 nCurPage = pFat[ nCurPage ];
226 // stream too long
227 if( nCurPage != nExpect && nCount == 1 )
228 return FatError::WrongLength;
229 // stream too short
230 if( nCurPage == nExpect && nCount != 1 && nCount != -1 )
231 return FatError::WrongLength;
232 // last block for stream without length
233 if( nCurPage == nExpect && nCount == -1 )
234 nCount = 1;
235 if( nCount != -1 )
236 nCount--;
237 }
238 return FatError::Ok;
239}
240
241namespace {
242
243class Validator
244{
245 FatError nError;
246
247 EasyFat aSmallFat;
248 EasyFat aFat;
249
250 StgIo &rIo;
251
252 FatError ValidateMasterFATs();
253 FatError ValidateDirectoryEntries();
254 FatError FindUnrefedChains() const;
255 FatError MarkAll( StgDirEntry *pEntry );
256
257public:
258 explicit Validator( StgIo &rIo );
259 bool IsError() const { return nError != FatError::Ok; }
260};
261
262}
263
264Validator::Validator( StgIo &rIoP )
265 : aSmallFat( rIoP, rIoP.m_pDataFAT, 1 << rIoP.m_aHdr.GetDataPageSize() ),
266 aFat( rIoP, rIoP.m_pFAT, 1 << rIoP.m_aHdr.GetPageSize() ),
4
Passing value via 3rd parameter 'nPSize'
5
Calling constructor for 'EasyFat'
9
Returning from constructor for 'EasyFat'
267 rIo( rIoP )
268{
269 FatError nErr = nError = FatError::Ok;
270
271 if( ( nErr = ValidateMasterFATs() ) != FatError::Ok )
10
Calling 'Validator::ValidateMasterFATs'
23
Returning from 'Validator::ValidateMasterFATs'
24
Taking false branch
272 nError = nErr;
273 else if( ( nErr = ValidateDirectoryEntries() ) != FatError::Ok )
25
Calling 'Validator::ValidateDirectoryEntries'
274 nError = nErr;
275 else if( ( nErr = FindUnrefedChains()) != FatError::Ok )
276 nError = nErr;
277}
278
279FatError Validator::ValidateMasterFATs()
280{
281 sal_Int32 nCount = rIo.m_aHdr.GetFATSize();
282 FatError nErr;
283 if ( !rIo.m_pFAT
10.1
Field 'm_pFAT' is non-null
)
11
Taking false branch
284 return FatError::InMemoryError;
285
286 for( sal_Int32 i = 0; i < nCount; i++ )
12
Assuming 'i' is >= 'nCount'
13
Loop condition is false. Execution continues on line 291
287 {
288 if( ( nErr = aFat.Mark(rIo.m_pFAT->GetPage(i, false), aFat.GetPageSize(), -3 )) != FatError::Ok)
289 return nErr;
290 }
291 if( rIo.m_aHdr.GetMasters() )
14
Assuming the condition is true
15
Taking true branch
292 if( ( nErr = aFat.Mark(rIo.m_aHdr.GetFATChain( ), aFat.GetPageSize(), -4 )) != FatError::Ok )
16
Calling 'EasyFat::Mark'
21
Returning from 'EasyFat::Mark'
22
Taking false branch
293 return nErr;
294
295 return FatError::Ok;
296}
297
298FatError Validator::MarkAll( StgDirEntry *pEntry )
299{
300 if ( !pEntry )
301 return FatError::InMemoryError;
302
303 StgIterator aIter( *pEntry );
304 FatError nErr = FatError::Ok;
305 for( StgDirEntry* p = aIter.First(); p ; p = aIter.Next() )
306 {
307 if( p->m_aEntry.GetType() == STG_STORAGE )
308 {
309 nErr = MarkAll( p );
310 if( nErr != FatError::Ok )
311 return nErr;
312 }
313 else
314 {
315 sal_Int32 nSize = p->m_aEntry.GetSize();
316 if( nSize < rIo.m_aHdr.GetThreshold() )
317 nErr = aSmallFat.Mark( p->m_aEntry.GetStartPage(),nSize, -2 );
318 else
319 nErr = aFat.Mark( p->m_aEntry.GetStartPage(),nSize, -2 );
320 if( nErr != FatError::Ok )
321 return nErr;
322 }
323 }
324 return FatError::Ok;
325}
326
327FatError Validator::ValidateDirectoryEntries()
328{
329 if ( !rIo.m_pTOC )
26
Assuming field 'm_pTOC' is non-null
27
Taking false branch
330 return FatError::InMemoryError;
331
332 // Normal DirEntries
333 FatError nErr = MarkAll( rIo.m_pTOC->GetRoot() );
334 if( nErr
27.1
'nErr' is equal to Ok
!= FatError::Ok )
28
Taking false branch
335 return nErr;
336 // Small Data
337 nErr = aFat.Mark( rIo.m_pTOC->GetRoot()->m_aEntry.GetStartPage(),
29
Calling 'EasyFat::Mark'
338 rIo.m_pTOC->GetRoot()->m_aEntry.GetSize(), -2 );
339 if( nErr != FatError::Ok )
340 return nErr;
341 // Small Data FAT
342 nErr = aFat.Mark(
343 rIo.m_aHdr.GetDataFATStart(),
344 rIo.m_aHdr.GetDataFATSize() * aFat.GetPageSize(), -2 );
345 if( nErr != FatError::Ok )
346 return nErr;
347 // TOC
348 nErr = aFat.Mark(
349 rIo.m_aHdr.GetTOCStart(), -1, -2 );
350 return nErr;
351}
352
353FatError Validator::FindUnrefedChains() const
354{
355 if( aSmallFat.HasUnrefChains() ||
356 aFat.HasUnrefChains() )
357 return FatError::UnrefChain;
358 else
359 return FatError::Ok;
360}
361
362FatError StgIo::ValidateFATs()
363{
364 if( m_bFile )
1
Assuming field 'm_bFile' is true
2
Taking true branch
365 {
366 std::unique_ptr<Validator> pV(new Validator( *this ));
3
Calling constructor for 'Validator'
367 bool bRet1 = !pV->IsError(), bRet2 = true ;
368 pV.reset();
369
370 SvFileStream *pFileStrm = static_cast<SvFileStream *>( GetStrm() );
371 if ( !pFileStrm )
372 return FatError::InMemoryError;
373
374 StgIo aIo;
375 if( aIo.Open( pFileStrm->GetFileName(),
376 StreamMode::READ | StreamMode::SHARE_DENYNONE) &&
377 aIo.Load() )
378 {
379 pV.reset(new Validator( aIo ));
380 bRet2 = !pV->IsError();
381 pV.reset();
382 }
383
384 FatError nErr;
385 if( bRet1 != bRet2 )
386 nErr = bRet1 ? FatError::OnFileError : FatError::InMemoryError;
387 else nErr = bRet1 ? FatError::Ok : FatError::BothError;
388 if( nErr != FatError::Ok && !m_bCopied )
389 {
390 m_bCopied = true;
391 }
392// DBG_ASSERT( nErr == FatError::Ok ,"Storage broken");
393 return nErr;
394 }
395// OSL_FAIL("Do not validate (no FileStorage)");
396 return FatError::Ok;
397}
398
399/* vim:set shiftwidth=4 softtabstop=4 expandtab: */