gm2
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

more progress with -fanalyzer


From: Gaius Mulley
Subject: more progress with -fanalyzer
Date: Fri, 23 Apr 2021 20:42:02 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux)

Hello,

Here are the results of the latest changes to the GCC analyzer working
with gm2.  I've also fixed a number of token location bugs and improved
the subexpression tokens in gm2.  The analyzer has been extended to:

    * understand the M2RTS exception handler:
      NoReturnException and will complain if a procedure function can reach
      this call.  (The user needs to supply both -fsoft-check-all
      -fanalyzer).  The compiler will point out indeterminate return
      values and give a reasoning as to how that situation could come about.
    * understand Storage.def:ALLOCATE/DEALLOCATE,
      SysStorage.def:ALLOCATE/DEALLOCATE, libc.def:free/malloc are
      different and will complain if memory allocated from one heap is
      return to the other.

Anyway a run of the regression tests is given below:

I'll tidy up the code and fix some more token location bugs.  I'd also
like to make the analyzer understand DEALLOCATE changes the first
argument to NIL upon exit.  I've not yet git pushed the code -
hopefully this will occur in the next couple of days.

Here is the test run, enjoy:

 
$ cat badlistfree2.mod
MODULE badlistfree2 ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;

TYPE
   list = POINTER TO RECORD
                        value: CARDINAL ;
                        next : list ;
                     END ;
VAR
   head: list ;

PROCEDURE badfree (l: list) ;
BEGIN
   DISPOSE (l) ;
   WHILE l^.next # NIL DO  (* { dg-warning "use after 'DISPOSE via 
Storage.DEALLOCATE' of 'l'.*" }  *)
      l := l^.next ;
      DISPOSE (l)
   END
END badfree ;


BEGIN
   badfree (head)
END badlistfree2. 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g badlistfree2.mod
badlistfree2.mod: In function ‘badfree’:
badlistfree2.mod:19:18: warning: use after ‘DISPOSE via Storage.DEALLOCATE’ of 
‘l’ [CWE-416] [-Wanalyzer-use-after-free]
   19 |    WHILE l^.next # NIL DO  (* { dg-warning "use after 'DISPOSE via 
Storage.DEALLOCATE' of 'l'.*" }  *)
      |          ~~~~~~~~^~~~~
  ‘badfree’: events 1-2
    |
    |   18 |    DISPOSE (l) ;
    |      |    ^~~~~~~~~~
    |      |    |
    |      |    (1) deallocated here
    |   19 |    WHILE l^.next # NIL DO  (* { dg-warning "use after 'DISPOSE via 
Storage.DEALLOCATE' of 'l'.*" }  *)
    |      |          ~~~~~~~~~~~~~
    |      |                  |
    |      |                  (2) use after ‘DISPOSE via Storage.DEALLOCATE’ of 
‘l’; deallocated at (1)
    |
 
$ cat badlistfree.mod
MODULE badlistfree ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;

TYPE
   list = POINTER TO RECORD
                        value: CARDINAL ;
                        next : list ;
                     END ;
VAR
   head: list ;

PROCEDURE badfree (l: list) ;
BEGIN
   DISPOSE (l) ;
   WHILE l^.next # NIL DO  (* { dg-warning "use after 'DISPOSE via 
Storage.DEALLOCATE' of 'l'.*" }  *)
      l := l^.next ;
      DISPOSE (l)
   END
END badfree ;


BEGIN
   NEW (head) ;
   badfree (head) ;
END badlistfree. 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g badlistfree.mod
badlistfree.mod: In function ‘badfree’:
badlistfree.mod:19:18: warning: use after ‘DISPOSE via Storage.DEALLOCATE’ of 
‘l’ [CWE-416] [-Wanalyzer-use-after-free]
   19 |    WHILE l^.next # NIL DO  (* { dg-warning "use after 'DISPOSE via 
Storage.DEALLOCATE' of 'l'.*" }  *)
      |          ~~~~~~~~^~~~~
  ‘badfree’: events 1-2
    |
    |   18 |    DISPOSE (l) ;
    |      |    ^~~~~~~~~~
    |      |    |
    |      |    (1) deallocated here
    |   19 |    WHILE l^.next # NIL DO  (* { dg-warning "use after 'DISPOSE via 
Storage.DEALLOCATE' of 'l'.*" }  *)
    |      |          ~~~~~~~~~~~~~
    |      |                  |
    |      |                  (2) use after ‘DISPOSE via Storage.DEALLOCATE’ of 
‘l’; deallocated at (1)
    |
badlistfree.mod:20:9: warning: dereference of NULL ‘l’ [CWE-476] 
[-Wanalyzer-null-dereference]
   20 |       l := l^.next ;
      |         ^~
  ‘_M2_badlistfree_init’: events 1-3
    |
    |   26 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (1) entry to ‘_M2_badlistfree_init’
    |   27 |    NEW (head) ;
    |      |    ~~~~~~~~~
    |      |    |
    |      |    (2) allocated here
    |   28 |    badfree (head) ;
    |      |    ~~~~~~~
    |      |    |
    |      |    (3) calling ‘badfree’ from ‘_M2_badlistfree_init’
    |
    +--> ‘badfree’: events 4-15
           |
           |   17 | BEGIN
           |      | ^~~~~
           |      | |
           |      | (4) entry to ‘badfree’
           |   18 |    DISPOSE (l) ;
           |      |    ~~~~~~~~~~
           |      |    |
           |      |    (5) state of ‘INIT_VAL(head)’: ‘unchecked’ -> ‘freed’ 
(NULL origin)
           |   19 |    WHILE l^.next # NIL DO  (* { dg-warning "use after 
'DISPOSE via Storage.DEALLOCATE' of 'l'.*" }  *)
           |      |          ~~~~~~~~~~~~~
           |      |           |      |
           |      |           |      (8) following ‘false’ branch...
           |      |           |      (9) ...to here
           |      |           |      (10) ‘l’ is NULL
           |      |           |      (11) following ‘false’ branch...
           |      |           (6) following ‘true’ branch...
           |      |           (7) ...to here
           |   20 |       l := l^.next ;
           |      |         ~~  ~
           |      |         |   |
           |      |         |   (12) ...to here
           |      |         |   (13) following ‘true’ branch...
           |      |         |   (14) ...to here
           |      |         (15) dereference of NULL ‘l’
           |
 
$ cat callbyref2.mod
MODULE callbyref2 ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
FROM SYSTEM IMPORT ADR ;
FROM libc IMPORT printf ;

TYPE
   ptrProc = POINTER TO PROCEDURE (CARDINAL) ;


PROCEDURE foo (c: CARDINAL) ;
BEGIN
   printf ("yes\n")
END foo ;


PROCEDURE setup () : ptrProc ;
VAR
   p: ptrProc ;
BEGIN
   NEW (p) ;
   p^ := foo ;  (* { dg-warning "dereference of possibly-NULL 'p'.*" }  *)
   RETURN p
END setup ;


VAR
   p: ptrProc ;
BEGIN
   p := setup () ;
   p^ (1)
END callbyref2.
 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g callbyref2.mod
callbyref2.mod: In function ‘setup’:
callbyref2.mod:25:7: warning: dereference of NULL ‘p’ [CWE-476] 
[-Wanalyzer-null-dereference]
   25 |    p^ := foo ;  (* { dg-warning "dereference of possibly-NULL 'p'.*" }  
*)
      |       ^~
  ‘setup’: events 1-5
    |
    |   24 |    NEW (p) ;
    |      |    ^~~~~~
    |      |    |
    |      |    (1) allocated here
    |   25 |    p^ := foo ;  (* { dg-warning "dereference of possibly-NULL 
'p'.*" }  *)
    |      |     ~ ~~
    |      |     | |
    |      |     | (4) ...to here
    |      |     | (5) dereference of NULL ‘p’
    |      |     (2) assuming ‘p’ is NULL
    |      |     (3) following ‘true’ branch...
    |
 
$ cat callbyref3badreturn.mod
MODULE callbyref3badreturn ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
FROM SYSTEM IMPORT ADR ;
FROM libc IMPORT printf ;

TYPE
   ptrProc = POINTER TO PROCEDURE (CARDINAL) ;


PROCEDURE foo (c: CARDINAL) ;
BEGIN
   printf ("yes\n")
END foo ;


PROCEDURE setup () : ptrProc ;
VAR
   p: ptrProc ;
BEGIN
   NEW (p) ;
   IF p # NIL
   THEN
      p^ := foo ;  (* { dg-warning "dereference of possibly-NULL 'p'.*" }  *)
      RETURN p
   END
END setup ;


VAR
   p: ptrProc ;
BEGIN
   p := setup () ;
   p^ (1)
END callbyref3badreturn.
 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g callbyref3badreturn.mod
callbyref3badreturn.mod: In function ‘setup’:
callbyref3badreturn.mod:36:9: warning: call to the procedure function ‘setup’ 
may not return a result and therefore the value of ‘setup ()’ maybe 
indeterminate [-fanalyzer]
   36 |    p := setup () ;
      |         ^~~~~
  ‘_M2_callbyref3badreturn_init’: events 1-2
    |
    |   35 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (1) entry to ‘_M2_callbyref3badreturn_init’
    |   36 |    p := setup () ;
    |      |         ~~~~~
    |      |         |
    |      |         (2) later on, when the indeterminate value is returned
    |
  ‘setup’: events 3-6
    |
    |   23 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (3) entry to ‘setup’
    |   24 |    NEW (p) ;
    |   25 |    IF p # NIL
    |      |       ~~~~~~~
    |      |         |
    |      |         (4) following ‘true’ branch...
    |......
    |   29 |    END
    |      |    ~~~
    |      |    |
    |      |    (5) ...to here
    |......
    |   36 |    p := setup () ;
    |      |         ~~~~~
    |      |         |
    |      |         (6) the procedure function might return an indeterminate 
value (procedure function ‘setup’ is missing a return statement)
    |
 
$ cat callbyref.mod
MODULE callbyref ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
FROM SYSTEM IMPORT ADR ;
FROM libc IMPORT printf ;

TYPE
   ptrProc = POINTER TO PROCEDURE (CARDINAL) ;


PROCEDURE foo (c: CARDINAL) ;
BEGIN
   printf ("yes\n")
END foo ;


PROCEDURE setup () : ptrProc ;
BEGIN
   RETURN NIL
END setup ;


VAR
   p: ptrProc ;
BEGIN
   p := setup () ;
   p^ (1)   (* { dg-warning "dereference of NULL '0B'.*" }  *)
END callbyref.
 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g callbyref.mod
 
$ cat calldesptr.mod
MODULE calldesptr ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM SYSTEM IMPORT ADR ;
FROM libc IMPORT printf ;
FROM Storage IMPORT ALLOCATE ;

PROCEDURE foo ;
BEGIN
   printf ("hello world\n")
END foo ;

TYPE
   ProcPtr = POINTER TO PROCEDURE ;
VAR
   p: ProcPtr ;
BEGIN
   (* the following commented code is blocked by the Modula-2 grammar
      as a designator cannot call a procedure function.
      VAL (ProcPtr, ADR (foo))^ ()   *)
   NEW (p);
   p^ := foo ;  (* { dg-warning "dereference of possibly-NULL 'p'.*" }  *)
   p^
END calldesptr.
 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g calldesptr.mod
 
$ cat disposenoalloc.mod
MODULE disposenoalloc ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
FROM SYSTEM IMPORT ADR ;

TYPE
   list = POINTER TO RECORD
                        value: CARDINAL ;
                        next : list ;
                     END ;
VAR
   head: list ;

BEGIN
   head := ADR (head) ;
   DISPOSE (head)  (* { dg-warning "'DISPOSE via Storage.DEALLOCATE' of 'head' 
which points to memory not on the heap.*" }  *)
END disposenoalloc.
 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g disposenoalloc.mod
disposenoalloc.mod: In function ‘_M2_disposenoalloc_init’:
disposenoalloc.mod:19:4: warning: ‘DISPOSE via Storage.DEALLOCATE’ of ‘head’ 
which points to memory not on the heap [CWE-590] [-Wanalyzer-free-of-non-heap]
   19 |    DISPOSE (head)  (* { dg-warning "'DISPOSE via Storage.DEALLOCATE' of 
'head' which points to memory not on the heap.*" }  *)
      |    ^~~~~~~~~~~~~
  ‘_M2_disposenoalloc_init’: events 1-3
    |
    |   18 |    head := ADR (head) ;
    |      |            ^~~~~~~~~
    |      |            |
    |      |            (1) pointer is from here
    |   19 |    DISPOSE (head)  (* { dg-warning "'DISPOSE via 
Storage.DEALLOCATE' of 'head' which points to memory not on the heap.*" }  *)
    |      |    ~~~~~~~~~~~~~
    |      |    |        |
    |      |    |        (2) pointer is from here
    |      |    (3) call to ‘DISPOSE via Storage.DEALLOCATE’ here
    |
 
$ cat globalnilderef.mod
MODULE globalnilderef ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM libc IMPORT printf ;

TYPE
   List = POINTER TO RECORD
                        value: CARDINAL ;
                        next : List ;
                     END ;

VAR
   l: List ;
BEGIN
   l^.value := 1 ;   (* { dg-warning "runtime error will occur, if this pointer 
value 'l' is ever dereferenced it will cause an exception.*" }  *)
   printf ("value is: %d\n", l^.value)   (* { dg-error "runtime error will 
occur, if this pointer value 'l' is ever dereferenced it will cause an 
exception.*" }  *)
END globalnilderef. 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g globalnilderef.mod
 
$ cat localnilderef.mod
MODULE localnilderef ;

(* { dg-options "-fanalyzer -fauto-init" }  *)
(* { dg-do compile }  *)

FROM libc IMPORT printf ;

TYPE
   List = POINTER TO RECORD
                        value: CARDINAL ;
                        next : List ;
                     END ;

PROCEDURE test ;
VAR
   l: List ;
BEGIN
   l^.value := 1 ;    (* { dg-warning "dereference of NULL.*" }  *)
   printf ("value is: %d\n", l^.value)
END test ;

BEGIN
   test
END localnilderef. 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g localnilderef.mod
 
$ cat mismatchedalloca.mod
MODULE mismatchedalloca ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Builtins IMPORT alloca ;
FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
FROM SYSTEM IMPORT SIZE ;

TYPE
   List = POINTER TO RECORD
                        next : List ;
                        value: CARDINAL ;
                     END ;


VAR
   head: List ;
BEGIN
   head := alloca (SIZE (head^)) ;
   DISPOSE (head)  (* { dg-warning "'DISPOSE via Storage.DEALLOCATE' of 'head' 
which points to memory not on the heap.*" }  *)
END mismatchedalloca.
 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g mismatchedalloca.mod
mismatchedalloca.mod: In function ‘_M2_mismatchedalloca_init’:
mismatchedalloca.mod:21:4: warning: ‘DISPOSE via Storage.DEALLOCATE’ of ‘head’ 
which points to memory not on the heap [CWE-590] [-Wanalyzer-free-of-non-heap]
   21 |    DISPOSE (head)  (* { dg-warning "'DISPOSE via Storage.DEALLOCATE' of 
'head' which points to memory not on the heap.*" }  *)
      |    ^~~~~~~~~~~~~
  ‘_M2_mismatchedalloca_init’: event 1
    |
    
|/home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/11.0.1/m2/m2pim/wrapc.def:125:1:
    |
  ‘_M2_mismatchedalloca_init’: event 2
    |
    |mismatchedalloca.mod:21:4:
    |   21 |    DISPOSE (head)  (* { dg-warning "'DISPOSE via 
Storage.DEALLOCATE' of 'head' which points to memory not on the heap.*" }  *)
    |      |    ^~~~~~~~~~~~~
    |      |    |
    |      |    (2) call to ‘DISPOSE via Storage.DEALLOCATE’ here
    |
 
$ cat mismatchedheap2.mod
MODULE mismatchedheap2 ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM SysStorage IMPORT ALLOCATE, DEALLOCATE ;
FROM libc IMPORT free ;

TYPE
   List = POINTER TO RECORD
                        next : List ;
                        value: CARDINAL ;
                     END ;

VAR
   head: List ;
BEGIN
   NEW (head) ;
   free (head)  (* { dg-warning "'head' should have been deallocated with 
'DISPOSE via SysStorage.DEALLOCATE' but was deallocated with 'free'.*" }  *)
END mismatchedheap2.
 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g mismatchedheap2.mod
mismatchedheap2.mod: In function ‘_M2_mismatchedheap2_init’:
mismatchedheap2.mod:19:4: warning: ‘head’ should have been deallocated with 
‘DISPOSE via SysStorage.DEALLOCATE’ but was deallocated with ‘free’ [CWE-762] 
[-Wanalyzer-mismatching-deallocation]
   19 |    free (head)  (* { dg-warning "'head' should have been deallocated 
with 'DISPOSE via SysStorage.DEALLOCATE' but was deallocated with 'free'.*" }  
*)
      |    ^~~~
  ‘_M2_mismatchedheap2_init’: events 1-2
    |
    |   18 |    NEW (head) ;
    |      |    ^~~~~~~~~
    |      |    |
    |      |    (1) allocated here (expects deallocation with ‘DISPOSE via 
SysStorage.DEALLOCATE’)
    |   19 |    free (head)  (* { dg-warning "'head' should have been 
deallocated with 'DISPOSE via SysStorage.DEALLOCATE' but was deallocated with 
'free'.*" }  *)
    |      |    ~~~~
    |      |    |
    |      |    (2) deallocated with ‘free’ here; allocation at (1) expects 
deallocation with ‘DISPOSE via SysStorage.DEALLOCATE’
    |
 
$ cat mismatchedheap.mod
MODULE mismatchedheap ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM SysStorage IMPORT ALLOCATE, DEALLOCATE ;

TYPE
   List = POINTER TO RECORD
                        next : List ;
                        value: CARDINAL ;
                     END ;

   MODULE user ;

   FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
   IMPORT List ;
   EXPORT userProc ;

   PROCEDURE userProc (l: List) ;
   BEGIN
      DISPOSE (l)  (* { dg-warning "'l' should have been deallocated with 
'DISPOSE via SysStorage.DEALLOCATE' but was deallocated with 'DISPOSE via 
Storage.DEALLOCATE'.*" }  *)
   END userProc ;

   END user ;

VAR
   head: List ;
BEGIN
   NEW (head) ;
   userProc (head)
END mismatchedheap. 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g mismatchedheap.mod
mismatchedheap.mod: In function ‘user_mismatchedheap_userProc’:
mismatchedheap.mod:22:7: warning: ‘l’ should have been deallocated with 
‘DISPOSE via SysStorage.DEALLOCATE’ but was deallocated with ‘DISPOSE via 
Storage.DEALLOCATE’ [CWE-762] [-Wanalyzer-mismatching-deallocation]
   22 |       DISPOSE (l)  (* { dg-warning "'l' should have been deallocated 
with 'DISPOSE via SysStorage.DEALLOCATE' but was deallocated with 'DISPOSE via 
Storage.DEALLOCATE'.*" }  *)
      |       ^~~~~~~~~~
  ‘_M2_mismatchedheap_init’: events 1-3
    |
    |   29 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (1) entry to ‘_M2_mismatchedheap_init’
    |   30 |    NEW (head) ;
    |      |    ~~~~~~~~~
    |      |    |
    |      |    (2) allocated here (expects deallocation with ‘DISPOSE via 
SysStorage.DEALLOCATE’)
    |   31 |    userProc (head)
    |      |    ~~~~~~~~
    |      |    |
    |      |    (3) calling ‘user_mismatchedheap_userProc’ from 
‘_M2_mismatchedheap_init’
    |
    +--> ‘user_mismatchedheap_userProc’: events 4-5
           |
           |   21 |    BEGIN
           |      |    ^~~~~
           |      |    |
           |      |    (4) entry to ‘user_mismatchedheap_userProc’
           |   22 |       DISPOSE (l)  (* { dg-warning "'l' should have been 
deallocated with 'DISPOSE via SysStorage.DEALLOCATE' but was deallocated with 
'DISPOSE via Storage.DEALLOCATE'.*" }  *)
           |      |       ~~~~~~~~~~
           |      |       |
           |      |       (5) deallocated with ‘DISPOSE via Storage.DEALLOCATE’ 
here; allocation at (2) expects deallocation with ‘DISPOSE via 
SysStorage.DEALLOCATE’
           |
 
$ cat testdoubledispose2.mod
MODULE testdoubledispose2 ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;

TYPE
   list = POINTER TO RECORD
                        value: CARDINAL ;
                        next : list ;
                     END ;
VAR
   head: list ;
BEGIN
   DISPOSE (head) ;
   DISPOSE (head) ;  (* { dg-warning "double-'DISPOSE via Storage.DEALLOCATE' 
of 'head'.*" }  *)
END testdoubledispose2. 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g testdoubledispose2.mod
testdoubledispose2.mod: In function ‘_M2_testdoubledispose2_init’:
testdoubledispose2.mod:17:4: warning: double-‘DISPOSE via Storage.DEALLOCATE’ 
of ‘head’ [CWE-415] [-Wanalyzer-double-free]
   17 |    DISPOSE (head) ;  (* { dg-warning "double-'DISPOSE via 
Storage.DEALLOCATE' of 'head'.*" }  *)
      |    ^~~~~~~~~~~~~
  ‘_M2_testdoubledispose2_init’: events 1-2
    |
    |   16 |    DISPOSE (head) ;
    |      |    ^~~~~~~~~~~~~
    |      |    |
    |      |    (1) first ‘DISPOSE via Storage.DEALLOCATE’ here
    |   17 |    DISPOSE (head) ;  (* { dg-warning "double-'DISPOSE via 
Storage.DEALLOCATE' of 'head'.*" }  *)
    |      |    ~~~~~~~~~~~~~
    |      |    |
    |      |    (2) second ‘DISPOSE via Storage.DEALLOCATE’ here; first 
‘DISPOSE via Storage.DEALLOCATE’ was at (1)
    |
 
$ cat testdoubledispose3.mod
MODULE testdoubledispose3 ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;

TYPE
   list = POINTER TO RECORD
                        value: CARDINAL ;
                        next : list ;
                     END ;
VAR
   head: list ;
BEGIN
   NEW (head) ;
   NEW (head) ;
   DISPOSE (head) ;
   DISPOSE (head)  (* { dg-warning "double-'DISPOSE via Storage.DEALLOCATE' of 
'head'.*" }  *)
END testdoubledispose3. 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g testdoubledispose3.mod
testdoubledispose3.mod: In function ‘_M2_testdoubledispose3_init’:
testdoubledispose3.mod:19:4: warning: double-‘DISPOSE via Storage.DEALLOCATE’ 
of ‘head’ [CWE-415] [-Wanalyzer-double-free]
   19 |    DISPOSE (head)  (* { dg-warning "double-'DISPOSE via 
Storage.DEALLOCATE' of 'head'.*" }  *)
      |    ^~~~~~~~~~~~~
  ‘_M2_testdoubledispose3_init’: events 1-3
    |
    |   16 |    NEW (head) ;
    |      |    ^~~~~~~~~
    |      |    |
    |      |    (1) allocated here
    |   17 |    NEW (head) ;
    |   18 |    DISPOSE (head) ;
    |      |    ~~~~~~~~~~~~~
    |      |    |
    |      |    (2) first ‘DISPOSE via Storage.DEALLOCATE’ here
    |   19 |    DISPOSE (head)  (* { dg-warning "double-'DISPOSE via 
Storage.DEALLOCATE' of 'head'.*" }  *)
    |      |    ~~~~~~~~~~~~~
    |      |    |
    |      |    (3) second ‘DISPOSE via Storage.DEALLOCATE’ here; first 
‘DISPOSE via Storage.DEALLOCATE’ was at (2)
    |
 
$ cat testdoubledispose.mod
MODULE testdoubledispose ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;

TYPE
   list = POINTER TO RECORD
                        value: CARDINAL ;
                        next : list ;
                     END ;
VAR
   head: list ;
BEGIN
   NEW (head) ;
   DISPOSE (head) ;
   DISPOSE (head) ;  (* { dg-warning "double-'DISPOSE via Storage.DEALLOCATE' 
of 'head'.*" }  *)
END testdoubledispose. 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g testdoubledispose.mod
testdoubledispose.mod: In function ‘_M2_testdoubledispose_init’:
testdoubledispose.mod:18:4: warning: double-‘DISPOSE via Storage.DEALLOCATE’ of 
‘head’ [CWE-415] [-Wanalyzer-double-free]
   18 |    DISPOSE (head) ;  (* { dg-warning "double-'DISPOSE via 
Storage.DEALLOCATE' of 'head'.*" }  *)
      |    ^~~~~~~~~~~~~
  ‘_M2_testdoubledispose_init’: events 1-3
    |
    |   16 |    NEW (head) ;
    |      |    ^~~~~~~~~
    |      |    |
    |      |    (1) allocated here
    |   17 |    DISPOSE (head) ;
    |      |    ~~~~~~~~~~~~~
    |      |    |
    |      |    (2) first ‘DISPOSE via Storage.DEALLOCATE’ here
    |   18 |    DISPOSE (head) ;  (* { dg-warning "double-'DISPOSE via 
Storage.DEALLOCATE' of 'head'.*" }  *)
    |      |    ~~~~~~~~~~~~~
    |      |    |
    |      |    (3) second ‘DISPOSE via Storage.DEALLOCATE’ here; first 
‘DISPOSE via Storage.DEALLOCATE’ was at (2)
    |
 
$ cat testdoublefree2.mod
MODULE testdoublefree2 ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM libc IMPORT free ;
FROM SYSTEM IMPORT ADDRESS ;

VAR
   a: ADDRESS ;
BEGIN
   free (a) ;
   free (a)  (* { dg-warning "double-'free' of 'a'.*" }  *)
END testdoublefree2. 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g testdoublefree2.mod
testdoublefree2.mod: In function ‘_M2_testdoublefree2_init’:
testdoublefree2.mod:13:4: warning: double-‘free’ of ‘a’ [CWE-415] 
[-Wanalyzer-double-free]
   13 |    free (a)  (* { dg-warning "double-'free' of 'a'.*" }  *)
      |    ^~~~
  ‘_M2_testdoublefree2_init’: events 1-2
    |
    |   12 |    free (a) ;
    |      |    ^~~~
    |      |    |
    |      |    (1) first ‘free’ here
    |   13 |    free (a)  (* { dg-warning "double-'free' of 'a'.*" }  *)
    |      |    ~~~~
    |      |    |
    |      |    (2) second ‘free’ here; first ‘free’ was at (1)
    |
 
$ cat testdoublefree.mod
MODULE testdoublefree ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM libc IMPORT malloc, free ;
FROM SYSTEM IMPORT ADDRESS ;

VAR
   a: ADDRESS ;
BEGIN
   a := malloc (100) ;
   free (a) ;
   free (a)  (* { dg-warning "double-'free' of 'a'.*" }  *)
END testdoublefree. 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g testdoublefree.mod
testdoublefree.mod: In function ‘_M2_testdoublefree_init’:
testdoublefree.mod:14:4: warning: double-‘free’ of ‘a’ [CWE-415] 
[-Wanalyzer-double-free]
   14 |    free (a)  (* { dg-warning "double-'free' of 'a'.*" }  *)
      |    ^~~~
  ‘_M2_testdoublefree_init’: events 1-3
    |
    |   12 |    a := malloc (100) ;
    |      |         ^~~~~~
    |      |         |
    |      |         (1) allocated here
    |   13 |    free (a) ;
    |      |    ~~~~  
    |      |    |
    |      |    (2) first ‘free’ here
    |   14 |    free (a)  (* { dg-warning "double-'free' of 'a'.*" }  *)
    |      |    ~~~~  
    |      |    |
    |      |    (3) second ‘free’ here; first ‘free’ was at (2)
    |
 
$ cat testnoreturn2.mod
MODULE testnoreturn2 ;

(* { dg-options "-fsoft-check-all -O" }  *)
(* { dg-do compile }  *)

FROM libc IMPORT printf ;
FROM Storage IMPORT ALLOCATE ;

TYPE
   ptrToCard = POINTER TO CARDINAL ;


PROCEDURE foo (i, j: ptrToCard) : ptrToCard ;
BEGIN
   IF i = j
   THEN
      RETURN i
   END ;
END foo ;   (* { dg-error ".*this function will exit without executing a RETURN 
statement.*" }  *)

VAR
   a, b, c: ptrToCard ;
BEGIN
   NEW (a) ;
   IF a # NIL
   THEN
      a^ := 1 ;
      NEW (b) ;
      IF b # NIL
      THEN
         b^ := 1 ;
         c := foo (a, b) ;
         printf ("value is %d\n", c^)
      END
   END
END testnoreturn2.
 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g testnoreturn2.mod
testnoreturn2.mod: In function ‘foo’:
testnoreturn2.mod:32:15: warning: call to the procedure function ‘foo’ may not 
return a result and therefore the value of ‘foo (a, b)’ maybe indeterminate 
[-fanalyzer]
   32 |          c := foo (a, b) ;
      |               ^~~
  ‘_M2_testnoreturn2_init’: events 1-10
    |
    |   23 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (1) entry to ‘_M2_testnoreturn2_init’
    |   24 |    NEW (a) ;
    |   25 |    IF a # NIL
    |      |       ~~~~~~~
    |      |         |
    |      |         (2) following ‘false’ branch...
    |   26 |    THEN
    |   27 |       a^ := 1 ;
    |      |        ~ ~~
    |      |        | |
    |      |        | (5) ...to here
    |      |        (3) ...to here
    |      |        (4) following ‘false’ branch...
    |   28 |       NEW (b) ;
    |   29 |       IF b # NIL
    |      |          ~~~~~~~
    |      |            |
    |      |            (6) following ‘false’ branch...
    |   30 |       THEN
    |   31 |          b^ := 1 ;
    |      |           ~ ~~
    |      |           | |
    |      |           | (9) ...to here
    |      |           | (10) later on, when the indeterminate value is returned
    |      |           (7) ...to here
    |      |           (8) following ‘false’ branch...
    |
  ‘foo’: events 11-14
    |
    |   14 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (11) entry to ‘foo’
    |   15 |    IF i = j
    |      |       ~~~~~
    |      |         |
    |      |         (12) following ‘true’ branch...
    |......
    |   18 |    END ;
    |      |    ~~~
    |      |    |
    |      |    (13) ...to here
    |......
    |   32 |          c := foo (a, b) ;
    |      |               ~~~
    |      |               |
    |      |               (14) the procedure function might return an 
indeterminate value (procedure function ‘foo’ is missing a return statement)
    |
 
$ cat testnoreturn3.mod
MODULE testnoreturn3 ;

(* { dg-options "-fsoft-check-all -O" }  *)
(* { dg-do compile }  *)

PROCEDURE foo (i, j: CARDINAL) : CARDINAL ;
BEGIN
   IF i = j
   THEN
      RETURN i
   END ;
END foo ;   (* { dg-error ".*this function will exit without executing a RETURN 
statement.*" }  *)


PROCEDURE bar (i, j: CARDINAL) : CARDINAL ;
BEGIN
   RETURN foo (i, j)
END bar ;


VAR
   x: CARDINAL ;
BEGIN
   x := bar (1, 2)
END testnoreturn3.
 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g testnoreturn3.mod
testnoreturn3.mod: In function ‘foo’:
testnoreturn3.mod:17:11: warning: call to the procedure function ‘foo’ may not 
return a result and therefore the value of ‘foo (i_2(D), j_3(D))’ maybe 
indeterminate [-fanalyzer]
   17 |    RETURN foo (i, j)
      |           ^~~
  ‘bar’: events 1-2
    |
    |   16 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (1) entry to ‘bar’
    |   17 |    RETURN foo (i, j)
    |      |           ~~~
    |      |           |
    |      |           (2) later on, when the indeterminate value is returned
    |
  ‘foo’: events 3-6
    |
    |    7 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (3) entry to ‘foo’
    |    8 |    IF i = j
    |      |       ~~~~~
    |      |         |
    |      |         (4) following ‘true’ branch (when ‘i_1(D) != j_2(D)’)...
    |......
    |   11 |    END ;
    |      |    ~~~
    |      |    |
    |      |    (5) ...to here
    |......
    |   17 |    RETURN foo (i, j)
    |      |           ~~~
    |      |           |
    |      |           (6) the procedure function might return an indeterminate 
value (procedure function ‘foo’ is missing a return statement)
    |
testnoreturn3.mod:24:9: warning: call to the procedure function ‘bar’ may not 
return a result and therefore the value of ‘bar (1, 2)’ maybe indeterminate 
[-fanalyzer]
   24 |    x := bar (1, 2)
      |         ^~~
  ‘_M2_testnoreturn3_init’: events 1-2
    |
    |   23 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (1) entry to ‘_M2_testnoreturn3_init’
    |   24 |    x := bar (1, 2)
    |      |         ~~~
    |      |         |
    |      |         (2) later on, when the indeterminate value is returned
    |
  ‘bar’: events 3-4
    |
    |   16 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (3) entry to ‘bar’
    |   17 |    RETURN foo (i, j)
    |      |           ~~~
    |      |           |
    |      |           (4) calling ‘foo’ from ‘bar’
    |
    +--> ‘foo’: events 5-8
           |
           |    7 | BEGIN
           |      | ^~~~~
           |      | |
           |      | (5) entry to ‘foo’
           |    8 |    IF i = j
           |      |       ~~~~~
           |      |         |
           |      |         (6) following ‘true’ branch (when ‘i_1(D) != 
j_2(D)’)...
           |......
           |   11 |    END ;
           |      |    ~~~
           |      |    |
           |      |    (7) ...to here
           |......
           |   24 |    x := bar (1, 2)
           |      |         ~~~
           |      |         |
           |      |         (8) the procedure function might return an 
indeterminate value (procedure function ‘bar’ is missing a return statement)
           |
 
$ cat testnoreturn.mod
MODULE testnoreturn ;

(* { dg-options "-fsoft-check-all -O" }  *)
(* { dg-do compile }  *)

PROCEDURE foo (i, j: CARDINAL) : CARDINAL ;
BEGIN
   IF i = j
   THEN
      RETURN i
   END ;
END foo ;   (* { dg-error ".*this function will exit without executing a RETURN 
statement.*" }  *)

VAR
   x: CARDINAL ;
BEGIN
   x := foo (1, 2)
END testnoreturn.
 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g testnoreturn.mod
testnoreturn.mod: In function ‘foo’:
testnoreturn.mod:17:9: warning: call to the procedure function ‘foo’ may not 
return a result and therefore the value of ‘foo (1, 2)’ maybe indeterminate 
[-fanalyzer]
   17 |    x := foo (1, 2)
      |         ^~~
  ‘_M2_testnoreturn_init’: events 1-2
    |
    |   16 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (1) entry to ‘_M2_testnoreturn_init’
    |   17 |    x := foo (1, 2)
    |      |         ~~~
    |      |         |
    |      |         (2) later on, when the indeterminate value is returned
    |
  ‘foo’: events 3-6
    |
    |    7 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (3) entry to ‘foo’
    |    8 |    IF i = j
    |      |       ~~~~~
    |      |         |
    |      |         (4) following ‘true’ branch (when ‘i_1(D) != j_2(D)’)...
    |......
    |   11 |    END ;
    |      |    ~~~
    |      |    |
    |      |    (5) ...to here
    |......
    |   17 |    x := foo (1, 2)
    |      |         ~~~
    |      |         |
    |      |         (6) the procedure function might return an indeterminate 
value (procedure function ‘foo’ is missing a return statement)
    |
 
$ cat uncheckedmalloc.mod
MODULE uncheckedmalloc ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM libc IMPORT malloc, free ;
FROM SYSTEM IMPORT SIZE ;

TYPE
   List = POINTER TO RECORD
                        value: CARDINAL ;
                        next : List ;
                     END ;

VAR
   l: List ;
BEGIN
   l := malloc (SIZE (List)) ;
   l^.value := 1 ;    (* { dg-warning "dereference of possibly-NULL .*" }  *)
   l^.next := NIL ;
   free (l)
END uncheckedmalloc. 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g uncheckedmalloc.mod
 
$ cat useafterdeallocate.mod
MODULE useafterdeallocate ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;

TYPE
   ptrType = POINTER TO RECORD
                           foo: CARDINAL ;
                        END ;

VAR
   head: ptrType ;
BEGIN
   NEW (head) ;
   IF head # NIL
   THEN
      head^.foo := 1 ;
      DISPOSE (head) ;
      head^.foo := 2  (* { dg-warning "use after 'DISPOSE via 
Storage.DEALLOCATE' of 'head'.*" }  *)
   END
END useafterdeallocate.
 
$ gm2 -fsoft-check-all -O2 -fanalyzer -c -g useafterdeallocate.mod
useafterdeallocate.mod: In function ‘_M2_useafterdeallocate_init’:
useafterdeallocate.mod:21:17: warning: use after ‘DISPOSE via 
Storage.DEALLOCATE’ of ‘head’ [CWE-416] [-Wanalyzer-use-after-free]
   21 |       head^.foo := 2  (* { dg-warning "use after 'DISPOSE via 
Storage.DEALLOCATE' of 'head'.*" }  *)
      |                 ^~
  ‘_M2_useafterdeallocate_init’: events 1-10
    |
    |   16 |    NEW (head) ;
    |      |    ^~~~~~~~~
    |      |    |
    |      |    (1) allocated here
    |   17 |    IF head # NIL
    |      |       ~~~~~~~~~~
    |      |            |
    |      |            (2) assuming ‘head’ is non-NULL
    |      |            (3) following ‘false’ branch...
    |   18 |    THEN
    |   19 |       head^.foo := 1 ;
    |      |           ~~
    |      |           ||
    |      |           |(6) ...to here
    |      |           (4) ...to here
    |      |           (5) following ‘false’ branch...
    |   20 |       DISPOSE (head) ;
    |      |       ~~~~~~~~~~~~~
    |      |       |
    |      |       (7) deallocated here
    |   21 |       head^.foo := 2  (* { dg-warning "use after 'DISPOSE via 
Storage.DEALLOCATE' of 'head'.*" }  *)
    |      |           ~~    ~~
    |      |           ||    |
    |      |           ||    (10) use after ‘DISPOSE via Storage.DEALLOCATE’ of 
‘_T30’; deallocated at (7)
    |      |           |(9) ...to here
    |      |           (8) following ‘false’ branch...
    |
 
$ cat callbyref2.mod
MODULE callbyref2 ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
FROM SYSTEM IMPORT ADR ;
FROM libc IMPORT printf ;

TYPE
   ptrProc = POINTER TO PROCEDURE (CARDINAL) ;


PROCEDURE foo (c: CARDINAL) ;
BEGIN
   printf ("yes\n")
END foo ;


PROCEDURE setup () : ptrProc ;
VAR
   p: ptrProc ;
BEGIN
   NEW (p) ;
   p^ := foo ;  (* { dg-warning "dereference of possibly-NULL 'p'.*" }  *)
   RETURN p
END setup ;


VAR
   p: ptrProc ;
BEGIN
   p := setup () ;
   p^ (1)
END callbyref2.
 
$ gm2 -O2 -fsoft-check-all -fanalyzer -c -g callbyref2.mod
callbyref2.mod: In function ‘setup’:
callbyref2.mod:25:7: warning: dereference of NULL ‘p’ [CWE-476] 
[-Wanalyzer-null-dereference]
   25 |    p^ := foo ;  (* { dg-warning "dereference of possibly-NULL 'p'.*" }  
*)
      |       ^~
  ‘setup’: events 1-5
    |
    |   24 |    NEW (p) ;
    |      |    ^~~~~~
    |      |    |
    |      |    (1) allocated here
    |   25 |    p^ := foo ;  (* { dg-warning "dereference of possibly-NULL 
'p'.*" }  *)
    |      |     ~ ~~
    |      |     | |
    |      |     | (4) ...to here
    |      |     | (5) dereference of NULL ‘p’
    |      |     (2) assuming ‘p’ is NULL
    |      |     (3) following ‘true’ branch...
    |
 
$ cat callbyref3badreturn.mod
MODULE callbyref3badreturn ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
FROM SYSTEM IMPORT ADR ;
FROM libc IMPORT printf ;

TYPE
   ptrProc = POINTER TO PROCEDURE (CARDINAL) ;


PROCEDURE foo (c: CARDINAL) ;
BEGIN
   printf ("yes\n")
END foo ;


PROCEDURE setup () : ptrProc ;
VAR
   p: ptrProc ;
BEGIN
   NEW (p) ;
   IF p # NIL
   THEN
      p^ := foo ;  (* { dg-warning "dereference of possibly-NULL 'p'.*" }  *)
      RETURN p
   END
END setup ;


VAR
   p: ptrProc ;
BEGIN
   p := setup () ;
   p^ (1)
END callbyref3badreturn.
 
$ gm2 -O2 -fsoft-check-all -fanalyzer -c -g callbyref3badreturn.mod
callbyref3badreturn.mod: In function ‘setup’:
callbyref3badreturn.mod:36:9: warning: call to the procedure function ‘setup’ 
may not return a result and therefore the value of ‘setup ()’ maybe 
indeterminate [-fanalyzer]
   36 |    p := setup () ;
      |         ^~~~~
  ‘_M2_callbyref3badreturn_init’: events 1-2
    |
    |   35 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (1) entry to ‘_M2_callbyref3badreturn_init’
    |   36 |    p := setup () ;
    |      |         ~~~~~
    |      |         |
    |      |         (2) later on, when the indeterminate value is returned
    |
  ‘setup’: events 3-6
    |
    |   23 | BEGIN
    |      | ^~~~~
    |      | |
    |      | (3) entry to ‘setup’
    |   24 |    NEW (p) ;
    |   25 |    IF p # NIL
    |      |       ~~~~~~~
    |      |         |
    |      |         (4) following ‘true’ branch...
    |......
    |   29 |    END
    |      |    ~~~
    |      |    |
    |      |    (5) ...to here
    |......
    |   36 |    p := setup () ;
    |      |         ~~~~~
    |      |         |
    |      |         (6) the procedure function might return an indeterminate 
value (procedure function ‘setup’ is missing a return statement)
    |
 
$ cat callbyref.mod
MODULE callbyref ;

(* { dg-options "-fanalyzer" }  *)
(* { dg-do compile }  *)

FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
FROM SYSTEM IMPORT ADR ;
FROM libc IMPORT printf ;

TYPE
   ptrProc = POINTER TO PROCEDURE (CARDINAL) ;


PROCEDURE foo (c: CARDINAL) ;
BEGIN
   printf ("yes\n")
END foo ;


PROCEDURE setup () : ptrProc ;
BEGIN
   RETURN NIL
END setup ;


VAR
   p: ptrProc ;
BEGIN
   p := setup () ;
   p^ (1)   (* { dg-warning "dereference of NULL '0B'.*" }  *)
END callbyref.
 
$ gm2 -O2 -fsoft-check-all -fanalyzer -c -g callbyref.mod
callbyref.mod:30:5: error: runtime error will occur, if this pointer value ‘p’ 
is ever dereferenced it will cause an exception (in program module callbyref)

   30 |    p^ (1)   (* { dg-warning "dereference of NULL '0B'.*" }  *)
      |     ^

regards,
Gaius



reply via email to

[Prev in Thread] Current Thread [Next in Thread]