Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
26 changes: 26 additions & 0 deletions qa/L0_http/http_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,32 @@ def test_repository_index_deeply_nested_json(self):
"Expected server to remain live after deeply nested JSON request.",
)

def test_inference_header_content_length_out_of_range(self):
"""Inference-Header-Content-Length value exceeding INT_MAX triggers
std::out_of_range in std::stoi. Before the fix this crashed the server
process via std::terminate(); now it must return 400 and leave the
server alive."""
model = "onnx_zero_1_float32"
headers = {"Inference-Header-Content-Length": "99999999999"}
r = requests.post(
self._get_infer_url(model),
json={"inputs": []},
headers=headers,
)
self.assertEqual(
400,
r.status_code,
"Expected 400 for out-of-range Inference-Header-Content-Length; "
"got: {}".format(r.status_code),
)
# Server must still be alive — a crash would make this fail.
health = requests.get("http://localhost:8000/v2/health/live")
self.assertEqual(
200,
health.status_code,
"Server is not live after out-of-range Inference-Header-Content-Length request",
)


if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion qa/L0_http/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ fi

TEST_RESULT_FILE='test_results.txt'
PYTHON_TEST=http_test.py
EXPECTED_NUM_TESTS=16
EXPECTED_NUM_TESTS=17
set +e
python $PYTHON_TEST >$CLIENT_LOG 2>&1
if [ $? -ne 0 ]; then
Expand Down
36 changes: 36 additions & 0 deletions qa/L0_sagemaker/sagemaker_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,42 @@ def test_malformed_binary_header_large_number(self):
),
)

def test_malformed_binary_header_out_of_range(self):
"""json-header-size value exceeding INT_MAX triggers std::out_of_range
in std::stoi. Before the fix this crashed the server process via
std::terminate(); now it must return 400 and leave the server alive."""
inputs = []
outputs = []
inputs.append(httpclient.InferInput("INPUT0", [1, 16], "INT32"))
inputs.append(httpclient.InferInput("INPUT1", [1, 16], "INT32"))
input_data = np.array(self.input_data_, dtype=np.int32)
input_data = np.expand_dims(input_data, axis=0)
inputs[0].set_data_from_numpy(input_data, binary_data=True)
inputs[1].set_data_from_numpy(input_data, binary_data=False)
outputs.append(httpclient.InferRequestedOutput("OUTPUT0", binary_data=False))
outputs.append(httpclient.InferRequestedOutput("OUTPUT1", binary_data=False))
(request_body, _) = httpclient.InferenceServerClient.generate_request_body(
inputs, outputs=outputs
)
headers = {
"Content-Type": "application/vnd.sagemaker-triton.binary+json;json-header-size=99999999999"
}
r = requests.post(self.url_, data=request_body, headers=headers)
self.assertEqual(
400,
r.status_code,
"Expected 400 for out-of-range json-header-size; got: {}".format(
r.status_code
),
)
# Server must still be alive — a crash would make this fail.
health = requests.get(self.url_.replace("/invocations", "/ping"))
self.assertEqual(
200,
health.status_code,
"Server is not live after out-of-range json-header-size request",
)


if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion qa/L0_sagemaker/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ SAGEMAKER_MULTI_MODEL_TEST=sagemaker_multi_model_test.py
SAGEMAKER_GENERATE_TEST=sagemaker_generate_test.py
SAGEMAKER_GENERATE_STREAM_TEST=sagemaker_generate_stream_test.py
MULTI_MODEL_UNIT_TEST_COUNT=7
UNIT_TEST_COUNT=9
UNIT_TEST_COUNT=10
GENERATE_UNIT_TEST_COUNT=1
GENERATE_STREAM_UNIT_TEST_COUNT=1
CLIENT_LOG="./client.log"
Expand Down
2 changes: 1 addition & 1 deletion qa/L0_vertex_ai/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ rm -f *.log
rm -f *.out

CLIENT_TEST_SCRIPT=vertex_ai_test.py
UNIT_TEST_COUNT=8
UNIT_TEST_COUNT=9
CLIENT_LOG="./client.log"

DATADIR=/data/inferenceserver/${REPO_VERSION}
Expand Down
38 changes: 38 additions & 0 deletions qa/L0_vertex_ai/vertex_ai_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,44 @@ def test_malformed_binary_header_large_number(self):
),
)

def test_malformed_binary_header_out_of_range(self):
"""json-header-size value exceeding INT_MAX triggers std::out_of_range
in std::stoi. Before the fix this crashed the server process via
std::terminate(); now it must return 400 and leave the server alive."""
inputs = []
outputs = []
inputs.append(httpclient.InferInput("INPUT0", [1, 16], "INT32"))
inputs.append(httpclient.InferInput("INPUT1", [1, 16], "INT32"))
input_data = np.array(self.input_data_, dtype=np.int32)
input_data = np.expand_dims(input_data, axis=0)
inputs[0].set_data_from_numpy(input_data, binary_data=True)
inputs[1].set_data_from_numpy(input_data, binary_data=False)
outputs.append(httpclient.InferRequestedOutput("OUTPUT0", binary_data=False))
outputs.append(httpclient.InferRequestedOutput("OUTPUT1", binary_data=False))
(request_body, _) = httpclient.InferenceServerClient.generate_request_body(
inputs, outputs=outputs
)
headers = {
"Content-Type": "application/vnd.vertex-ai-triton.binary+json;json-header-size=99999999999"
}
r = requests.post(self.url_, data=request_body, headers=headers)
self.assertEqual(
400,
r.status_code,
"Expected 400 for out-of-range json-header-size; got: {}".format(
r.status_code
),
)
# Server must still be alive — a crash would make this fail.
port = os.getenv("AIP_HTTP_PORT", "8080")
health_endpoint = os.getenv("AIP_HEALTH_ROUTE", "/health")
health = requests.get("http://localhost:{}{}".format(port, health_endpoint))
self.assertEqual(
200,
health.status_code,
"Server is not live after out-of-range json-header-size request",
)


if __name__ == "__main__":
unittest.main()
10 changes: 10 additions & 0 deletions src/http_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2493,6 +2493,16 @@ HTTPAPIServer::GetInferenceHeaderLength(
", got: " + header_length_c_str)
.c_str());
}
catch (const std::out_of_range& oor) {
return TRITONSERVER_ErrorNew(
TRITONSERVER_ERROR_INVALID_ARG,
(std::string("Unable to parse ") + kInferHeaderContentLengthHTTPHeader +
", value is out of range [ " +
std::to_string(std::numeric_limits<std::int32_t>::min()) + ", " +
std::to_string(std::numeric_limits<std::int32_t>::max()) +
" ], got: " + header_length_c_str)
.c_str());
}

// Check if the content length is in proper range
if ((parsed_value < 0) || (parsed_value > content_length)) {
Expand Down
15 changes: 15 additions & 0 deletions src/sagemaker_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ SagemakerAPIServer::GetInferenceHeaderLength(
(content_type_c_str + binary_mime_type_.length()))
.c_str());
}
catch (const std::out_of_range& oor) {
return TRITONSERVER_ErrorNew(
TRITONSERVER_ERROR_INVALID_ARG,
(std::string("Unable to parse inference header size, value is out "
"of range, got: ") +
(content_type_c_str + binary_mime_type_.length()))
.c_str());
}

// Check if the content length is in proper range
if ((parsed_value < 0) || (parsed_value > content_length)) {
Expand Down Expand Up @@ -519,6 +527,13 @@ SagemakerAPIServer::SageMakerMMEHandleInfer(
", got: " + content_length_c_str)
.c_str());
}
catch (const std::out_of_range& oor) {
err = TRITONSERVER_ErrorNew(
TRITONSERVER_ERROR_INVALID_ARG,
(std::string("Unable to parse ") + kContentLengthHeader +
", value is out of range, got: " + content_length_c_str)
.c_str());
}
}
} else {
// The Content-Length doesn't reflect the actual request body size
Expand Down
8 changes: 8 additions & 0 deletions src/vertex_ai_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ VertexAiAPIServer::GetInferenceHeaderLength(
(content_type_c_str + binary_mime_type_.length()))
.c_str());
}
catch (const std::out_of_range& oor) {
return TRITONSERVER_ErrorNew(
TRITONSERVER_ERROR_INVALID_ARG,
(std::string("Unable to parse inference header size, value is out "
"of range, got: ") +
(content_type_c_str + binary_mime_type_.length()))
.c_str());
}

// Check if the content length is in proper range
if ((parsed_value < 0) || (parsed_value > content_length)) {
Expand Down
Loading