Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions qa/L0_infer/infer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

import infer_util as iu
import numpy as np
import requests
import test_util as tu
from tritonclient.utils import *

Expand Down Expand Up @@ -314,6 +315,60 @@ def test_raw_uuu(self):
np.uint8, np.uint8, np.uint8, output0_raw=True, output1_raw=True, swap=True
)

@unittest.skipIf(not USE_HTTP, "HTTP testing is disabled")
@unittest.skipIf("onnx" not in BACKENDS, "ONNX backend is disabled")
def test_http_json_tensor_data_out_of_range(self):
if BATCH:
model_prefix = "onnx"
shape = [1, 16]
elif NOBATCH:
model_prefix = "onnx_nobatch"
shape = [16]
else:
self.skipTest("No ONNX model is enabled")

test_cases = [
(np.uint8, "UINT8", 256, "UINT8 data value 256 is out of range"),
(np.int8, "INT8", 128, "INT8 data value 128 is out of range"),
]

for input_dtype, datatype, invalid_value, expected_error in test_cases:
with self.subTest(datatype=datatype):
model_name = tu.get_model_name(
model_prefix, input_dtype, input_dtype, input_dtype
)
tensor_data = [invalid_value] + ([0] * 15)
payload = {
"inputs": [
{
"name": "INPUT0",
"shape": shape,
"datatype": datatype,
"data": tensor_data,
},
{
"name": "INPUT1",
"shape": shape,
"datatype": datatype,
"data": [0] * 16,
},
],
"outputs": [{"name": "OUTPUT0"}, {"name": "OUTPUT1"}],
}

infer_url = (
f"http://{os.environ.get('TRITONSERVER_IPADDR', 'localhost')}:8000"
f"/v2/models/{model_name}/infer"
)
response = requests.post(
infer_url,
json=payload,
timeout=NETWORK_TIMEOUT,
)

self.assertEqual(response.status_code, 400, response.text)
self.assertIn(expected_error, response.json()["error"])

def test_raw_bbb(self):
self._full_exact(
np.int8, np.int8, np.int8, output0_raw=True, output1_raw=True, swap=True
Expand Down
58 changes: 45 additions & 13 deletions src/http_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#include <algorithm>
#include <cstdlib>
#include <limits>
#include <list>
#include <regex>
#include <thread>
Expand Down Expand Up @@ -476,6 +477,37 @@ AllocEVBuffer(const size_t byte_size, evbuffer** evb, void** base)
return nullptr; // success
}

template <typename T>
TRITONSERVER_Error*
ValidateJsonUIntRange(const uint64_t value, const char* datatype)
{
if (value > static_cast<uint64_t>(std::numeric_limits<T>::max())) {
return TRITONSERVER_ErrorNew(
TRITONSERVER_ERROR_INVALID_ARG,
(std::string(datatype) + " data value " + std::to_string(value) +
" is out of range")
.c_str());
}

return nullptr;
}

template <typename T>
TRITONSERVER_Error*
ValidateJsonIntRange(const int64_t value, const char* datatype)
{
if ((value < static_cast<int64_t>(std::numeric_limits<T>::min())) ||
(value > static_cast<int64_t>(std::numeric_limits<T>::max()))) {
return TRITONSERVER_ErrorNew(
TRITONSERVER_ERROR_INVALID_ARG,
(std::string(datatype) + " data value " + std::to_string(value) +
" is out of range")
.c_str());
}

return nullptr;
}

// Recursively adds to byte_size from multi dimensional data input
TRITONSERVER_Error*
JsonBytesArrayByteSize(
Expand Down Expand Up @@ -563,34 +595,34 @@ ReadDataFromJsonHelper(
bool b = false;
RETURN_IF_ERR(tensor_data.AsBool(&b));
uint8_t* data_vec = reinterpret_cast<uint8_t*>(base);
// FIXME for unsigned should bounds check and raise error
// since otherwise the actually used value will be
// unexpected.
data_vec[*counter] = (uint8_t)(b ? 1 : 0);
data_vec[*counter] = static_cast<uint8_t>(b ? 1 : 0);
*counter += 1;
break;
}
case TRITONSERVER_TYPE_UINT8: {
uint64_t ui = 0;
RETURN_IF_ERR(tensor_data.AsUInt(&ui));
RETURN_IF_ERR(ValidateJsonUIntRange<uint8_t>(ui, "UINT8"));
uint8_t* data_vec = reinterpret_cast<uint8_t*>(base);
data_vec[*counter] = (uint8_t)ui;
data_vec[*counter] = static_cast<uint8_t>(ui);
*counter += 1;
break;
}
case TRITONSERVER_TYPE_UINT16: {
uint64_t ui = 0;
RETURN_IF_ERR(tensor_data.AsUInt(&ui));
RETURN_IF_ERR(ValidateJsonUIntRange<uint16_t>(ui, "UINT16"));
uint16_t* data_vec = reinterpret_cast<uint16_t*>(base);
data_vec[*counter] = (uint16_t)ui;
data_vec[*counter] = static_cast<uint16_t>(ui);
*counter += 1;
break;
}
case TRITONSERVER_TYPE_UINT32: {
uint64_t ui = 0;
RETURN_IF_ERR(tensor_data.AsUInt(&ui));
RETURN_IF_ERR(ValidateJsonUIntRange<uint32_t>(ui, "UINT32"));
uint32_t* data_vec = reinterpret_cast<uint32_t*>(base);
data_vec[*counter] = (uint32_t)ui;
data_vec[*counter] = static_cast<uint32_t>(ui);
*counter += 1;
break;
}
Expand All @@ -603,29 +635,29 @@ ReadDataFromJsonHelper(
break;
}
case TRITONSERVER_TYPE_INT8: {
// FIXME for signed type just assigning to smaller type is
// "implementation defined" and so really need to bounds
// check.
int64_t si = 0;
RETURN_IF_ERR(tensor_data.AsInt(&si));
RETURN_IF_ERR(ValidateJsonIntRange<int8_t>(si, "INT8"));
int8_t* data_vec = reinterpret_cast<int8_t*>(base);
data_vec[*counter] = (int8_t)si;
data_vec[*counter] = static_cast<int8_t>(si);
*counter += 1;
break;
}
case TRITONSERVER_TYPE_INT16: {
int64_t si = 0;
RETURN_IF_ERR(tensor_data.AsInt(&si));
RETURN_IF_ERR(ValidateJsonIntRange<int16_t>(si, "INT16"));
int16_t* data_vec = reinterpret_cast<int16_t*>(base);
data_vec[*counter] = (int16_t)si;
data_vec[*counter] = static_cast<int16_t>(si);
*counter += 1;
break;
}
case TRITONSERVER_TYPE_INT32: {
int64_t si = 0;
RETURN_IF_ERR(tensor_data.AsInt(&si));
RETURN_IF_ERR(ValidateJsonIntRange<int32_t>(si, "INT32"));
int32_t* data_vec = reinterpret_cast<int32_t*>(base);
data_vec[*counter] = (int32_t)si;
data_vec[*counter] = static_cast<int32_t>(si);
*counter += 1;
break;
}
Expand Down