Skip to content

Crash on reading NC_VLEN variable with unlimited dimension #2181

@krisfed

Description

@krisfed

(This seems to be again running into issues with reclaiming NC_VLENs and might be possibly addressed by the proposed fix for #2143 )

We are using netcdf-c 4.8.1, and ran into this interesting crash. It seems to happen when

  • there are 2 NC_VLEN variables (the base type doesn't seem to matter), both with the same unlimited dimension
  • at least one of them has to have offset in the unlimited dimension
  • crash happens on read of one of the variables (depending on their offsets in unlimited dimension... seems like reading the one with the smaller offset crashes)
  • offsets in the unlimited dimensions for two variables must be different

Here is some simplistic reproduction code that both creates the file and then tries to read the variable:

#include <iostream>
#include "netcdf.h"


void checkErrorCode(int status, const char* message){
    if (status != NC_NOERR){
        std::cout << "Error code: " << status << " from " << message << std::endl;
        std::cout << nc_strerror(status) << std::endl << std::endl;
    }
}

int main(int argc, const char * argv[]) {
    
    // ================ WRITE ==================
    
    // Setup data
    size_t DATA_LENGTHS[2] = {2, 3};
    nc_vlen_t data[DATA_LENGTHS[0] * DATA_LENGTHS[1]];
    
    const int first_size = 6;
    double first[first_size] = {65, 66, 67, 68, 69, 70};
    data[0].p = first;
    data[0].len = first_size;
    
    const int second_size = 6;
    double second[second_size] = {65, 66, 67, 68, 69, 70};
    data[1].p = second;
    data[1].len = second_size;
    
    const int third_size = 5;
    double third[third_size] = {65, 66, 67, 68, 69};
    data[2].p = third;
    data[2].len = third_size;
    
    const int fourth_size = 5;
    double fourth[fourth_size] = {65, 66, 67, 68, 69};
    data[3].p = fourth;
    data[3].len = fourth_size;
    
    const int fifth_size = 8;
    double fifth[fifth_size] = {65, 66, 67, 68, 69, 70, 71, 72};
    data[4].p = fifth;
    data[4].len = fifth_size;
    
    const int sixth_size = 4;
    double sixth[sixth_size] = {65, 66, 67, 68};
    data[5].p = sixth;
    data[5].len = sixth_size;

    // Open file
    int ncid;
    int retval;
    
    retval = nc_create("myfile.nc", NC_NETCDF4, &ncid);
    checkErrorCode(retval, "nc_create");
    
    // Define vlen type named RAGGED_DOUBLE
    nc_type vlen_typeID;
    retval = nc_def_vlen(ncid, "RAGGED_DOUBLE", NC_DOUBLE, &vlen_typeID);
    checkErrorCode(retval, "nc_def_vlen");
    
    // Define dimensions
    int dimid_x;
    retval = nc_def_dim(ncid, "xdim", NC_UNLIMITED, &dimid_x);
    checkErrorCode(retval, "nc_def_dim (1)");
    
    int dimid_y;
    retval = nc_def_dim(ncid, "ydim", 10, &dimid_y);
    checkErrorCode(retval, "nc_def_dim (2)");
    
    int dims[2] = {dimid_y, dimid_x};
    
    // Define vlen variable 1
    int varid1;
    retval = nc_def_var(ncid, "Var1", vlen_typeID, 2, dims, &varid1);
    checkErrorCode(retval, "nc_def_var (1)");
    
    // Write vlen variable 1
    size_t start1[2] = {0, 1};
    ptrdiff_t stride[2] = {1,1};
    retval = nc_put_vars(ncid, varid1, start1, DATA_LENGTHS, stride, data);
    checkErrorCode(retval, "nc_put_vars (1)");
    
    // Define vlen variable 2
    int varid2;
    retval = nc_def_var(ncid, "Var2", vlen_typeID, 2, dims, &varid2);
    checkErrorCode(retval, "nc_def_var (2)");
    
    // Write vlen variable 2
    size_t start2[2] = {0, 2};
    retval = nc_put_vars(ncid, varid2, start2, DATA_LENGTHS, stride, data);
    checkErrorCode(retval, "nc_put_vars (2)");
    
    retval = nc_close(ncid);
    checkErrorCode(retval, "nc_close (1)");
    
    
    // ================ READ ==================
    
    // open file
    retval = nc_open("myfile.nc", NC_NOWRITE, &ncid);
    checkErrorCode(retval, "nc_open");
    
    // read vlen variable with the smaller of the offsets
    
    // the length of fixed dimension is 10, and
    // the length of unlimited dimension is 5
    // (we wrote 3 datapoints with offset of 2 for 2nd var)
    const int num_items = 50;
    
    nc_vlen_t* data_read = new nc_vlen_t[num_items];
    retval = nc_get_var(ncid, varid1, data_read);
    checkErrorCode(retval, "nc_get_var");
    
    retval = nc_free_vlens(num_items, data_read);
    checkErrorCode(retval, "nc_free_vlens");
    
    retval = nc_close(ncid);
    checkErrorCode(retval, "nc_close (2)");
    
    return retval;
}

To reproduce the crash outside of our code base I had to use some environment variables that mess with memory allocation and release. I.e. on Debian 10:

% setenv MALLOC_CHECK_ 3
% setenv MALLOC_PERTURB_ 204
% ./a.out
free(): invalid pointer
Abort

And on macOS 11.2.3:

$ export MallocScribble=1
$ ./a.out 
a.out(23741,0x10b7fae00) malloc: enabling scribbling to detect mods to free blocks
a.out(23741,0x10b7fae00) malloc: *** error for object 0xaaaaaaaaaaaaaaaa: pointer being freed was not allocated
a.out(23741,0x10b7fae00) malloc: *** set a breakpoint in malloc_error_break to debug
Abort trap: 6

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions