the penultimate challenge bug report construction in the
play

The Penultimate Challenge: Bug report construction in the Clang - PowerPoint PPT Presentation

The Penultimate Challenge: Bug report construction in the Clang Static Analyzer Kristf Umann dkszelethus@gmail.com Etvs Lornd University, Budapest Ericsson Hungary 2019. oct. 22. 2/38 Clear, precise bug reports are important One of


  1. [B5 (ENTRY)] [B1] foo: [B0 (EXIT)] [B0 (EXIT)] [B2 (ENTRY)] flag = coin(); [B1] main: [B0 (EXIT)] *x = 5 x == nullptr; [B4] if (flag) foo(); [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; 8/38 flag ∈ ( −∞ , ∞ ) ;

  2. [B5 (ENTRY)] x == nullptr; foo: [B0 (EXIT)] [B2 (ENTRY)] flag = coin(); [B1] main: [B0 (EXIT)] *x = 5 [B1] 8/38 [B4] if (flag) foo(); [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; flag ∈ ( −∞ , ∞ ) ;

  3. [B5 (ENTRY)] [B4] foo: [B0 (EXIT)] [B2 (ENTRY)] flag = coin(); [B1] main: [B0 (EXIT)] *x = 5 [B1] x == nullptr; if (flag) foo(); [B2] if (flag) foo(); [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; 8/38 flag ∈ ( −∞ , ∞ ) ;

  4. [B5 (ENTRY)] [B4] foo: [B0 (EXIT)] [B2 (ENTRY)] flag = coin(); [B1] main: [B0 (EXIT)] dereference of x! x == nullptr; *x = 5 [B1] *x = 5 [B1] if (flag) foo(); [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; 8/38 flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ;

  5. The ExplodedGraph Contains everything the analyzer learned during symbolic execution All explored paths of execution Every symbolic value in every program state 9/38

  6. [B5 (ENTRY)] flag = 0 x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) dereference of x! *x = 5 x = (heap allocated object) x = nullptr *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr [B5 (ENTRY)] x = nullptr (after the call to foo ) main: [B4] int *x = 0; flag = 1; foo(); if (flag) [B3] x = new int; [B2] foo(); if (flag) [B1] *x = 5 [B0 (EXIT)] 10/38 flag = 1 [B1] flag = coin(); x = nullptr [B2 (ENTRY)] [B0 (EXIT)] foo: flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  7. [B5 (ENTRY)] dereference of x! (after the call to foo ) [B4] x = nullptr x = nullptr x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) *x = 5 flag = 1 flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr x = nullptr 10/38 x = nullptr flag = 1 int *x = 0; flag = 1; foo(); if (flag) [B4] int *x = 0; flag = 1; foo(); if (flag) [B3] x = new int; [B2] foo(); if (flag) flag = coin(); [B2 (ENTRY)] *x = 5 foo: [B0 (EXIT)] main: [B1] [B0 (EXIT)] [B1] flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  8. [B5 (ENTRY)] flag = 0 x = nullptr x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) dereference of x! *x = 5 x = (heap allocated object) x = nullptr *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr [B4] 10/38 (after the call to foo ) [B1] int *x = 0; flag = 1; foo(); if (flag) [B3] x = new int; [B2] foo(); if (flag) [B1] *x = 5 [B0 (EXIT)] main: x = nullptr flag = coin(); [B2 (ENTRY)] x = nullptr [B0 (EXIT)] foo: flag = 1 flag = 1 flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  9. [B5 (ENTRY)] *x = 5 [B4] x = nullptr x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) dereference of x! flag = 0 (after the call to foo ) x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr x = nullptr 10/38 x = nullptr [B1] int *x = 0; flag = 1; foo(); if (flag) [B3] x = new int; [B2] foo(); if (flag) [B1] *x = 5 [B0 (EXIT)] main: flag = 1 flag = coin(); [B2 (ENTRY)] flag = 1 [B2 (ENTRY)] [B0 (EXIT)] foo: x = nullptr flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  10. [B5 (ENTRY)] *x = 5 [B4] x = nullptr x = nullptr x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) dereference of x! flag = 0 x = nullptr x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr (after the call to foo ) 10/38 (after the call to foo ) flag = coin(); [B1] *x = 5 [B0 (EXIT)] main: [B1] [B2] flag = coin(); [B1] x = new int; [B3] [B2 (ENTRY)] foo(); [B0 (EXIT)] if (flag) foo: foo(); flag = 1 flag = 1; x = nullptr flag = 1 x = nullptr int *x = 0; if (flag) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  11. [B5 (ENTRY)] *x = 5 [B4] x = nullptr x = nullptr x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) dereference of x! flag = 0 x = nullptr x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr (after the call to foo ) 10/38 [B1] if (flag) [B1] *x = 5 [B0 (EXIT)] main: [B2] x = new int; flag = coin(); [B2 (ENTRY)] [B3] [B0 (EXIT)] foo(); [B0 (EXIT)] foo: foo(); flag = 1 flag = 1; x = nullptr flag = 1 int *x = 0; x = nullptr (after the call to foo ) if (flag) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  12. [B5 (ENTRY)] *x = 5 [B4] x = nullptr x = nullptr x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) dereference of x! flag = 0 x = nullptr x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr (after the call to foo ) 10/38 [B1] if (flag) foo(); if (flag) [B1] *x = 5 [B0 (EXIT)] main: [B3] flag = coin(); [B2 (ENTRY)] foo(); x = new int; [B0 (EXIT)] foo: flag = 1 flag = 1; x = nullptr int *x = 0; flag = 1 x = nullptr (after the call to foo ) [B2] flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  13. [B5 (ENTRY)] dereference of x! x = nullptr [B4] x = nullptr x = nullptr x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) *x = 5 x = nullptr flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr (after the call to foo ) (after the call to foo ) flag = 1 foo(); if (flag) foo(); [B2] x = new int; [B3] if (flag) flag = 1; *x = 5 int *x = 0; [B4] if (flag) foo(); flag = 1; int *x = 0; x = nullptr [B1] 10/38 [B0 (EXIT)] flag = 1 [B0 (EXIT)] main: [B1] flag = coin(); [B2 (ENTRY)] foo: flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  14. [B5 (ENTRY)] dereference of x! [B4] x = nullptr x = nullptr x = nullptr x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) *x = 5 x = nullptr flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr (after the call to foo ) 10/38 x = new int; flag = coin(); [B2] foo(); if (flag) [B1] *x = 5 [B0 (EXIT)] x = new int; main: [B1] [B3] if (flag) [B2 (ENTRY)] [B3] [B0 (EXIT)] foo(); foo: flag = 1; flag = 1 int *x = 0; x = nullptr flag = 1 x = nullptr (after the call to foo ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  15. [B5 (ENTRY)] x = (heap allocated object) x = nullptr [B4] x = nullptr x = nullptr x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined dereference of x! x = nullptr *x = 5 flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr (after the call to foo ) 10/38 main: [B2 (ENTRY)] foo(); if (flag) [B1] *x = 5 [B0 (EXIT)] [B3] [B1] x = new int; [B3] flag = coin(); [B0 (EXIT)] x = new int; if (flag) foo: flag = 1 foo(); x = nullptr flag = 1; flag = 1 x = nullptr (after the call to foo ) int *x = 0; [B2] flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  16. [B5 (ENTRY)] dereference of x! [B4] x = nullptr x = nullptr x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) *x = 5 x = nullptr flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr (after the call to foo ) x = nullptr 10/38 [B0 (EXIT)] int *x = 0; flag = 1; foo(); if (flag) [B3] x = new int; [B2] foo(); if (flag) [B2] foo(); if (flag) [B1] *x = 5 main: [B1] flag = 1 (after the call to foo ) x = nullptr flag = coin(); [B2 (ENTRY)] flag = 1 [B0 (EXIT)] foo: x = nullptr flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  17. [B5 (ENTRY)] dereference of x! [B4] x = nullptr x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) *x = 5 (after the call to foo ) flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr x = nullptr x = nullptr x = nullptr [B2 (ENTRY)] int *x = 0; flag = 1; foo(); if (flag) [B3] x = new int; [B2] foo(); if (flag) [B1] *x = 5 [B0 (EXIT)] main: [B1] flag = coin(); 10/38 flag = 1 (after the call to foo ) flag = 1 x = nullptr x = nullptr foo: [B0 (EXIT)] flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  18. [B5 (ENTRY)] dereference of x! [B4] x = nullptr x = nullptr x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) *x = 5 (after the call to foo ) flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr x = nullptr 10/38 x = nullptr [B2 (ENTRY)] int *x = 0; flag = 1; foo(); if (flag) [B3] x = new int; [B2] foo(); if (flag) [B1] *x = 5 [B0 (EXIT)] main: [B1] flag = coin(); flag = 1 x = nullptr [B2 (ENTRY)] [B0 (EXIT)] foo: flag = 1 x = nullptr (after the call to foo ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  19. [B5 (ENTRY)] x = (heap allocated object) [B4] x = nullptr x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined dereference of x! x = nullptr *x = 5 flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr x = nullptr 10/38 (after the call to foo ) [B3] [B1] *x = 5 [B0 (EXIT)] main: [B1] [B2] flag = coin(); [B1] x = new int; flag = coin(); [B2 (ENTRY)] [B0 (EXIT)] if (flag) foo(); foo: flag = 1 x = nullptr foo(); flag = 1 x = nullptr flag = 1; (after the call to foo ) int *x = 0; x = nullptr if (flag) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  20. [B5 (ENTRY)] x = (heap allocated object) [B4] x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined dereference of x! x = nullptr *x = 5 flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr x = nullptr x = nullptr (after the call to foo ) if (flag) flag = coin(); [B1] main: [B0 (EXIT)] *x = 5 [B1] foo(); [B0 (EXIT)] [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; [B2 (ENTRY)] 10/38 [B0 (EXIT)] flag = 1 x = nullptr x = nullptr flag = 1 x = nullptr (after the call to foo ) foo: flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  21. [B5 (ENTRY)] x = (heap allocated object) [B4] x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined dereference of x! x = nullptr *x = 5 flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr x = nullptr x = nullptr 10/38 foo(); [B1] main: [B0 (EXIT)] *x = 5 [B1] if (flag) [B2] [B0 (EXIT)] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; flag = coin(); [B2 (ENTRY)] foo: x = nullptr (after the call to foo ) x = nullptr x = nullptr flag = 1 (after the call to foo ) flag = 1 flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  22. [B5 (ENTRY)] *x = undefined x = nullptr [B4] x = nullptr x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) x = (heap allocated object) (after the call to foo ) dereference of x! *x = 5 flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr x = nullptr 10/38 x = nullptr [B3] [B2] foo(); if (flag) [B1] *x = 5 [B2] [B0 (EXIT)] main: x = new int; [B1] flag = coin(); [B2 (ENTRY)] if (flag) foo(); [B0 (EXIT)] foo: flag = 1 foo(); x = nullptr flag = 1 flag = 1; x = nullptr (after the call to foo ) int *x = 0; if (flag) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  23. [B5 (ENTRY)] dereference of x! x = nullptr [B4] x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) *x = 5 x = nullptr x = (heap allocated object) dereference of x! *x = 5 flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr x = nullptr 10/38 flag = coin(); flag = 1 *x = 5 [B0 (EXIT)] [B1] main: [B1] if (flag) foo(); [B2 (ENTRY)] [B0 (EXIT)] [B2] x = new int; foo: x = nullptr *x = 5 [B3] if (flag) flag = 1 x = nullptr (after the call to foo ) foo(); flag = 1; x = nullptr (after the call to foo ) int *x = 0; [B1] flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  24. [B5 (ENTRY)] *x = 5 x = nullptr [B4] *x = undefined x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) dereference of x! x = (heap allocated object) x = nullptr dereference of x! *x = 5 flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr x = nullptr x = (heap allocated object) 10/38 if (flag) [B1] main: [B0 (EXIT)] [B0 (EXIT)] *x = 5 [B1] foo(); [B0 (EXIT)] [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; flag = coin(); [B2 (ENTRY)] foo: x = nullptr (after the call to foo ) x = nullptr x = nullptr flag = 1 (after the call to foo ) flag = 1 flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  25. [B5 (ENTRY)] dereference of x! x = nullptr [B4] x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) *x = 5 x = nullptr x = (heap allocated object) dereference of x! *x = 5 flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr x = nullptr 10/38 foo(); [B2] if (flag) [B2] foo(); if (flag) [B1] *x = 5 x = new int; [B0 (EXIT)] main: [B1] flag = coin(); [B2 (ENTRY)] [B3] if (flag) [B0 (EXIT)] foo: x = nullptr (after the call to foo ) x = nullptr (after the call to foo ) flag = 1 int *x = 0; flag = 1 x = nullptr flag = 1; foo(); flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  26. [B5 (ENTRY)] dereference of x! x = (heap allocated object) [B4] x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) dereference of x! *x = 5 x = (heap allocated object) *x = 5 x = nullptr flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr x = nullptr *x = undefined 10/38 if (flag) flag = coin(); [B1] main: [B0 (EXIT)] [B0 (EXIT)] *x = 5 [B1] foo(); foo: [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; [B2 (ENTRY)] [B0 (EXIT)] flag = 1 x = nullptr x = nullptr flag = 1 (after the call to foo ) x = nullptr (after the call to foo ) x = nullptr flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  27. [B5 (ENTRY)] x = (heap allocated object) dereference of x! x = (heap allocated object) *x = undefined x = (heap allocated object) (after the call to foo ) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined x = (heap allocated object) [B4] x = nullptr *x = 5 dereference of x! x = nullptr x = nullptr x = nullptr flag = 0 dereference of x! x = nullptr x = nullptr (after the call to foo ) flag = 0 *x = 5 *x = undefined x = (heap allocated object) flag = 0 *x = undefined x = (heap allocated object) flag = 0 x = nullptr 10/38 [B1] int *x = 0; foo(); if (flag) [B1] *x = 5 [B0 (EXIT)] [B3] main: if (flag) flag = coin(); foo(); [B2 (ENTRY)] [B0 (EXIT)] flag = 1; foo: x = new int; flag = 1 x = nullptr [B4] if (flag) flag = 1 x = nullptr (after the call to foo ) foo(); flag = 1; x = nullptr (after the call to foo ) int *x = 0; [B2] flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  28. [B5 (ENTRY)] (after the call to foo ) x = (heap allocated object) *x = 5 dereference of x! x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined *x = 5 x = (heap allocated object) (after the call to foo ) *x = undefined x = (heap allocated object) [B4] x = (heap allocated object) dereference of x! flag = 0 x = nullptr x = nullptr x = nullptr flag = 0 dereference of x! x = nullptr x = nullptr (after the call to foo ) flag = 0 x = (heap allocated object) x = nullptr flag = 0 *x = undefined x = (heap allocated object) flag = 0 *x = undefined x = nullptr *x = undefined 10/38 x = nullptr foo(); if (flag) [B1] *x = 5 [B0 (EXIT)] main: [B1] flag = coin(); [B2 (ENTRY)] foo(); [B0 (EXIT)] foo: flag = 1 [B2] flag = 1 if (flag) x = new int; x = nullptr int *x = 0; flag = 1; (after the call to foo ) x = nullptr [B2] foo(); if (flag) (after the call to foo ) x = nullptr [B3] flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  29. [B5 (ENTRY)] *x = 5 *x = undefined [B4] *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined (after the call to foo ) x = (heap allocated object) *x = undefined x = (heap allocated object) dereference of x! *x = 5 x = (heap allocated object) dereference of x! flag = 0 x = nullptr x = nullptr x = nullptr flag = 0 dereference of x! x = nullptr x = nullptr (after the call to foo ) flag = 0 x = (heap allocated object) x = nullptr flag = 0 *x = undefined x = (heap allocated object) flag = 0 *x = undefined x = (heap allocated object) x = (heap allocated object) 10/38 if (flag) [B2 (ENTRY)] flag = coin(); [B1] main: [B0 (EXIT)] *x = 5 [B1] foo(); foo: [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; [B0 (EXIT)] flag = 1 x = nullptr (after the call to foo ) x = nullptr x = nullptr x = nullptr (after the call to foo ) flag = 1 x = nullptr flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  30. [B5 (ENTRY)] (after the call to foo ) x = (heap allocated object) *x = 5 dereference of x! x = (heap allocated object) *x = undefined x = (heap allocated object) *x = undefined *x = 5 x = (heap allocated object) (after the call to foo ) *x = undefined x = (heap allocated object) [B4] *x = undefined dereference of x! flag = 0 x = nullptr x = nullptr x = nullptr flag = 0 dereference of x! x = nullptr x = nullptr (after the call to foo ) flag = 0 x = (heap allocated object) x = nullptr flag = 0 *x = undefined x = (heap allocated object) flag = 0 *x = undefined x = (heap allocated object) 10/38 foo: x = new int; *x = 5 [B0 (EXIT)] main: [B1] flag = coin(); [B2 (ENTRY)] [B2 (ENTRY)] [B0 (EXIT)] foo(); [B2] flag = 1 x = nullptr flag = 1 x = nullptr if (flag) (after the call to foo ) [B3] if (flag) x = nullptr foo(); flag = 1; (after the call to foo ) x = nullptr int *x = 0; x = nullptr [B1] flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  31. [B5 (ENTRY)] x = (heap allocated object) dereference of x! x = (heap allocated object) *x = 5 dereference of x! x = (heap allocated object) *x = undefined (after the call to foo ) flag = 0 *x = undefined x = (heap allocated object) (after the call to foo ) [B4] x = (heap allocated object) *x = undefined *x = 5 x = (heap allocated object) x = nullptr x = nullptr x = nullptr flag = 0 dereference of x! x = nullptr x = nullptr (after the call to foo ) (after the call to foo ) *x = undefined x = nullptr flag = 0 x = nullptr flag = 0 *x = undefined x = (heap allocated object) flag = 0 x = (heap allocated object) *x = undefined 10/38 [B1] [B2 (ENTRY)] flag = coin(); [B1] flag = coin(); [B1] main: [B0 (EXIT)] *x = 5 if (flag) flag = 1 foo(); [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; foo: [B0 (EXIT)] x = nullptr (after the call to foo ) x = nullptr (after the call to foo ) x = nullptr x = nullptr flag = 1 x = nullptr flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  32. [B5 (ENTRY)] *x = undefined *x = 5 dereference of x! x = (heap allocated object) *x = 5 dereference of x! x = (heap allocated object) x = (heap allocated object) x = (heap allocated object) (after the call to foo ) *x = undefined x = (heap allocated object) (after the call to foo ) [B4] x = (heap allocated object) flag = 0 *x = undefined x = (heap allocated object) x = nullptr x = nullptr flag = 0 dereference of x! x = nullptr x = nullptr (after the call to foo ) (after the call to foo ) flag = 0 x = nullptr flag = 0 x = nullptr flag = 0 *x = undefined x = (heap allocated object) *x = undefined *x = undefined x = nullptr [B1] [B0 (EXIT)] [B0 (EXIT)] [B2 (ENTRY)] flag = coin(); [B1] main: [B0 (EXIT)] *x = 5 if (flag) x = nullptr foo(); [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; flag = 1 foo: 10/38 x = nullptr x = nullptr flag = 1 x = nullptr (after the call to foo ) (after the call to foo ) x = nullptr flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  33. [B5 (ENTRY)] *x = undefined *x = 5 dereference of x! x = (heap allocated object) *x = 5 dereference of x! x = (heap allocated object) x = (heap allocated object) x = (heap allocated object) (after the call to foo ) *x = undefined x = (heap allocated object) [B4] *x = undefined x = (heap allocated object) flag = 0 *x = undefined x = (heap allocated object) x = nullptr x = nullptr flag = 0 dereference of x! x = nullptr x = nullptr (after the call to foo ) (after the call to foo ) flag = 0 x = nullptr flag = 0 x = nullptr flag = 0 *x = undefined x = (heap allocated object) *x = undefined (after the call to foo ) 10/38 [B1] x = nullptr [B0 (EXIT)] [B2 (ENTRY)] flag = coin(); [B1] main: [B0 (EXIT)] *x = 5 if (flag) x = nullptr foo(); [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; flag = 1 foo: flag = 1 x = nullptr x = nullptr (after the call to foo ) x = nullptr x = nullptr (after the call to foo ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  34. [B5 (ENTRY)] x = (heap allocated object) dereference of x! x = (heap allocated object) *x = 5 dereference of x! x = (heap allocated object) *x = undefined (after the call to foo ) flag = 0 *x = undefined x = (heap allocated object) (after the call to foo ) *x = undefined [B4] *x = undefined *x = 5 x = (heap allocated object) x = nullptr x = nullptr x = nullptr flag = 0 dereference of x! x = nullptr x = nullptr (after the call to foo ) (after the call to foo ) *x = undefined x = nullptr flag = 0 x = nullptr flag = 0 *x = undefined x = (heap allocated object) flag = 0 x = (heap allocated object) x = (heap allocated object) 10/38 foo(); x = nullptr flag = coin(); [B1] main: [B0 (EXIT)] *x = 5 [B1] if (flag) [B2] foo: if (flag) foo(); [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; [B0 (EXIT)] [B2 (ENTRY)] x = nullptr x = nullptr flag = 1 x = nullptr (after the call to foo ) (after the call to foo ) x = nullptr flag = 1 flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  35. [B5 (ENTRY)] x = (heap allocated object) flag = 0 *x = 5 dereference of x! x = (heap allocated object) *x = 5 dereference of x! *x = undefined *x = undefined x = (heap allocated object) (after the call to foo ) *x = undefined x = (heap allocated object) [B4] *x = undefined x = (heap allocated object) x = (heap allocated object) flag = 0 x = (heap allocated object) x = nullptr x = nullptr flag = 0 dereference of x! x = nullptr dereference of x! x = nullptr (after the call to foo ) x = (heap allocated object) x = nullptr (after the call to foo ) x = nullptr flag = 0 x = nullptr flag = 0 *x = undefined *x = undefined (after the call to foo ) 10/38 *x = 5 x = nullptr [B2 (ENTRY)] flag = coin(); [B1] main: [B0 (EXIT)] *x = 5 [B1] [B1] flag = 1 if (flag) foo(); [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; foo: [B0 (EXIT)] x = nullptr x = nullptr x = nullptr x = nullptr (after the call to foo ) x = nullptr flag = 1 (after the call to foo ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  36. [B5 (ENTRY)] x = (heap allocated object) flag = 0 *x = 5 dereference of x! x = (heap allocated object) *x = 5 dereference of x! *x = undefined *x = undefined x = (heap allocated object) (after the call to foo ) *x = undefined x = (heap allocated object) [B4] *x = undefined x = (heap allocated object) x = (heap allocated object) flag = 0 x = (heap allocated object) x = nullptr x = nullptr flag = 0 dereference of x! x = nullptr dereference of x! x = nullptr (after the call to foo ) x = (heap allocated object) x = nullptr (after the call to foo ) x = nullptr flag = 0 x = nullptr flag = 0 *x = undefined *x = undefined (after the call to foo ) x = nullptr foo(); [B0 (EXIT)] flag = coin(); [B1] main: [B0 (EXIT)] *x = 5 [B1] if (flag) [B2] flag = 1 if (flag) foo(); [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; foo: [B2 (ENTRY)] x = nullptr x = nullptr x = nullptr x = nullptr (after the call to foo ) 10/38 flag = 1 (after the call to foo ) x = nullptr flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  37. [B5 (ENTRY)] dereference of x! x = (heap allocated object) flag = 0 *x = 5 dereference of x! x = (heap allocated object) *x = 5 x = (heap allocated object) flag = 0 *x = undefined x = (heap allocated object) (after the call to foo ) *x = undefined x = (heap allocated object) [B4] (after the call to foo ) *x = undefined x = (heap allocated object) x = (heap allocated object) x = nullptr x = nullptr flag = 0 x = nullptr flag = 0 dereference of x! x = nullptr dereference of x! x = nullptr *x = undefined (after the call to foo ) x = nullptr (after the call to foo ) x = nullptr flag = 0 x = nullptr flag = 0 *x = undefined 10/38 *x = undefined [B1] [B0 (EXIT)] [B2 (ENTRY)] flag = coin(); [B1] main: [B0 (EXIT)] [B0 (EXIT)] *x = 5 if (flag) x = nullptr foo(); [B2] x = new int; [B3] if (flag) foo(); flag = 1; int *x = 0; flag = 1 foo: flag = 1 x = nullptr x = nullptr x = nullptr x = nullptr x = nullptr (after the call to foo ) (after the call to foo ) x = (heap allocated object) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  38. There is always more to talk about... Branches in the ExplodedGraph may happen far more often Representation of values, regions: symbols ExplodedGraphs are usually very-very large, and contain tremendous amount of information 11/38

  39. There is always more to talk about... Branches in the ExplodedGraph may happen far more often Representation of values, regions: symbols ExplodedGraphs are usually very-very large, and contain tremendous amount of information 11/38

  40. There is always more to talk about... Branches in the ExplodedGraph may happen far more often Representation of values, regions: symbols ExplodedGraphs are usually very-very large, and contain tremendous amount of information 11/38

  41. Bug report construction

  42. Processing of the ExplodedGraph Bugs are represented with error nodes The graph may contain several of them The goal is to explain the path to these nodes For each node, construct the shortest path from the root to the error node This is called a bug path 12/38

  43. Processing of the ExplodedGraph Bugs are represented with error nodes The graph may contain several of them The goal is to explain the path to these nodes For each node, construct the shortest path from the root to the error node This is called a bug path 12/38

  44. Processing of the ExplodedGraph Bugs are represented with error nodes The graph may contain several of them The goal is to explain the path to these nodes For each node, construct the shortest path from the root to the error node This is called a bug path 12/38

  45. flag = 1 x = (heap allocated object) dereference of x! *x = 5 x = (heap allocated object) dereference of x! *x = 5 flag = 0 x = (heap allocated object) *x = undefined flag = 0 *x = undefined *x = undefined flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! flag = 0 x = nullptr flag = 0 x = nullptr x = nullptr x = (heap allocated object) x = (heap allocated object) (after the call to foo ) (after the call to foo ) x = nullptr x = nullptr x = nullptr x = (heap allocated object) x = (heap allocated object) *x = undefined *x = undefined 13/38 x = (heap allocated object) *x = undefined (after the call to foo ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  46. flag = 1 x = nullptr x = nullptr *x = 5 x = (heap allocated object) dereference of x! *x = 5 flag = 0 x = (heap allocated object) *x = undefined flag = 0 x = (heap allocated object) *x = undefined flag = 0 flag = 0 *x = undefined x = nullptr (after the call to foo ) x = nullptr (after the call to foo ) x = nullptr x = nullptr dereference of x! x = nullptr dereference of x! flag = 0 x = nullptr flag = 0 x = nullptr x = (heap allocated object) dereference of x! x = (heap allocated object) x = (heap allocated object) flag = 1 x = nullptr (after the call to foo ) x = nullptr (after the call to foo ) x = nullptr x = nullptr x = (heap allocated object) *x = undefined x = nullptr *x = undefined x = (heap allocated object) (after the call to foo ) *x = undefined 13/38 (after the call to foo ) ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  47. flag = 1 11 04 void foo() { 05 flag = coin(); 06 } 07 08 int main() { 09 int *x = 0; 10 flag = 1; foo(); bool coin(); 12 if (flag) 13 x = new int; 14 foo(); 15 16 if (flag) 17 *x = 5; 18 } 03 02 x = nullptr int flag; flag = 1 x = nullptr (after the call to foo ) x = nullptr (after the call to foo ) x = nullptr flag = 0 x = nullptr flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr 01 dereference of x! x = nullptr dereference of x! 14/38 x = nullptr (after the call to foo ) ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  48. The ideal bug report The goal is to generate a bug report from the bug path that is complete: contains every information necessary to understand how the bug occured minimal: contains no unnecessary information 15/38

  49. Techniques used by the analyzer 2 techniques: BugReporterVisitors Interestingness propagation Visit the nodes of the bugpath from the error node to the root 16/38

  50. Techniques used by the analyzer 2 techniques: BugReporterVisitors Interestingness propagation Visit the nodes of the bugpath from the error node to the root 16/38

  51. Techniques used by the analyzer 2 techniques: BugReporterVisitors Interestingness propagation Visit the nodes of the bugpath from the error node to the root 16/38

  52. flag = 1 flag = 0 dereference of x! x = nullptr dereference of x! x = nullptr x = nullptr (after the call to foo ) x = nullptr x = nullptr x = nullptr (after the call to foo ) x = nullptr flag = 0 (after the call to foo ) x = nullptr (after the call to foo ) 17/38 x = nullptr ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  53. flag = 1 flag = 0 flag = 1 x = nullptr (after the call to foo ) flag x = nullptr (after the call to foo ) flag x = nullptr flag = 0 x = nullptr x = nullptr x = nullptr (after the call to foo ) flag x = nullptr (after the call to foo ) flag x = nullptr x = nullptr dereference of x! x = nullptr dereference of x! <warning msg> <warning msg> dereference of x! x = nullptr (after the call to foo ) (after the call to foo ) x = nullptr (after the call to foo ) x = nullptr flag = 0 x = nullptr x = nullptr flag = 0 17/38 x = nullptr x = nullptr (after the call to foo ) dereference of x! x = nullptr ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  54. flag = 1 x = nullptr (after the call to foo ) flag x = nullptr (after the call to foo ) flag x = nullptr flag = 0 x = nullptr flag = 0 x = nullptr (after the call to foo ) (after the call to foo ) flag = 1 x = nullptr flag 0 0 x = nullptr dereference of x! flag 0 0 x = nullptr dereference of x! <diagnostic msg 4> x = nullptr x = nullptr <diagnostic msg 4> dereference of x! (after the call to foo ) x = nullptr (after the call to foo ) x = nullptr flag = 0 x = nullptr flag = 0 x = nullptr (after the call to foo ) x = nullptr x = nullptr x = nullptr dereference of x! x = nullptr (after the call to foo ) 17/38 ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  55. flag = 1 (after the call to foo ) flag x = nullptr (after the call to foo ) flag x = nullptr flag = 0 x = nullptr flag = 0 x = nullptr (after the call to foo ) flag x = nullptr flag x = nullptr x = nullptr flag 0 0 x = nullptr dereference of x! flag 0 0 x = nullptr dereference of x! <diagnostic msg 3> (after the call to foo ) flag = 1 x = nullptr <diagnostic msg 3> (after the call to foo ) x = nullptr (after the call to foo ) x = nullptr flag = 0 x = nullptr flag = 0 x = nullptr (after the call to foo ) 17/38 x = nullptr x = nullptr dereference of x! x = nullptr dereference of x! (after the call to foo ) x = nullptr ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  56. flag = 1 (after the call to foo ) (after the call to foo ) x = nullptr (after the call to foo ) x = nullptr flag = 0 x = nullptr flag = 0 x = nullptr (after the call to foo ) flag x = nullptr flag flag = 1 x = nullptr flag 0 0 x = nullptr dereference of x! flag 0 0 x = nullptr dereference of x! <diagnostic msg 2> x = nullptr x = nullptr <diagnostic msg 2> (after the call to foo ) (after the call to foo ) x = nullptr (after the call to foo ) x = nullptr flag = 0 x = nullptr flag = 0 x = nullptr (after the call to foo ) dereference of x! x = nullptr 17/38 x = nullptr x = nullptr x = nullptr dereference of x! ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  57. flag = 1 (after the call to foo ) flag x = nullptr (after the call to foo ) flag x = nullptr flag = 0 x = nullptr flag = 0 x = nullptr (after the call to foo ) flag x = nullptr flag x = nullptr x = nullptr flag 0 0 x = nullptr dereference of x! flag 0 0 x = nullptr dereference of x! <diagnostic msg 1> (after the call to foo ) flag = 1 x = nullptr <diagnostic msg 1> (after the call to foo ) x = nullptr (after the call to foo ) x = nullptr flag = 0 x = nullptr flag = 0 x = nullptr (after the call to foo ) 17/38 x = nullptr x = nullptr dereference of x! x = nullptr dereference of x! (after the call to foo ) x = nullptr ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ ( −∞ , ∞ ) ( −∞ , ∞ ) flag ∈ flag ∈ flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ ) flag ∈ ( −∞ , 0 ) ∪ ( 0 , ∞ )

  58. BugReporterVisitors An arbitrary number of visitors can be registered Their visitNode() is called on each node visit Visitors may create diagnostic messages about the node they are currently visiting Despite the misleading name, they are more like callbacks than visitors 18/38

  59. BugReporterVisitors An arbitrary number of visitors can be registered Their visitNode() is called on each node visit Visitors may create diagnostic messages about the node they are currently visiting Despite the misleading name, they are more like callbacks than visitors 18/38

  60. BugReporterVisitors An arbitrary number of visitors can be registered Their visitNode() is called on each node visit Visitors may create diagnostic messages about the node they are currently visiting Despite the misleading name, they are more like callbacks than visitors 18/38

  61. BugReporterVisitors An arbitrary number of visitors can be registered Their visitNode() is called on each node visit Visitors may create diagnostic messages about the node they are currently visiting Despite the misleading name, they are more like callbacks than visitors 18/38

  62. Visitors ConditionBRVisitor : Describes conditions of if branches, loops, conditional operators etc. FindLastStoreBRVisitor : Finds the last store to a given variable TrackControlDependencyCondBRVisitor 19/38

  63. Visitors ConditionBRVisitor : Describes conditions of if branches, loops, conditional operators etc. FindLastStoreBRVisitor : Finds the last store to a given variable TrackControlDependencyCondBRVisitor 19/38

  64. Visitors ConditionBRVisitor : Describes conditions of if branches, loops, conditional operators etc. FindLastStoreBRVisitor : Finds the last store to a given variable TrackControlDependencyCondBRVisitor 19/38

  65. TrackControlDependencyCondBRVisitor Most recent addition, available in Clang 10.0.0 GSoC’19 project mentored by Artem Dergachev, Gábor Horváth and Ádám Balogh https://szelethus.github.io/gsoc2019/ Calculates control dependencies to points of interest Tells the analyzer to explain the conditions of control dependency blocks 20/38

  66. TrackControlDependencyCondBRVisitor Most recent addition, available in Clang 10.0.0 GSoC’19 project mentored by Artem Dergachev, Gábor Horváth and Ádám Balogh https://szelethus.github.io/gsoc2019/ Calculates control dependencies to points of interest Tells the analyzer to explain the conditions of control dependency blocks 20/38

  67. TrackControlDependencyCondBRVisitor Most recent addition, available in Clang 10.0.0 GSoC’19 project mentored by Artem Dergachev, Gábor Horváth and Ádám Balogh https://szelethus.github.io/gsoc2019/ Calculates control dependencies to points of interest Tells the analyzer to explain the conditions of control dependency blocks 20/38

  68. [B5 (ENTRY)] foo(); int *x = 0; 10 flag = 1; 11 foo(); 12 if (flag) 13 x = new int; 14 15 int main() { 16 if (flag) 17 *x = 5; 18 } foo: [B2 (ENTRY)] [B1] flag = coin(); [B0 (EXIT)] 09 08 [B4] [B0 (EXIT)] int *x = 0; flag = 1; foo(); if (flag) [B3] x = new int; [B2] foo(); if (flag) [B1] *x = 5 main: 07 01 int flag; 02 bool coin(); 03 04 void foo() { 05 flag = coin(); 06 } 21/38

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend