-
Notifications
You must be signed in to change notification settings - Fork 88
Fix memOutOfBounds handling of negative pointer offsets
#2017
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
321fe15
f57838e
8ad5fb3
5447075
8400643
08f01ee
9c3cf67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,6 +34,27 @@ 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_lt_offs = | ||
| try | ||
| let one = intdom_of_int 1 in | ||
| let max_valid_offs = ID.sub ptr_size one in | ||
| ID.lt max_valid_offs offs | ||
| with IntDomain.ArithmeticOnIntegerBot _ -> None | ||
| in | ||
| offs_lt_zero offs, ptr_size_lt_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 on lines
+48
to
+53
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you elaborate on why this needs to be handled differently from the things above?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First I didn't give it much thought, I just refactored out the parts that were in the original code, but with the help of Codex, I now know the difference and we also added regtests that would catch a change in these in both directions:
The unsoundness by changing the deref case to
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for checking!
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But this is still strange. The memOutOfBounds analysis should only consider dereferencing. If a pointer points one or more past the end should make no difference if it's never dereferenced. It would only matter for the valid-memtrack subproperty which is handled by memLeak (which has many other problems).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll merge this so we can see if it makes any difference in the knightly. |
||
|
|
||
| let rec exp_contains_a_ptr (exp:exp) = | ||
| match exp with | ||
| | Const _ | ||
|
|
@@ -290,23 +311,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 | ||
|
|
@@ -343,15 +358,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 | ||
|
|
@@ -431,15 +446,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 | ||
|
|
||
| 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; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.