[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
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- more progress with -fanalyzer,
Gaius Mulley <=