mirror of
https://github.com/opencv/opencv.git
synced 2026-11-09 18:04:29 -06:00
Merge pull request #30395 from uwezkhan:onnx-qconv-scale-bias-bound
dnn: bound per-channel w_scale to bias length in onnx parseQConv 🤖🤖🤖 - #30395 OpenCV Extra: https://github.com/opencv/opencv_extra/pull/1445 `QLinearConv` import folds a constant int32 bias to float in `parseQConv` (`modules/dnn/src/onnx/onnx_importer2.cpp`): each bias element is multiplied by its per-channel weight scale and written into `bias_float`, which is sized from the bias tensor. The fold loop runs over the length of `w_scale`, but `w_scale` and the bias are independent inputs of the model, so a 1-D `w_scale` longer than the bias writes past `bias_float`. It is reached from `readNetFromONNX` on any `.onnx` carrying such a `QLinearConv`, and the scalar `x_scale` read is likewise unguarded against an empty tensor. ASan on the default engine loading a model whose bias has one element and whose per-channel `w_scale` has 64: ``` ==ERROR: AddressSanitizer: heap-buffer-overflow ... READ of size 4 #0 ONNXImporter2::parseQConv(...) #1 ONNXImporter2::parseNode(...) #2 ONNXImporter2::parseGraph(...) #5 readNetFromONNX2(...) #6 readNetFromONNX(...) 0x... is located 0 bytes after 4-byte region allocated by Mat::convertTo in parseQConv ``` Require the per-channel `w_scale` to hold one entry per bias element before the loop, the per-axis quantization invariant that pairs one scale and one bias with each output channel, and check the scalar `x_scale` is non-empty before the read. A well-formed model is unaffected; a mismatched one now throws from the importer and the net loads empty instead of overrunning the buffer. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] 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 - [x] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [x] 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
This commit is contained in:
@@ -2604,12 +2604,17 @@ void ONNXImporter2::parseQConv(LayerParams& layerParams, const opencv_onnx::Node
|
||||
|
||||
Mat bias_float;
|
||||
bias_int32.convertTo(bias_float, CV_32F);
|
||||
CV_CheckGE(x_scale_mat.total(), (size_t)1, "DNN/ONNX/QLinearConv: x_scale is empty");
|
||||
float x_sc = x_scale_mat.at<float>(0);
|
||||
if (w_scale_mat.total() == 1)
|
||||
bias_float *= x_sc * w_scale_mat.at<float>(0);
|
||||
else
|
||||
{
|
||||
CV_CheckEQ(w_scale_mat.total(), bias_float.total(),
|
||||
"DNN/ONNX/QLinearConv: per-channel w_scale length must match the bias length");
|
||||
for (int i = 0; i < (int)w_scale_mat.total(); i++)
|
||||
bias_float.at<float>(i) *= x_sc * w_scale_mat.at<float>(i);
|
||||
}
|
||||
|
||||
bias_arg = netimpl->newConstArg(bn + "/bias_float", bias_float);
|
||||
}
|
||||
|
||||
@@ -3897,4 +3897,26 @@ INSTANTIATE_TEST_CASE_P(/**/, Test_ONNX_MHA_Lowering, testing::Values(
|
||||
"multi_head_attention_key_padding_mask_attention_bias"
|
||||
));
|
||||
|
||||
// QLinearConv folds a const int32 bias to float by multiplying each bias element
|
||||
// by the matching per-channel weight scale, writing into a buffer sized from the
|
||||
// bias tensor. w_scale and the bias are independent model fields, so a w_scale
|
||||
// longer than the bias wrote past the bias buffer during import. The mismatched
|
||||
// pair has to be rejected instead of overrunning the buffer.
|
||||
// Test data in opencv_extra under the same branch name as this patch.
|
||||
TEST(Test_ONNX_importer, parse_qconv_bias_scale_mismatch)
|
||||
{
|
||||
Net net;
|
||||
EXPECT_NO_THROW(net = readNetFromONNX(_tf("models/qconv_bias_scale_mismatch.onnx")));
|
||||
EXPECT_TRUE(net.empty());
|
||||
}
|
||||
|
||||
// The same bias fold reads the scalar x_scale unconditionally, so an empty
|
||||
// x_scale has to be rejected instead of being read past its end.
|
||||
TEST(Test_ONNX_importer, parse_qconv_empty_x_scale)
|
||||
{
|
||||
Net net;
|
||||
EXPECT_NO_THROW(net = readNetFromONNX(_tf("models/qconv_empty_x_scale.onnx")));
|
||||
EXPECT_TRUE(net.empty());
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
Reference in New Issue
Block a user