cmemory-managementfile-iosegmentation-faultfread

fread() is reading the wrong data even though previous chunk were read correctly


Background:
I'm trying to parse layer info from a .goo file (SLA printer file format for Elegoo Mars 4).

Each layer of the 3d model is encoded in a new chunk of binaries stored in goo_layer_definition[nlayers].

Problem:
My parsing function worked for the first chunk. All the fields of goo_layer_definition[0] were read and printed out perfectly. However, I got a segfault when trying to read the 2nd chunk. i.e. I got an error when reading into goo_layer_definition[1].

For the 2nd chunk, the pause_flag and pause_position_z values were read correctly. However, the value read into layer_position_z was completely wrong and cannot be found anywhere in the file's binary.

This wrong reading occurred on the fread() function where wrong data are being read into the buffer.

Summary
I'm stuck on how the first chunk is able to read all the fields correctly, and part of the 2nd chunk is read correctly, but the same code just decides to read the wrong data at same point in the 2nd chunk.

The problem occur in fread_goo_layer_content() when the loop is at i=1. The problem appears on the line if(!fread_le(&dst->layer_position_z, sizeof(float), 1, fp) ) return 0;

Does anyone have a suggestion on what the problem could be?

// main.c
...
FILE *p_fin = fopen(argv[1], "rb");
if(p_fin == NULL){
  (void)PRINT_ERR("Failed to open %s\n\n", argv[1]);
  return ENOENT;
}
...
goo_layer_content_t *goo_layer_contents;
if(!fread_goo_layer_content(&goo_layer_contents, &goo_header_info, p_fin)){
  (void)PRINT_ERR("Failed to read content info for %s\n\n", argv[1]);
  return EIO;
}
...

Note: fread_le() is just a wrapper for fread() but converted to little endian.

// goo.c
int fread_goo_layer_definition(goo_layer_definition_t *dst, FILE *fp){
  if(!fread_le(&dst->pause_flag               , sizeof(int16_t ), 1, fp) ) return 0;
  if(!fread_le(&dst->pause_position_z         , sizeof(float   ), 1, fp) ) return 0; 
  if(!fread_le(&dst->layer_position_z         , sizeof(float   ), 1, fp) ) return 0; 
  if(!fread_le(&dst->layer_exposure_time      , sizeof(float   ), 1, fp) ) return 0; 
  if(!fread_le(&dst->layer_off_time           , sizeof(float   ), 1, fp) ) return 0;
  ...
  return 1;
}

int  fread_goo_layer_content(goo_layer_content_t **dst, goo_header_info_t *header, FILE *fp){
  // Jump to layer content segment
  if(fseek(fp, header->offset_of_layer_content, SEEK_SET)) return 0;

  *dst = (goo_layer_content_t*)calloc(header->total_layers, sizeof(goo_layer_content_t));
  if(*dst == NULL) return 0;

  // Loop through all layers and read their layer info
  for(int i=0; i < header->total_layers; i++){
    // Reading layer definition
    if(!fread_goo_layer_definition(&dst[i]->definition, fp) ) return 0;

    // Reading image data
    ...
  }
  return 1;
}
//goo.h
#pragma packed
typedef struct goo_layer_definition_t {
  int16_t  pause_flag               ;  // 
  float    pause_position_z         ;  // 
  float    layer_position_z         ;  // 
  float    layer_exposure_time      ;  // 
  float    layer_off_time           ;  // 
  float    before_lift_time         ;  // 
  float    after_lift_time          ;  // 
  float    after_retract_time       ;  // 
  float    lift_distance            ;  // 
  float    lift_speed               ;  // 
  float    second_lift_distance     ;  // 
  float    second_lift_speed        ;  // 
  float    retract_distance         ;  // 
  float    retract_speed            ;  // 
  float    second_retract_distance  ;  // 
  float    second_retract_speed     ;  // 
  int16_t  light_pwm                ;  // 
  uint16_t delimiter                ;  // 
} goo_layer_definition_t;

#pragma packed
typedef struct goo_layer_content_t {
  goo_layer_definition_t definition ;  // Layer definitions
  int32_t   data_size               ;  // Indicate size of image_data
  uint8_t   data_start              ;  // Fix value: 0x55 
  image_data_chunk_t *image_data    ;  // See spec PDF
  uint16_t  delimiter               ;  // Fix value: 0x0D_0A
} goo_layer_content_t;  

Debugging Attempts:
I've look at GDB and the hex editor and stepped through each fread(). The value stored in the buffer is all correct up to the point on the 2nd chunk where it reads fread_le(&dst->layer_position_z...) .

I've also tried printing out the fgetpos() of each read and the positions were incrementing in the correct amount per read, i.e. 2 bytes for u16 and 4 bytes for float.

I've also compared the fgetpos() position value with the address offset from the hex editor and its the data lined up until the point where it reads layer_position_x on the 2nd chunk.
i.e.
What I see in hex editor:
correct datas ... 0A 8C 8D 1E 0D 0A 00 00 3C A3 D7 0A *3C A3 D7 0A*

What I get from fread():
correct datas ... 0A 8C 8D 1E 0D 0A 00 00 3C A3 D7 0A *40 A3 D7 0D*

The previous 204168 bytes were all correct. The error occurs on address 204172--I'm not sure if this is some limit to fread() or not. The error is consistent and read the same wrong value at the same point every run.

I've also checked to make sure that the calloc() value was correct in GDB. It was 1 for my 1-layer test file and 2 for my 2-layer test file.

Expected:
I expected fread() to read 3C A3 D7 0A into the buffer, not 40 A3 D7 0D.


Solution

  • &dst[i]->definition in the fread_goo_layer_definition should be &(*dst)[i].definition.

    First (*dst) like all other uses in the function, then the index.