-
-
Notifications
You must be signed in to change notification settings - Fork 56.5k
imwrite() with params which has odd elements may make out-of-boundary memory access . #22752
Description
System Information
OpenCV version: 4.x
Operating System / Platform: Ubuntu 22.10
Compiler & compiler version: GCC 12.2.0
Detailed description
This is a low priority issue due to a user implementation error.
However, as compilers and libraries improve, this problem will occur some compile/runtime errors.
What is problem.
The params for imwrite() is expected to store even number integer array .
https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html#gabbc7ef1aa2edfaa87772f1202d67e0ce
params Format-specific parameters encoded as pairs (paramId_1, paramValue_1, paramId_2, paramValue_2, ... .) see cv::ImwriteFlags
params = [ IMWRITE_JPEG_QUALITY, 100, IMWRITE_JPEG_xxxx, yyy ] ;
(All or almost) decoder believe this condition must be kept.
opencv/modules/imgcodecs/src/grfmt_jpeg.cpp
Lines 643 to 654 in 21133a2
| for( size_t i = 0; i < params.size(); i += 2 ) | |
| { | |
| if( params[i] == IMWRITE_JPEG_QUALITY ) | |
| { | |
| quality = params[i+1]; | |
| quality = MIN(MAX(quality, 0), 100); | |
| } | |
| if( params[i] == IMWRITE_JPEG_PROGRESSIVE ) | |
| { | |
| progressive = params[i+1]; | |
| } |
However, if user-application breaks this condition, those codecs makes out-of-boundary memory access.
For example, params has only 1 ID which is interesting by codec..
params = [ IMWRITE_JPEG_QUALITY };
step 1. i = 0
step 2. if ( i < params.size() ) -> true.
step 3. if( params[i] == IMWRITE_JPEG_QUALITY ) -> true
step 4. quality = params[i+1]; // out-of-boundary
error log
[ RUN ] Imgcodecs.write_parameter_length
==103191== Invalid read of size 4
==103191== at 0x48A6D80: cv::JpegEncoder::write(cv::Mat const&, std::vector<int, std::allocator<int> > const&) (grfmt_jpeg.cpp:647)
==103191== by 0x487ED7A: cv::imwrite_(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<cv::Mat, std::allocator<cv::Mat> > const&, std::vector<int, std::allocator<int> > const&, bool) (loadsave.cpp:xxx)
==103191== by 0x487F48F: cv::imwrite(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, cv::_InputArray const&, std::vector<int, std::allocator<int> > const&) (loadsave.cpp:xxx)
(sorry, loadsave.cpp is modified to investigate. so line number is invalid.)
How to fix
I think it is better to check in imwrite_() and imencode() at loadsave.cpp. And imencode() shoule be add CV_Assert(params.size() <= CV_IO_MAX_IMAGE_PARAMS*2);,
opencv/modules/imgcodecs/src/loadsave.cpp
Lines 716 to 721 in 21133a2
| } | |
| encoder->setDestination( filename ); | |
| CV_Assert(params.size() <= CV_IO_MAX_IMAGE_PARAMS*2); | |
| bool code = false; | |
| try |
opencv/modules/imgcodecs/src/loadsave.cpp
Lines 1083 to 1109 in 21133a2
| bool imencode( const String& ext, InputArray _image, | |
| std::vector<uchar>& buf, const std::vector<int>& params ) | |
| { | |
| CV_TRACE_FUNCTION(); | |
| Mat image = _image.getMat(); | |
| CV_Assert(!image.empty()); | |
| int channels = image.channels(); | |
| CV_Assert( channels == 1 || channels == 3 || channels == 4 ); | |
| ImageEncoder encoder = findEncoder( ext ); | |
| if( !encoder ) | |
| CV_Error( Error::StsError, "could not find encoder for the specified extension" ); | |
| if( !encoder->isFormatSupported(image.depth()) ) | |
| { | |
| CV_Assert( encoder->isFormatSupported(CV_8U) ); | |
| Mat temp; | |
| image.convertTo(temp, CV_8U); | |
| image = temp; | |
| } | |
| bool code; | |
| if( encoder->setDestination(buf) ) | |
| { | |
| code = encoder->write(image, params); |
I feels that it is not bad idea to add CV_Assert( (params.count() % 2) == 0 ) .
Steps to reproduce
sample code
/**
* $ g++ main.cpp -o a,out -I /usr/local/include/opencv4 -lopencv_core -lopencv_imgcodecs
* $ valgrind ./a.out
*/
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <string>
#include <vector>
using namespace std;
using namespace cv;
int main(void)
{
const Mat img( 16, 16, CV_8UC3, cv::Scalar::all(0) );
vector<int> params;
params.push_back(IMWRITE_JPEG_QUALITY);
// params.push_back(100)); // Forget it.
cv::imwrite("test.jpg", img, params);
}Issue submission checklist
- I report the issue, it's not a question
- I checked the problem with documentation, FAQ, open issues, forum.opencv.org, Stack Overflow, etc and have not found any solution
- I updated to the latest OpenCV version and the issue is still there
- There is reproducer code and related data files (videos, images, onnx, etc)