CVE-2021-39432 (C++, published 2022-11-04T17:15Z)
Description: value: diplib v3.0.0 is vulnerable to Double Free.
Severity: nan (CVSS v3 base score: 6.5)
Weakness: CWE-415 \u2014 Double Free
The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations.
Repository: https://github.com/DIPlib/diplib
Files changed:
CMakeLists.txt
changelogs/diplib_3.1.0.md
src/file_io/jpeg.cpp
Commit message: Fixed error handling in `dip::ImageReadJPEG()`, `dip::ImageWriteJPEG()`, etc. Fixes #80.


Vulnerable code:
#include <setjmp.h>

namespace dip {

namespace {
   struct jpeg_error_mgr pub;   // "public" fields
   jmp_buf setjmp_buffer;      // for return to caller
void my_error_exit( j_common_ptr cinfo ) {
   my_error_ptr myerr = reinterpret_cast<my_error_ptr>(cinfo->err);
void my_output_message( j_common_ptr ) {} // Don't do anything with messages!
      JpegInput( String filename ) : filename_( std::move( filename )) {
         if( setjmp( jerr_.setjmp_buffer )) {
            // If we get here, the JPEG code has signaled an error.
            DIP_THROW_RUNTIME( "Error reading JPEG file." );
         }
      explicit JpegOutput( String const& filename ) {
         if( setjmp( jerr_.setjmp_buffer )) {
            // If we get here, the JPEG code has signaled an error.
            DIP_THROW_RUNTIME( "Error writing JPEG file." );
         }
   JpegInput jpeg( filename );
   JpegInput jpeg( filename );
      JpegInput jpeg( filename );
   jpegLevel = clamp< dip::uint >( jpegLevel, 1, 100 );
   JpegOutput jpeg( filename );
   jpeg_set_quality( jpeg.cinfoptr(), static_cast< int >( jpegLevel ), FALSE );

Fixed code:
   set(CMAKE_C_FLAGS_SANITIZE "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address")
   set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og") # Does some optimization that doesn't impact debugging.
   set(CMAKE_C_FLAGS_SANITIZE "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address")
- Fixed error handling in `dip::ImageReadJPEG()` and `dip::ImageWriteJPEG()`, which previously would crash when libjpeg produced an error (#80).

#include <csetjmp>
extern "C" {
   static void my_error_exit( j_common_ptr cinfo );
   static void my_output_message( j_common_ptr );
}

   struct jpeg_error_mgr pub; // "public" fields
   std::jmp_buf setjmp_buffer;     // for return to caller
static void my_error_exit( j_common_ptr cinfo ) {
   my_error_ptr myerr = reinterpret_cast<my_error_ptr>( cinfo->err );
static void my_output_message( j_common_ptr ) {} // Don't do anything with messages!

#define DIP__DECLARE_JPEG_EXIT( message ) \
std::jmp_buf setjmp_buffer; if( setjmp( setjmp_buffer )) { DIP_THROW_RUNTIME( message ); }


namespace dip {

namespace {
      JpegInput( String filename, std::jmp_buf const& setjmp_buffer ) : filename_( std::move( filename )) {
         std::memcpy( jerr_.setjmp_buffer, setjmp_buffer, sizeof( setjmp_buffer ));
      explicit JpegOutput( String const& filename, std::jmp_buf const& setjmp_buffer ) {
         std::memcpy( jerr_.setjmp_buffer, setjmp_buffer, sizeof( setjmp_buffer ));
   DIP__DECLARE_JPEG_EXIT( "Error reading JPEG file" );
   JpegInput jpeg( filename, setjmp_buffer );
   DIP__DECLARE_JPEG_EXIT( "Error reading JPEG file" );
   JpegInput jpeg( filename, setjmp_buffer );
      DIP__DECLARE_JPEG_EXIT( "Error reading JPEG file" );
      JpegInput jpeg( filename, setjmp_buffer );
   DIP__DECLARE_JPEG_EXIT( "Error writing JPEG file" );
   JpegOutput jpeg( filename, setjmp_buffer );
   jpeg_set_quality( jpeg.cinfoptr(), static_cast< int >( clamp< dip::uint >( jpegLevel, 1, 100 )), FALSE );