Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 33 additions & 21 deletions src/analyses/memOutOfBounds.ml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@ struct
let size_of_type_in_bytes typ =
intdom_of_int (Cilfacade.bytesSizeOf typ)

let offs_lt_zero offs =
try ID.lt offs (intdom_of_int 0)
with IntDomain.ArithmeticOnIntegerBot _ -> None

let check_deref_offset_bounds ptr_size offs =
let ptr_size_le_offs =
try ID.le ptr_size offs
with IntDomain.ArithmeticOnIntegerBot _ -> None
in
offs_lt_zero offs, ptr_size_le_offs

let check_ptr_offset_bounds ptr_size offs =
let ptr_size_lt_offs =
try ID.lt ptr_size offs
with IntDomain.ArithmeticOnIntegerBot _ -> None
in
offs_lt_zero offs, ptr_size_lt_offs
Comment thread
sim642 marked this conversation as resolved.

let rec exp_contains_a_ptr (exp:exp) =
match exp with
| Const _
Expand Down Expand Up @@ -290,23 +308,17 @@ struct
| `Lifted es ->
let casted_es = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) es in (* TODO: proper castkind *)
let casted_offs = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) offs_intdom in (* TODO: proper castkind *)
let ptr_size_lt_offs =
let one = intdom_of_int 1 in
let casted_es = ID.sub casted_es one in
begin try ID.lt casted_es casted_offs
with IntDomain.ArithmeticOnIntegerBot _ -> None
end
in
let behavior = Undefined MemoryOutOfBoundsAccess in
let cwe_number = 823 in
begin match ptr_size_lt_offs with
| Some true ->
begin match check_deref_offset_bounds casted_es casted_offs with
| Some true, _
| _, Some true ->
(set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of lval dereference expression is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" ID.pretty casted_es ID.pretty casted_offs);
Checks.warn Checks.Category.InvalidMemoryAccess "Size of lval dereference expression is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" ID.pretty casted_es ID.pretty casted_offs
| Some false ->
| Some false, Some false ->
Checks.safe Checks.Category.InvalidMemoryAccess
| None ->
| _ ->
(set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of lval dereference expression (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_es ID.pretty casted_offs);
Checks.warn Checks.Category.InvalidMemoryAccess "Could not compare size of lval dereference expression (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_es ID.pretty casted_offs
Expand Down Expand Up @@ -343,15 +355,15 @@ struct
| `Lifted ps, ao ->
let casted_ps = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) ps in (* TODO: proper castkind *)
let casted_ao = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) ao in (* TODO: proper castkind *)
let ptr_size_lt_offs = ID.lt casted_ps casted_ao in
begin match ptr_size_lt_offs with
| Some true ->
begin match check_ptr_offset_bounds casted_ps casted_ao with
| Some true, _
| _, Some true ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" ID.pretty casted_ps ID.pretty casted_ao;
Checks.warn Checks.Category.InvalidMemoryAccess "Size of pointer is %a (in bytes). It is offset by %a (in bytes) due to pointer arithmetic. Memory out-of-bounds access must occur" ID.pretty casted_ps ID.pretty casted_ao
| Some false ->
| Some false, Some false ->
Checks.safe Checks.Category.InvalidMemoryAccess
| None ->
| _ ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare size of pointer (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_ps ID.pretty casted_ao;
Checks.warn Checks.Category.InvalidMemoryAccess "Could not compare size of pointer (%a) (in bytes) with offset by (%a) (in bytes). Memory out-of-bounds access might occur" ID.pretty casted_ps ID.pretty casted_ao
Expand Down Expand Up @@ -431,15 +443,15 @@ struct
| `Lifted ps, `Lifted o ->
let casted_ps = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) ps in (* TODO: proper castkind *)
let casted_o = ID.cast_to ~kind:Internal (Cilfacade.ptrdiff_ikind ()) o in (* TODO: proper castkind *)
let ptr_size_lt_offs = ID.lt casted_ps casted_o in
begin match ptr_size_lt_offs with
| Some true ->
begin match check_ptr_offset_bounds casted_ps casted_o with
| Some true, _
| _, Some true ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp ID.pretty casted_ps ID.pretty casted_o;
Checks.warn Checks.Category.InvalidMemoryAccess "Size of pointer in expression %a is %a (in bytes). It is offset by %a (in bytes). Memory out-of-bounds access must occur" d_exp binopexp ID.pretty casted_ps ID.pretty casted_o
| Some false ->
| Some false, Some false ->
Checks.safe Checks.Category.InvalidMemoryAccess
| None ->
| _ ->
set_mem_safety_flag InvalidDeref;
M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Could not compare pointer size (%a) with offset (%a). Memory out-of-bounds access may occur" ID.pretty casted_ps ID.pretty casted_o;
Checks.warn Checks.Category.InvalidMemoryAccess "Could not compare pointer size (%a) with offset (%a). Memory out-of-bounds access may occur" ID.pretty casted_ps ID.pretty casted_o
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval
#include <stdlib.h>

int main() {
int *b = malloc(2 * sizeof(int));
int x;

*b++ = 0; //NOWARN

x = *(b - 2); //WARN

free(b - 1);
return x;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval
#include <stdlib.h>

int main() {
int *b = malloc(2 * sizeof(int));

*b++ = 0; //NOWARN

if (b[-2]) //WARN
return 1;

free(b - 1);
return 0;
}
12 changes: 12 additions & 0 deletions tests/regression/74-invalid_deref/36-one-past-pointer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval
#include <stdlib.h>
#include <stdio.h>

int main(void) {
char *buf = malloc(4);
char *end;
end = buf + 4; //NOWARN
printf("%p", (void *) end); //NOWARN

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
printf("%p", (void *) end); //NOWARN
printf("%p", (void *) end); //NOWARN
printf("%c", *end); //WARN

So we also see that we do indeed warn if this pointer is dereferenced.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ha! it seems you just uncovered an unrelated unsoundness with this ;) I suggest we merge this as is and I will add this suggestion on another PR with an appropriate fix.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sounds good!

free(buf);
return 0;
}
16 changes: 16 additions & 0 deletions tests/regression/74-invalid_deref/37-one-past-deref-offset.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// PARAM: --set ana.activated[+] memOutOfBounds --enable ana.int.interval
#include <stdlib.h>

struct S {
unsigned char a;
unsigned char b:2;
unsigned char c:2;
unsigned char d;
} __attribute__((packed));

int main(void) {
struct S *p = malloc(2);
p->d = 1; //WARN
free(p);
return 0;
}
Loading