From d3f833f125ad186374a49e97847d0150fb63a93a Mon Sep 17 00:00:00 2001 From: "Nathaniel J. McClatchey, PhD" Date: Sat, 16 Nov 2019 23:02:52 -0800 Subject: [PATCH 1/3] Close FILE pointer on exception in npz_load Fixes issue #53, which notes that an exception thrown when loading an npz file would cause an open FILE pointer to be leaked. This bug could be triggered intentionally (via specially-crafted NPZ files), so fixing it is a priority. --- cnpy.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/cnpy.cpp b/cnpy.cpp index 2d28578..eae28ca 100644 --- a/cnpy.cpp +++ b/cnpy.cpp @@ -277,13 +277,21 @@ cnpy::npz_t cnpy::npz_load(std::string fname) { } cnpy::NpyArray cnpy::npz_load(std::string fname, std::string varname) { - FILE* fp = fopen(fname.c_str(),"rb"); + struct AutoCloser + { + FILE * fp; + ~AutoCloser (void) + { + fclose(fp); + } + } closer; + closer.fp = fopen(fname.c_str(), "rb"); - if(!fp) throw std::runtime_error("npz_load: Unable to open file "+fname); + if(!closer.fp) throw std::runtime_error("npz_load: Unable to open file "+fname); while(1) { std::vector local_header(30); - size_t header_res = fread(&local_header[0],sizeof(char),30,fp); + size_t header_res = fread(&local_header[0],sizeof(char),30,closer.fp); if(header_res != 30) throw std::runtime_error("npz_load: failed fread"); @@ -293,33 +301,30 @@ cnpy::NpyArray cnpy::npz_load(std::string fname, std::string varname) { //read in the variable name uint16_t name_len = *(uint16_t*) &local_header[26]; std::string vname(name_len,' '); - size_t vname_res = fread(&vname[0],sizeof(char),name_len,fp); + size_t vname_res = fread(&vname[0],sizeof(char),name_len,closer.fp); if(vname_res != name_len) throw std::runtime_error("npz_load: failed fread"); vname.erase(vname.end()-4,vname.end()); //erase the lagging .npy //read in the extra field uint16_t extra_field_len = *(uint16_t*) &local_header[28]; - fseek(fp,extra_field_len,SEEK_CUR); //skip past the extra field + fseek(closer.fp,extra_field_len,SEEK_CUR); //skip past the extra field uint16_t compr_method = *reinterpret_cast(&local_header[0]+8); uint32_t compr_bytes = *reinterpret_cast(&local_header[0]+18); uint32_t uncompr_bytes = *reinterpret_cast(&local_header[0]+22); if(vname == varname) { - NpyArray array = (compr_method == 0) ? load_the_npy_file(fp) : load_the_npz_array(fp,compr_bytes,uncompr_bytes); - fclose(fp); + NpyArray array = (compr_method == 0) ? load_the_npy_file(closer.fp) : load_the_npz_array(closer.fp,compr_bytes,uncompr_bytes); return array; } else { //skip past the data uint32_t size = *(uint32_t*) &local_header[22]; - fseek(fp,size,SEEK_CUR); + fseek(closer.fp,size,SEEK_CUR); } } - fclose(fp); - //if we get here, we haven't found the variable in the file throw std::runtime_error("npz_load: Variable name "+varname+" not found in "+fname); } From d7fabcc17a19895bc698d47a21c48b2ef43007a5 Mon Sep 17 00:00:00 2001 From: "Nathaniel J. McClatchey, PhD" Date: Sat, 16 Nov 2019 23:05:35 -0800 Subject: [PATCH 2/3] Close FILE pointer on exception in npy_load Fixes issue #52, which notes that an exception thrown when loading an npy file would cause an open FILE pointer to be leaked. This bug could be triggered intentionally (via specially-crafted NPY files), so fixing it is a priority. --- cnpy.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/cnpy.cpp b/cnpy.cpp index eae28ca..61c29ee 100644 --- a/cnpy.cpp +++ b/cnpy.cpp @@ -331,13 +331,20 @@ cnpy::NpyArray cnpy::npz_load(std::string fname, std::string varname) { cnpy::NpyArray cnpy::npy_load(std::string fname) { - FILE* fp = fopen(fname.c_str(), "rb"); + struct AutoCloser + { + FILE * fp; + ~AutoCloser (void) + { + fclose(fp); + } + } closer; + closer.fp = fopen(fname.c_str(), "rb"); - if(!fp) throw std::runtime_error("npy_load: Unable to open file "+fname); + if(!closer.fp) throw std::runtime_error("npy_load: Unable to open file "+fname); - NpyArray arr = load_the_npy_file(fp); + NpyArray arr = load_the_npy_file(closer.fp); - fclose(fp); return arr; } From 6f272e55067433aa830601ff9608efb4cff77fe3 Mon Sep 17 00:00:00 2001 From: "Nathaniel J. McClatchey, PhD" Date: Sun, 8 Mar 2026 20:05:15 -0500 Subject: [PATCH 3/3] Apply feedback Addresses a note about potential undefined behavior on `nullptr` (and a false-positive compiler complaint about uninitialized values). --- cnpy.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cnpy.cpp b/cnpy.cpp index 61c29ee..9074e0e 100644 --- a/cnpy.cpp +++ b/cnpy.cpp @@ -280,12 +280,13 @@ cnpy::NpyArray cnpy::npz_load(std::string fname, std::string varname) { struct AutoCloser { FILE * fp; + AutoCloser (FILE * ptr) : fp(ptr) {} ~AutoCloser (void) { - fclose(fp); + if (fp != nullptr) + fclose(fp); } - } closer; - closer.fp = fopen(fname.c_str(), "rb"); + } closer(fopen(fname.c_str(), "rb")); if(!closer.fp) throw std::runtime_error("npz_load: Unable to open file "+fname); @@ -334,12 +335,13 @@ cnpy::NpyArray cnpy::npy_load(std::string fname) { struct AutoCloser { FILE * fp; + AutoCloser (FILE * ptr) : fp(ptr) {} ~AutoCloser (void) { - fclose(fp); + if (fp != nullptr) + fclose(fp); } - } closer; - closer.fp = fopen(fname.c_str(), "rb"); + } closer(fopen(fname.c_str(), "rb")); if(!closer.fp) throw std::runtime_error("npy_load: Unable to open file "+fname);