Skip to content

Commit 764b51e

Browse files
authored
[fix][tests] Fix Expand, Extract, Select fuzz tests (#1243)
* Fixed expand, extract and select fuzz tests: filtered invalid inputs to prevent invalid memory access
1 parent 316d2ed commit 764b51e

File tree

4 files changed

+133
-50
lines changed

4 files changed

+133
-50
lines changed

tools/tests/fuzzing/low-level-api/expand_fuzz_test.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
#include "qpl/qpl.h"
1212

13+
#include "filtering_fuzz_common.hpp"
14+
1315
#ifndef QPL_EXECUTION_PATH
1416
#define QPL_EXECUTION_PATH qpl_path_software
1517
#endif
@@ -34,23 +36,31 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
3436
Size--;
3537

3638
if (Size > sizeof(expand_properties)) {
37-
expand_properties properties_ptr = *reinterpret_cast<const expand_properties*>(Data);
39+
expand_properties properties = *reinterpret_cast<const expand_properties*>(Data);
3840

3941
// Make sure the output bit width is in the valid range of enum qpl_out_format
40-
properties_ptr.output_bit_width = static_cast<qpl_out_format>(properties_ptr.output_bit_width % 4);
41-
properties_ptr.input_bit_width = properties_ptr.input_bit_width % 32;
42+
properties.output_bit_width =
43+
static_cast<qpl_out_format>(static_cast<uint8_t>(properties.output_bit_width) % 4);
44+
properties.input_bit_width = properties.input_bit_width % 32;
4245

4346
Data += sizeof(expand_properties);
4447
Size -= sizeof(expand_properties);
4548

46-
auto mask_byte_length = properties_ptr.mask_byte_length % (32 * 1024);
47-
if (Size > mask_byte_length) {
49+
auto mask_byte_length = properties.mask_byte_length % (32 * 1024);
50+
51+
const int64_t source_size = static_cast<int64_t>(Size) - static_cast<int64_t>(mask_byte_length);
52+
const bool is_rle_parser = qpl_p_parquet_rle == parser;
53+
const auto source_ptr = Data + mask_byte_length;
54+
const bool is_good_data = validate_filtering_input(properties.input_bit_width, source_ptr, is_rle_parser,
55+
properties.output_bit_width, properties.number_of_elements,
56+
source_size, properties.destination_size);
57+
if (is_good_data) {
4858
std::vector<uint8_t> mask(Data, Data + mask_byte_length);
4959

5060
std::vector<uint8_t> source(Data + mask_byte_length, Data + Size);
51-
properties_ptr.destination_size =
52-
std::min(static_cast<uint32_t>(mask_byte_length * 32), properties_ptr.destination_size);
53-
std::vector<uint8_t> destination(properties_ptr.destination_size);
61+
properties.destination_size =
62+
std::min(static_cast<uint32_t>(mask_byte_length * 32), properties.destination_size);
63+
std::vector<uint8_t> destination(properties.destination_size);
5464

5565
qpl_status status;
5666
uint32_t job_size = 0;
@@ -72,10 +82,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
7282
job_ptr->next_out_ptr = destination.data();
7383
job_ptr->available_out = destination.size();
7484
job_ptr->op = qpl_op_expand;
75-
job_ptr->num_input_elements = properties_ptr.number_of_elements;
76-
job_ptr->src1_bit_width = properties_ptr.input_bit_width;
77-
job_ptr->src2_bit_width = properties_ptr.input_bit_width_2;
78-
job_ptr->out_bit_width = properties_ptr.output_bit_width;
85+
job_ptr->num_input_elements = properties.number_of_elements;
86+
job_ptr->src1_bit_width = properties.input_bit_width;
87+
job_ptr->src2_bit_width = properties.input_bit_width_2;
88+
job_ptr->out_bit_width = properties.output_bit_width;
7989
job_ptr->parser = parser;
8090

8191
status = qpl_execute_job(job_ptr);

tools/tests/fuzzing/low-level-api/extract_fuzz_test.cpp

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
#include "qpl/qpl.h"
1111

12+
#include "filtering_fuzz_common.hpp"
13+
1214
#ifndef QPL_EXECUTION_PATH
1315
#define QPL_EXECUTION_PATH qpl_path_software
1416
#endif
@@ -33,36 +35,50 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
3335
Size--;
3436

3537
if (Size > sizeof(extract_properties)) {
36-
auto* properties_ptr = reinterpret_cast<const extract_properties*>(Data);
37-
std::vector<uint8_t> source(Data + sizeof(extract_properties), Data + Size);
38-
std::vector<uint8_t> destination(properties_ptr->destination_size);
39-
40-
qpl_status status;
41-
uint32_t job_size = 0;
42-
43-
// Job initialization
44-
status = qpl_get_job_size(execution_path, &job_size);
45-
if (status != QPL_STS_OK) { return 0; }
46-
47-
auto job_buffer = std::make_unique<uint8_t[]>(job_size);
48-
qpl_job* job_ptr = reinterpret_cast<qpl_job*>(job_buffer.get());
49-
50-
status = qpl_init_job(execution_path, job_ptr);
51-
if (status != QPL_STS_OK) { return 0; }
52-
53-
job_ptr->next_in_ptr = source.data();
54-
job_ptr->available_in = source.size();
55-
job_ptr->next_out_ptr = destination.data();
56-
job_ptr->available_out = destination.size();
57-
job_ptr->op = qpl_op_extract;
58-
job_ptr->num_input_elements = properties_ptr->number_of_elements;
59-
job_ptr->src1_bit_width = properties_ptr->input_bit_width;
60-
job_ptr->param_low = properties_ptr->low_index_boundary;
61-
job_ptr->param_high = properties_ptr->high_index_boundary;
62-
job_ptr->out_bit_width = properties_ptr->output_bit_width;
63-
job_ptr->parser = parser;
64-
65-
status = qpl_execute_job(job_ptr);
38+
extract_properties properties = *reinterpret_cast<const extract_properties*>(Data);
39+
40+
// Make sure the output bit width is in the valid range of enum qpl_out_format
41+
properties.output_bit_width =
42+
static_cast<qpl_out_format>(static_cast<uint8_t>(properties.output_bit_width) % 4);
43+
44+
const int64_t source_size = static_cast<int64_t>(Size) - sizeof(extract_properties);
45+
const bool is_rle_parser = qpl_p_parquet_rle == parser;
46+
const auto source_ptr = Data + sizeof(extract_properties);
47+
bool is_good_data = validate_filtering_input(properties.input_bit_width, source_ptr, is_rle_parser,
48+
properties.output_bit_width, properties.number_of_elements,
49+
source_size, properties.destination_size);
50+
if (properties.high_index_boundary > properties.number_of_elements) { is_good_data = false; }
51+
if (is_good_data) {
52+
std::vector<uint8_t> source(Data + sizeof(extract_properties), Data + Size);
53+
std::vector<uint8_t> destination(properties.destination_size);
54+
55+
qpl_status status;
56+
uint32_t job_size = 0;
57+
58+
// Job initialization
59+
status = qpl_get_job_size(execution_path, &job_size);
60+
if (status != QPL_STS_OK) { return 0; }
61+
62+
auto job_buffer = std::make_unique<uint8_t[]>(job_size);
63+
qpl_job* job_ptr = reinterpret_cast<qpl_job*>(job_buffer.get());
64+
65+
status = qpl_init_job(execution_path, job_ptr);
66+
if (status != QPL_STS_OK) { return 0; }
67+
68+
job_ptr->next_in_ptr = source.data();
69+
job_ptr->available_in = source.size();
70+
job_ptr->next_out_ptr = destination.data();
71+
job_ptr->available_out = destination.size();
72+
job_ptr->op = qpl_op_extract;
73+
job_ptr->num_input_elements = properties.number_of_elements;
74+
job_ptr->src1_bit_width = properties.input_bit_width;
75+
job_ptr->param_low = properties.low_index_boundary;
76+
job_ptr->param_high = properties.high_index_boundary;
77+
job_ptr->out_bit_width = properties.output_bit_width;
78+
job_ptr->parser = parser;
79+
80+
status = qpl_execute_job(job_ptr);
81+
}
6682
}
6783

6884
return 0;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*******************************************************************************
2+
* Copyright (C) 2025 Intel Corporation
3+
*
4+
* SPDX-License-Identifier: MIT
5+
******************************************************************************/
6+
7+
#pragma once
8+
9+
#include "qpl/c_api/defs.h"
10+
11+
constexpr uint32_t qpl_out_format_to_uint(qpl_out_format output) {
12+
switch (output) {
13+
case qpl_ow_nom: return 1U;
14+
case qpl_ow_8: return 8U;
15+
case qpl_ow_16: return 16U;
16+
case qpl_ow_32: return 32U;
17+
default: return 1U;
18+
}
19+
}
20+
21+
inline bool validate_filtering_input(uint32_t input_bit_width_1, const uint8_t* p_input_bit_width_2,
22+
bool is_bit_width_2, qpl_out_format out_format_enum, size_t number_of_elements,
23+
int64_t src_available_bytes, size_t dst_available_bytes) {
24+
if (src_available_bytes <= 0) { return false; }
25+
26+
// Check if input and output bit widths are valid
27+
uint32_t input_bit_width = input_bit_width_1;
28+
if (is_bit_width_2) { input_bit_width = static_cast<uint32_t>(*p_input_bit_width_2); }
29+
const uint32_t output_bit_width = qpl_out_format_to_uint(out_format_enum);
30+
if (input_bit_width > output_bit_width) { return false; }
31+
32+
// Check if input and output sizes are valid
33+
const size_t src_required_bytes = (number_of_elements * input_bit_width + 7) / 8;
34+
const size_t dst_required_bytes = (number_of_elements * output_bit_width + 7) / 8;
35+
if (src_required_bytes > src_available_bytes || dst_required_bytes > dst_available_bytes) { return false; }
36+
37+
// Check if number of elements can be written with the given output bit width
38+
if (output_bit_width != 1U) {
39+
const uint32_t max_output_value = (1ULL << output_bit_width) - 1;
40+
if (max_output_value < number_of_elements) { return false; }
41+
}
42+
return true;
43+
}

tools/tests/fuzzing/low-level-api/select_fuzz_test.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
#include "qpl/qpl.h"
1111

12+
#include "filtering_fuzz_common.hpp"
13+
1214
#ifndef QPL_EXECUTION_PATH
1315
#define QPL_EXECUTION_PATH qpl_path_software
1416
#endif
@@ -33,14 +35,26 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
3335
Size--;
3436

3537
if (Size > sizeof(select_properties)) {
36-
auto* properties_ptr = reinterpret_cast<const select_properties*>(Data);
37-
auto mask_byte_length = properties_ptr->mask_byte_length % 4096;
38-
if (Size > sizeof(select_properties) + mask_byte_length) {
38+
select_properties properties = *reinterpret_cast<const select_properties*>(Data);
39+
auto mask_byte_length = properties.mask_byte_length % 4096;
40+
41+
// Make sure the output bit width is in the valid range of enum qpl_out_format
42+
properties.output_bit_width =
43+
static_cast<qpl_out_format>(static_cast<uint8_t>(properties.output_bit_width) % 4);
44+
45+
const int64_t source_size = static_cast<int64_t>(Size) - static_cast<int64_t>(sizeof(select_properties)) -
46+
static_cast<int64_t>(mask_byte_length);
47+
const bool is_rle_parser = qpl_p_parquet_rle == parser;
48+
const auto source_ptr = Data + sizeof(select_properties) + mask_byte_length;
49+
const bool is_good_data = validate_filtering_input(properties.input_bit_width, source_ptr, is_rle_parser,
50+
properties.output_bit_width, properties.number_of_elements,
51+
source_size, properties.destination_size);
52+
if (is_good_data) {
3953
std::vector<uint8_t> mask(Data + sizeof(select_properties),
4054
Data + sizeof(select_properties) + mask_byte_length);
4155

4256
std::vector<uint8_t> source(Data + sizeof(select_properties) + mask_byte_length, Data + Size);
43-
std::vector<uint8_t> destination(properties_ptr->destination_size);
57+
std::vector<uint8_t> destination(properties.destination_size);
4458

4559
qpl_status status;
4660
uint32_t job_size = 0;
@@ -62,10 +76,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
6276
job_ptr->next_out_ptr = destination.data();
6377
job_ptr->available_out = destination.size();
6478
job_ptr->op = qpl_op_select;
65-
job_ptr->num_input_elements = properties_ptr->number_of_elements;
66-
job_ptr->src1_bit_width = properties_ptr->input_bit_width;
67-
job_ptr->src2_bit_width = properties_ptr->input_bit_width_2;
68-
job_ptr->out_bit_width = properties_ptr->output_bit_width;
79+
job_ptr->num_input_elements = properties.number_of_elements;
80+
job_ptr->src1_bit_width = properties.input_bit_width;
81+
job_ptr->src2_bit_width = properties.input_bit_width_2;
82+
job_ptr->out_bit_width = properties.output_bit_width;
6983
job_ptr->parser = parser;
7084

7185
status = qpl_execute_job(job_ptr);

0 commit comments

Comments
 (0)