Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(wechat_qrcode): Init nBytes after the count value is determined #3480

Merged
merged 6 commits into from
Apr 26, 2023

Conversation

Konano
Copy link
Contributor

@Konano Konano commented Apr 24, 2023

Merge with test case: opencv/opencv_extra#1061

A malicious crafted image will crash the wechat_qrcode module by invalid memory access.

The earliest PoC by @GZTimeWalker: https://gist.github.com/GZTimeWalker/3ca70a8af2f5830711e9cccc73fb5270

Sample image:

image

In DecodedBitStreamParser::decodeByteSegment, an attacker can add a byte segment with the Character Count Indicator of non-zero but empty content at the end of the data segment, where available is 0 and count is updated to 0, but nBytes remains non-zero.

int nBytes = count;
BitSource& bits(*bits_);
// Don't crash trying to read more bits than we have available.
int available = bits.available();
// try to repair count data if count data is invalid
if (count * 8 > available) {
count = (available + 7 / 8);
}

Since count is 0, the readBytes in L203 is a null pointer.

ArrayRef<char> bytes_(count);
char* readBytes = &(*bytes_)[0];

Then DecodedBitStreamParser::decodeByteSegment will call DecodedBitStreamParser::append, where the nBytes parameter is not 0.

Eventually the program will crash at line 108 because it accesses a null pointer.

We think the essence of the issue is that count was modified without updating nBytes, resulting in inconsistent values for the two variables. Unlike #3479, we think that the initialization of nBytes should be done after the value of count has been determined, which helps avoid other potential errors.

Moreover, we also found some wrong calculations in line 199, which are corrected together in this PR.

Also fixed #3478.

Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
  • The PR is proposed to the proper branch
  • There is a reference to the original bug report and related work
  • There is accuracy test, performance test and test data in opencv_extra repository, if applicable
    Patch to opencv_extra has the same branch name.
  • The feature is well documented and sample code can be built with the project CMake

@ssst0n3
Copy link

ssst0n3 commented Apr 24, 2023

Amazing, too fast

@k3lpi3b4nsh33
Copy link

Orz!!

@TUGOhost
Copy link

amazing

@syuy
Copy link

syuy commented Apr 24, 2023

Cool!

@Lakr233
Copy link

Lakr233 commented Apr 24, 2023

LGTM

@helin0815
Copy link

太猛了哥

@KarKLi
Copy link

KarKLi commented Apr 24, 2023

amazing!

@KongWeibin
Copy link

interesting

@Timon-2022
Copy link

niuwa!!!!mobai dalao!

@Naville
Copy link

Naville commented Apr 24, 2023

ChatGPT 3.5:

What's the bug in this code?

int available = bits.available();
     // try to repair count data if count data is invalid
     if (count * 8 > available) {
         count = (available + 7 / 8);
     }

     ArrayRef<char> bytes_(count);

There is a bug in the line count = (available + 7 / 8);.
The expression 7/8 will be evaluated as integer division, resulting in a value of 0. Therefore, count will always be set to > available rather than being adjusted to the correct number of bytes needed to store the count data.
To fix the bug, the expression should be count = (available + 7) / 8;, which will correctly round up to the nearest integer number of bytes needed to store the count data.

@yilongli-tubi
Copy link

Amazing! This bug seems to just crash my WeChat client program...

@yoghurt-lee
Copy link

amazing!

@Nagico
Copy link

Nagico commented Apr 24, 2023

But today is not Thursday😂

@DapengFeng
Copy link

LGTM

@sfc9982
Copy link

sfc9982 commented Apr 24, 2023

Long live these volunteers! ❤️

@UPON-2021
Copy link

amazing!

@Apple-QAQ
Copy link

Too Fast!

@xieyumc
Copy link

xieyumc commented Apr 24, 2023

QQ和微信都会crash

@NoahZhang1
Copy link

Cool!! Really fast

Copy link

@mochaaP mochaaP left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@Konano
Copy link
Contributor Author

Konano commented Apr 24, 2023

@opencv-alalek @asmorkalov Would it be possible to review and merge of my pull request? Thank you very much!

@HRan2004
Copy link

666

@RJHD-GIT
Copy link

有品

@WanliZhong WanliZhong mentioned this pull request Apr 25, 2023
6 tasks
@JulianSong
Copy link

LGTM

Copy link
Member

@WanliZhong WanliZhong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Konano @GZTimeWalker Hi, thanks for your contribution! Your patch will be the main branch to fix this bug. There are some comments:

  1. put a test image to opencv_extra/testdata/cv/qrcode/
  2. create a new pr to opencv_extra which branch name should be patch-1 in Konano’s repository.
  3. then update your code in opencv_contrib, the CI test will automatically pull the new test data to test your pr
  4. add test code in modules/wechat_qrcode/test/test_qrcode.cpp
    TEST(Objdetect_QRCode_bug, issue_3478) {
        auto detector = wechat_qrcode::WeChatQRCode();
        std::string image_path = findDataFile("qrcode/issue_3478.png");
        Mat src = imread(image_path, IMREAD_GRAYSCALE);
        ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
        std::vector<std::string> outs = detector.detectAndDecode(src);
        ASSERT_EQ(1, (int) outs.size());
        ASSERT_EQ(16, (int) outs[0].size());
        ASSERT_EQ("KFCVW50         ", outs[0]);
    }
  5. after testing, your method does not pass the test sample on windows platform
  6. I suggest you add code at Line 202
    // issue https://github.com/opencv/opencv_contrib/issues/3478
    if (bytes_->empty())
        return;

@Konano
Copy link
Contributor Author

Konano commented Apr 25, 2023

@WanliZhong Thank you for your help!

You mentioned that our method did not pass the test sample on windows platform, so I checked the test logs and the error message for test failure is as follows:

unknown file: error: C++ exception with description "OpenCV(4.7.0-dev) C:\build\precommit-contrib_windows64\4.x\opencv\modules\ts\src\ts.cpp:1071: error: (-2:Unspecified error) OpenCV tests: Can't find required data file: qrcode/issue_3478.png in function 'cvtest::findData'

" thrown in the test body.

It looks like the new test case is not loaded?

@WanliZhong
Copy link
Member

@WanliZhong Thank you for your help!

You mentioned that our method did not pass the test sample on windows platform, so I checked the test logs and the error message for test failure is as follows:

unknown file: error: C++ exception with description "OpenCV(4.7.0-dev) C:\build\precommit-contrib_windows64\4.x\opencv\modules\ts\src\ts.cpp:1071: error: (-2:Unspecified error) OpenCV tests: Can't find required data file: qrcode/issue_3478.png in function 'cvtest::findData'

" thrown in the test body.

It looks like the new test case is not loaded?

Before runing the test, you should add an env variable to refer the test data OPENCV_TEST_DATA_PATH=path/to/opencv_extra/testdata

@GZTimeWalker
Copy link
Contributor

GZTimeWalker commented Apr 25, 2023

@WanliZhong All changes are applied, and we updated the branch https://github.com/Konano/opencv_extra/tree/patch-1

The error on windows x64 are caused by the wrong environment variables, we tested the code localy, it works fine.

image

@WanliZhong
Copy link
Member

WanliZhong commented Apr 25, 2023

Good! Let's check if the test can be passed on all platform

@Henryk07
Copy link

so fast!! 厉害!

Copy link
Member

@zihaomu zihaomu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution! 👍

@zihaomu
Copy link
Member

zihaomu commented Apr 25, 2023

Please take a look. @asmorkalov, @opencv-alalek, @vpisarev.
This error only occurs in wechat_qrcode, but not in the qrcode of opencv/opencv.

@WanliZhong
Copy link
Member

WanliZhong commented Apr 25, 2023

There is a slight difference between qrcode and wechat_qrcode. qrcode module will return an empty string, since the qrcode is considered to be wrong case. But wechat_qrcode will ignore the error and return "KFCVW50 "

@GZTimeWalker
Copy link
Contributor

There is a slight difference between qrcode and wechat_qrcode. qrcode module will return an empty string, since the qrcode is considered to be wrong case. But wechat_qrcode will ignore the error and return "KFCVW50 "

From my point of view, since the data in the mixed mode of the QRCode is parsed from many segments, a decoder should have to output as many partially correct results as possible.

Comment on lines +201 to +206
size_t nBytes = count;

ArrayRef<char> bytes_(nBytes);
// issue https://github.com/opencv/opencv_contrib/issues/3478
if (bytes_->empty())
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, bytes_->empty() should be equivalent to nBytes == 0. So why not using nBytes == 0 for simplicity and efficiency?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this change would be a more visual indication of the intention to avoid null pointers?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

@Konano
Copy link
Contributor Author

Konano commented Apr 25, 2023

🎉 All test cases on all platform have been passed!

@view6view
Copy link

tql,大佬

@hanxu317317
Copy link

昨天刚发现. 今天就修复~够速度 .

@7a3lv7
Copy link

7a3lv7 commented Apr 26, 2023

Amazing

@Konano
Copy link
Contributor Author

Konano commented Apr 26, 2023

It seems that everything is done. Can you please review and merge? @alalek.

@zihaomu zihaomu added the test label Apr 26, 2023
@vpisarev vpisarev self-requested a review April 26, 2023 07:08
@vpisarev vpisarev merged commit ccc2772 into opencv:4.x Apr 26, 2023
@hcqbuqingzhen
Copy link

NIU 大佬

@karzeoy
Copy link

karzeoy commented Apr 26, 2023

amazing!

@Takeoff0518
Copy link

OrzOrzOrzOrz

@Konano
Copy link
Contributor Author

Konano commented Apr 26, 2023

This pr has too much attention on the internet and has been merged, maybe we should lock it to avoid too many pointless comments. @WanliZhong @zihaomu

@opencv opencv locked as resolved and limited conversation to collaborators Apr 26, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

接入微信二维码识别引擎后 这个二维码会crash 微信线上包也一样