• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lutraconsulting / MDAL / 14922942761

09 May 2025 06:33AM UTC coverage: 89.587%. Remained the same
14922942761

push

github

web-flow
Fix out of bounds access when reading dat reference time (#500)

* Fix out of bounds access when reading dat reference time

We can't store an 8 byte result into a 4 byte float value

* Tighten scope of variable, rename to avoid clash with std::time

3 of 5 new or added lines in 1 file covered. (60.0%)

2 existing lines in 1 file now uncovered.

9300 of 10381 relevant lines covered (89.59%)

74896.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.98
/mdal/frmts/mdal_binary_dat.cpp
1
/*
2
 MDAL - Mesh Data Abstraction Library (MIT License)
3
 Copyright (C) 2018 Lutra Consulting Ltd.
4
*/
5

6
#include <stddef.h>
7
#include <iosfwd>
8
#include <iostream>
9
#include <fstream>
10
#include <sstream>
11
#include <string>
12
#include <vector>
13
#include <map>
14
#include <cassert>
15
#include <memory>
16

17
#include "mdal_binary_dat.hpp"
18
#include "mdal.h"
19
#include "mdal_utils.hpp"
20
#include "mdal_logger.hpp"
21

22
#include <math.h>
23

24
static const int CT_VERSION   = 3000;
25
static const int CT_OBJTYPE   = 100;
26
static const int CT_SFLT      = 110;
27
static const int CT_SFLG      = 120;
28
static const int CT_BEGSCL    = 130;
29
static const int CT_BEGVEC    = 140;
30
static const int CT_VECTYPE   = 150;
31
static const int CT_OBJID     = 160;
32
static const int CT_NUMDATA   = 170;
33
static const int CT_NUMCELLS  = 180;
34
static const int CT_NAME      = 190;
35
static const int CT_TS        = 200;
36
static const int CT_ENDDS     = 210;
37
static const int CT_RT_JULIAN = 240;
38
static const int CT_TIMEUNITS = 250;
39
static const int CT_2D_MESHES = 3;
40
static const int CT_FLOAT_SIZE = 4;
41
static const int CF_FLAG_SIZE = 1;
42
static const int CF_FLAG_INT_SIZE = 4;
43

44

45
static void exit_with_error( MDAL_Status error, const std::string msg )
×
46
{
47
  MDAL::Log::error( error, "BINARY_DAT", msg );
×
48
}
×
49

50
static bool read( std::ifstream &in, char *s, int n )
856,213✔
51
{
52
  in.read( s, n );
856,213✔
53
  if ( !in )
856,213✔
54
    return true; //error
1✔
55
  else
56
    return false; //OK
856,212✔
57
}
58

59
static bool readIStat( std::ifstream &in, int sflg, char *flag )
232,687✔
60
{
61
  if ( sflg == CF_FLAG_SIZE )
232,687✔
62
  {
63
    in.read( flag, sflg );
232,639✔
64
    if ( !in )
232,639✔
65
      return true; // error
×
66
  }
67
  else
68
  {
69
    int istat;
70
    in.read( reinterpret_cast< char * >( &istat ), sflg );
48✔
71
    if ( !in )
48✔
72
      return true; // error
×
73
    else
74
      *flag = ( istat == 1 );
48✔
75
  }
76
  return false;
232,687✔
77
}
78

79
MDAL::DriverBinaryDat::DriverBinaryDat():
34✔
80
  Driver( "BINARY_DAT",
81
          "Binary DAT",
82
          "*.dat",
83
          Capability::ReadDatasets | Capability::WriteDatasetsOnVertices
84
        )
34✔
85
{
86
}
34✔
87

88
MDAL::DriverBinaryDat *MDAL::DriverBinaryDat::create()
6✔
89
{
90
  return new DriverBinaryDat();
6✔
91
}
92

93
MDAL::DriverBinaryDat::~DriverBinaryDat() = default;
40✔
94

95
bool MDAL::DriverBinaryDat::canReadDatasets( const std::string &uri )
24✔
96
{
97
  std::ifstream in = MDAL::openInputFile( uri, std::ifstream::in | std::ifstream::binary );
24✔
98
  int version;
99

100
  if ( read( in, reinterpret_cast< char * >( &version ), 4 ) )
24✔
101
    return false;
×
102

103
  if ( version != CT_VERSION ) // Version should be 3000
24✔
104
    return false;
18✔
105

106
  return true;
6✔
107
}
24✔
108

109
/**
110
 * The DAT format contains "datasets" and each dataset has N-outputs. One output
111
 * represents data for all vertices/faces for one timestep
112
 *
113
 * in TUFLOW results there could be also a special timestep (99999) with maximums
114
 * we will put it into a separate dataset with name suffixed with "/Maximums"
115
 *
116
 * In MDAL we convert one output to one MDAL dataset;
117
 *
118
 */
119
void MDAL::DriverBinaryDat::load( const std::string &datFile, MDAL::Mesh *mesh )
6✔
120
{
121
  mDatFile = datFile;
6✔
122
  MDAL::Log::resetLastStatus();
6✔
123

124
  if ( !MDAL::fileExists( mDatFile ) )
6✔
125
  {
126
    MDAL::Log::error( MDAL_Status::Err_FileNotFound, name(), "File could not be found " + mDatFile );
×
127
    return;
×
128
  }
129

130
  std::ifstream in = MDAL::openInputFile( mDatFile, std::ifstream::in | std::ifstream::binary );
6✔
131

132
  // implementation based on information from:
133
  // http://www.xmswiki.com/wiki/SMS:Binary_Dataset_Files_*.dat
134
  if ( !in ) return exit_with_error( MDAL_Status::Err_FileNotFound, "Couldn't open the file" );
6✔
135

136
  size_t vertexCount = mesh->verticesCount();
6✔
137
  size_t elemCount = mesh->facesCount();
6✔
138

139
  int card = 0;
6✔
140
  int version;
141
  int objecttype;
142
  int sflt;
143
  int sflg = 0;
6✔
144
  int vectype;
145
  int objid;
146
  int numdata;
147
  int numcells;
148
  char groupName[40];
149
  std::string timeUnitStr;
6✔
150
  char istat;
151

152
  if ( read( in, reinterpret_cast< char * >( &version ), 4 ) ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "Unable to read version" );
6✔
153

154
  if ( version != CT_VERSION ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "Invalid version " );
6✔
155

156
  std::shared_ptr<DatasetGroup> group = std::make_shared< DatasetGroup >(
157
                                          name(),
×
158
                                          mesh,
159
                                          mDatFile
6✔
160
                                        ); // DAT datasets
6✔
161
  group->setDataLocation( MDAL_DataLocation::DataOnVertices );
6✔
162

163
  // in TUFLOW results there could be also a special timestep (99999) with maximums
164
  // we will put it into a separate dataset
165
  std::shared_ptr<DatasetGroup> groupMax = std::make_shared< DatasetGroup >(
166
        name(),
×
167
        mesh,
168
        mDatFile
6✔
169
      );
6✔
170
  groupMax->setDataLocation( MDAL_DataLocation::DataOnVertices );
6✔
171

172
  while ( card != CT_ENDDS )
237✔
173
  {
174
    if ( read( in, reinterpret_cast< char * >( &card ), 4 ) )
232✔
175
    {
176
      // We've reached the end of the file and there was no ends card
177
      break;
1✔
178
    }
179

180
    switch ( card )
231✔
181
    {
182
      case CT_OBJTYPE:
6✔
183
        // Object type
184
        if ( read( in, reinterpret_cast< char * >( &objecttype ), 4 ) || objecttype != CT_2D_MESHES ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "Invalid object type" );
6✔
185
        break;
226✔
186

187
      case CT_SFLT:
6✔
188
        // Float size
189
        if ( read( in, reinterpret_cast< char * >( &sflt ), 4 ) || sflt != CT_FLOAT_SIZE ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read float size" );
6✔
190
        break;
6✔
191

192
      case CT_SFLG:
6✔
193
        // Flag size
194
        if ( read( in, reinterpret_cast< char * >( &sflg ), 4 ) )
6✔
195
          if ( sflg != CF_FLAG_SIZE && sflg != CF_FLAG_INT_SIZE )
×
196
            return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read flag size" );
×
197
        break;
6✔
198

199
      case CT_BEGSCL:
4✔
200
        group->setIsScalar( true );
4✔
201
        groupMax->setIsScalar( true );
4✔
202
        break;
4✔
203

204
      case CT_BEGVEC:
2✔
205
        group->setIsScalar( false );
2✔
206
        groupMax->setIsScalar( false );
2✔
207
        break;
2✔
208

209
      case CT_VECTYPE:
1✔
210
        // Vector type
211
        if ( read( in, reinterpret_cast< char * >( &vectype ), 4 ) || vectype != 0 ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read vector type" );
1✔
212
        break;
1✔
213

214
      case CT_OBJID:
3✔
215
        // Object id
216
        if ( read( in, reinterpret_cast< char * >( &objid ), 4 ) ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read object id" );
3✔
217
        break;
3✔
218

219
      case CT_NUMDATA:
6✔
220
        // Num data
221
        if ( read( in, reinterpret_cast< char * >( &numdata ), 4 ) ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read num data" );
6✔
222
        if ( numdata != static_cast< int >( vertexCount ) ) return exit_with_error( MDAL_Status::Err_IncompatibleMesh, "invalid num data" );
6✔
223
        break;
6✔
224

225
      case CT_NUMCELLS:
6✔
226
        // Num data
227
        if ( read( in, reinterpret_cast< char * >( &numcells ), 4 ) ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read num cells" );
6✔
228
        if ( numcells != static_cast< int >( elemCount ) ) return exit_with_error( MDAL_Status::Err_IncompatibleMesh, "invalid num cells" );
6✔
229
        break;
6✔
230

231
      case CT_NAME:
6✔
232
        // Name
233
        if ( read( in, reinterpret_cast< char * >( &groupName ), 40 ) ) return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read name" );
6✔
234
        if ( groupName[39] != 0 )
6✔
235
          groupName[39] = 0;
4✔
236
        group->setName( trim( std::string( groupName ) ) );
6✔
237
        groupMax->setName( group->name() + "/Maximums" );
6✔
238
        break;
6✔
239

240
      case CT_RT_JULIAN:
×
241
      {
242
        // Reference time
243
        if ( readIStat( in, sflg, &istat ) )
×
244
          return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read reference time" );
×
245

NEW
246
        double referenceTime = 0;
×
NEW
247
        if ( read( in, reinterpret_cast< char * >( &referenceTime ), 8 ) )
×
UNCOV
248
          return exit_with_error( MDAL_Status::Err_UnknownFormat, "unable to read reference time" );
×
249

250
        group->setReferenceTime( DateTime( referenceTime, DateTime::JulianDay ) );
×
251
        break;
×
252
      }
253

254
      case CT_TIMEUNITS:
3✔
255
      {
256
        // Time unit
257
        int timeUnit = 0;
3✔
258
        if ( read( in, reinterpret_cast< char * >( &timeUnit ), 4 ) )
3✔
259
          return exit_with_error( MDAL_Status::Err_UnknownFormat, "Unable to read time units" );
×
260

261
        switch ( timeUnit )
3✔
262
        {
263
          case 0:
2✔
264
            timeUnitStr = "hours";
2✔
265
            break;
2✔
266
          case 1:
×
267
            timeUnitStr = "minutes";
×
268
            break;
×
269
          case 2:
1✔
270
            timeUnitStr = "seconds";
1✔
271
            break;
1✔
272
          case 4:
×
273
            timeUnitStr = "days";
×
274
            break;
×
275
          default:
×
276
            timeUnitStr = "unknown";
×
277
            break;
×
278
        }
279
        group->setMetadata( "TIMEUNITS", timeUnitStr );
3✔
280
        break;
3✔
281
      }
282

283
      case CT_TS:
177✔
284
        // Time step!
285
        if ( readIStat( in, sflg, &istat ) )
177✔
286
          return exit_with_error( MDAL_Status::Err_UnknownFormat, "Invalid time step" );
×
287

288
        float timeStep;
289
        if ( read( in, reinterpret_cast< char * >( &timeStep ), 4 ) )
177✔
UNCOV
290
          return exit_with_error( MDAL_Status::Err_UnknownFormat, "Invalid time step" );
×
291

292
        double rawTime = static_cast<double>( timeStep );
177✔
293
        MDAL::RelativeTimestamp t( rawTime, MDAL::parseDurationTimeUnit( timeUnitStr ) );
177✔
294

295
        if ( readVertexTimestep( mesh, group, groupMax, t, istat, sflg, in ) )
177✔
296
          return exit_with_error( MDAL_Status::Err_UnknownFormat, "Unable to read vertex timestep" );
×
297

298
        break;
177✔
299
    }
300
  }
301

302
  if ( !group || group->datasets.size() == 0 )
6✔
303
    return exit_with_error( MDAL_Status::Err_UnknownFormat, "No datasets" );
×
304

305
  group->setStatistics( MDAL::calculateStatistics( group ) );
6✔
306
  mesh->datasetGroups.push_back( group );
6✔
307

308
  if ( groupMax && groupMax->datasets.size() > 0 )
6✔
309
  {
310
    groupMax->setStatistics( MDAL::calculateStatistics( groupMax ) );
2✔
311
    mesh->datasetGroups.push_back( groupMax );
2✔
312
  }
313
}
6✔
314

315
bool MDAL::DriverBinaryDat::readVertexTimestep(
177✔
316
  const MDAL::Mesh *mesh,
317
  std::shared_ptr<DatasetGroup> group,
318
  std::shared_ptr<DatasetGroup> groupMax,
319
  MDAL::RelativeTimestamp time,
320
  bool hasStatus,
321
  int sflg,
322
  std::ifstream &in )
323
{
324
  assert( group && groupMax && ( group->isScalar() == groupMax->isScalar() ) );
177✔
325
  bool isScalar = group->isScalar();
177✔
326

327
  size_t vertexCount = mesh->verticesCount();
177✔
328
  size_t faceCount = mesh->facesCount();
177✔
329

330
  std::shared_ptr<MDAL::MemoryDataset2D> dataset = std::make_shared< MDAL::MemoryDataset2D >( group.get(), hasStatus );
177✔
331

332
  bool active = true;
177✔
333
  for ( size_t i = 0; i < faceCount; ++i )
1,191,055✔
334
  {
335
    if ( hasStatus )
1,190,878✔
336
    {
337
      if ( readIStat( in, sflg, reinterpret_cast< char * >( &active ) ) )
232,510✔
338
        return true; //error
×
339
      dataset->setActive( i, active );
232,510✔
340
    }
341
  }
342

343
  for ( size_t i = 0; i < vertexCount; ++i )
733,386✔
344
  {
345
    if ( !isScalar )
733,209✔
346
    {
347
      float x, y;
348

349
      if ( read( in, reinterpret_cast< char * >( &x ), 4 ) )
122,522✔
350
        return true; //error
×
351
      if ( read( in, reinterpret_cast< char * >( &y ), 4 ) )
122,522✔
352
        return true; //error
×
353

354
      dataset->setVectorValue( i, static_cast< double >( x ), static_cast< double >( y ) );
122,522✔
355
    }
356
    else
357
    {
358
      float scalar;
359

360
      if ( read( in, reinterpret_cast< char * >( &scalar ), 4 ) )
610,687✔
361
        return true; //error
×
362

363
      dataset->setScalarValue( i, static_cast< double >( scalar ) );
610,687✔
364
    }
365
  }
366

367
  if ( MDAL::equals( time.value( MDAL::RelativeTimestamp::hours ), 99999.0 ) ) // Special TUFLOW dataset with maximus
177✔
368
  {
369
    dataset->setTime( time );
2✔
370
    dataset->setStatistics( MDAL::calculateStatistics( dataset ) );
2✔
371
    groupMax->datasets.push_back( dataset );
2✔
372
  }
373
  else
374
  {
375
    dataset->setTime( time );
175✔
376
    dataset->setStatistics( MDAL::calculateStatistics( dataset ) );
175✔
377
    group->datasets.push_back( dataset );
175✔
378
  }
379
  return false; //OK
177✔
380
}
177✔
381

382
// ////////////////////////////////////////////
383
// WRITE
384
// ////////////////////////////////////////////
385

386
static bool writeRawData( std::ofstream &out, const char *s, int n )
84✔
387
{
388
  out.write( s, n );
84✔
389
  if ( !out )
84✔
390
    return true; //error
×
391
  else
392
    return false; //OK
84✔
393
}
394

395
bool MDAL::DriverBinaryDat::persist( MDAL::DatasetGroup *group )
2✔
396
{
397
  assert( group->dataLocation() == MDAL_DataLocation::DataOnVertices );
2✔
398

399
  std::ofstream out = MDAL::openOutputFile( group->uri(), std::ofstream::out | std::ofstream::binary );
2✔
400

401
  // implementation based on information from:
402
  // http://www.xmswiki.com/wiki/SMS:Binary_Dataset_Files_*.dat
403
  if ( !out )
2✔
404
    return true; // Couldn't open the file
×
405

406
  const Mesh *mesh = group->mesh();
2✔
407
  size_t nodeCount = mesh->verticesCount();
2✔
408
  size_t elemCount = mesh->facesCount();
2✔
409

410
  // version card
411
  writeRawData( out, reinterpret_cast< const char * >( &CT_VERSION ), 4 );
2✔
412

413
  // objecttype
414
  writeRawData( out, reinterpret_cast< const char * >( &CT_OBJTYPE ), 4 );
2✔
415
  writeRawData( out, reinterpret_cast< const char * >( &CT_2D_MESHES ), 4 );
2✔
416

417
  // float size
418
  writeRawData( out, reinterpret_cast< const char * >( &CT_SFLT ), 4 );
2✔
419
  writeRawData( out, reinterpret_cast< const char * >( &CT_FLOAT_SIZE ), 4 );
2✔
420

421
  // Flag size
422
  writeRawData( out, reinterpret_cast< const char * >( &CT_SFLG ), 4 );
2✔
423
  writeRawData( out, reinterpret_cast< const char * >( &CF_FLAG_SIZE ), 4 );
2✔
424

425
  // Dataset Group Type
426
  if ( group->isScalar() )
2✔
427
  {
428
    writeRawData( out, reinterpret_cast< const char * >( &CT_BEGSCL ), 4 );
1✔
429
  }
430
  else
431
  {
432
    writeRawData( out, reinterpret_cast< const char * >( &CT_BEGVEC ), 4 );
1✔
433
  }
434

435
  // Object id (ignored)
436
  int ignored_val = 1;
2✔
437
  writeRawData( out, reinterpret_cast< const char * >( &CT_OBJID ), 4 );
2✔
438
  writeRawData( out, reinterpret_cast< const char * >( &ignored_val ), 4 );
2✔
439

440
  // Num nodes
441
  writeRawData( out, reinterpret_cast< const char * >( &CT_NUMDATA ), 4 );
2✔
442
  writeRawData( out, reinterpret_cast< const char * >( &nodeCount ), 4 );
2✔
443

444
  // Num cells
445
  writeRawData( out, reinterpret_cast< const char * >( &CT_NUMCELLS ), 4 );
2✔
446
  writeRawData( out, reinterpret_cast< const char * >( &elemCount ), 4 );
2✔
447

448
  // Name
449
  writeRawData( out, reinterpret_cast< const char * >( &CT_NAME ), 4 );
2✔
450
  writeRawData( out, MDAL::leftJustified( group->name(), 39 ).c_str(), 40 );
2✔
451

452
  // Time steps
453
  int istat = 1; // include if elements are active
2✔
454

455
  for ( size_t time_index = 0; time_index < group->datasets.size(); ++ time_index )
6✔
456
  {
457
    const std::shared_ptr<MDAL::MemoryDataset2D> dataset = std::dynamic_pointer_cast<MDAL::MemoryDataset2D>( group->datasets[time_index] );
4✔
458

459
    writeRawData( out, reinterpret_cast< const char * >( &CT_TS ), 4 );
4✔
460
    writeRawData( out, reinterpret_cast< const char * >( &istat ), 1 );
4✔
461
    float ftime = static_cast<float>( dataset->time( RelativeTimestamp::hours ) );
4✔
462
    writeRawData( out, reinterpret_cast< const char * >( &ftime ), 4 );
4✔
463

464
    if ( istat )
4✔
465
    {
466
      // Write status flags
467
      for ( size_t i = 0; i < elemCount; i++ )
12✔
468
      {
469
        bool active = static_cast<bool>( dataset->active( i ) );
8✔
470
        writeRawData( out, reinterpret_cast< const char * >( &active ), 1 );
8✔
471
      }
472
    }
473

474
    for ( size_t i = 0; i < nodeCount; i++ )
24✔
475
    {
476
      // Read values flags
477
      if ( !group->isScalar() )
20✔
478
      {
479
        float x = static_cast<float>( dataset->valueX( i ) );
10✔
480
        float y = static_cast<float>( dataset->valueY( i ) );
10✔
481
        writeRawData( out, reinterpret_cast< const char * >( &x ), 4 );
10✔
482
        writeRawData( out, reinterpret_cast< const char * >( &y ), 4 );
10✔
483
      }
484
      else
485
      {
486
        float val = static_cast<float>( dataset->scalarValue( i ) );
10✔
487
        writeRawData( out, reinterpret_cast< const char * >( &val ), 4 );
10✔
488
      }
489
    }
490
  }
4✔
491

492
  if ( writeRawData( out, reinterpret_cast< const char * >( &CT_ENDDS ), 4 ) ) return true;
2✔
493

494
  return false;
2✔
495
}
2✔
496

497
std::string MDAL::DriverBinaryDat::writeDatasetOnFileSuffix() const
1✔
498
{
499
  return "dat";
1✔
500
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc