|
|
|
/*
|
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdnoreturn.h>
|
|
|
|
|
|
|
|
int* malloc_no_check_bad() {
|
|
|
|
int* p = (int*)malloc(sizeof(int));
|
|
|
|
*p = 42;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void create_null_path_ok(int* p) {
|
|
|
|
if (p) {
|
|
|
|
*p = 32;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void call_create_null_path_then_deref_unconditionally_ok(int* p) {
|
|
|
|
create_null_path_ok(p);
|
|
|
|
*p = 52;
|
|
|
|
}
|
|
|
|
|
|
|
|
void create_null_path2_latent(int* p) {
|
|
|
|
int* q = NULL;
|
|
|
|
if (p) {
|
|
|
|
*p = 32;
|
|
|
|
}
|
|
|
|
// arguably bogus to check p above but not here, but the above could
|
|
|
|
// also be macro-generated code so both reporting and not reporting
|
|
|
|
// are sort of justifiable
|
|
|
|
*p = 52;
|
|
|
|
}
|
|
|
|
|
|
|
|
// combine several of the difficulties above
|
|
|
|
void malloc_then_call_create_null_path_then_deref_unconditionally_latent(
|
|
|
|
int* p) {
|
|
|
|
int* x = (int*)malloc(sizeof(int));
|
|
|
|
if (p) {
|
|
|
|
*p = 32;
|
|
|
|
}
|
|
|
|
create_null_path_ok(p);
|
|
|
|
*p = 52;
|
|
|
|
free(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
// pulse should remember the value of vec[64] because it was just written to
|
|
|
|
void nullptr_deref_young_bad(int* x) {
|
|
|
|
int* vec[65] = {x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x,
|
|
|
|
x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x,
|
|
|
|
x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x,
|
|
|
|
x, x, x, x, x, x, x, x, x, x, x, x, x, NULL};
|
|
|
|
int p = *vec[64];
|
|
|
|
}
|
|
|
|
|
|
|
|
// due to the recency model of memory accesses, vec[0] can get forgotten
|
|
|
|
// by the time we have processed the last element of the
|
|
|
|
// initialization so we don't report here
|
|
|
|
void FN_nullptr_deref_old_bad(int* x) {
|
|
|
|
int* vec[65] = {NULL, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x,
|
|
|
|
x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x,
|
|
|
|
x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x,
|
|
|
|
x, x, x, x, x, x, x, x, x, x, x, x, x, x};
|
|
|
|
int p = *vec[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
void malloc_free_ok() {
|
|
|
|
int* p = (int*)malloc(sizeof(int));
|
|
|
|
free(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
void wrap_free(void* p) { free(p); }
|
|
|
|
|
|
|
|
void interproc_free_ok() {
|
|
|
|
int* p = (int*)malloc(sizeof(int));
|
|
|
|
wrap_free(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
noreturn void no_return();
|
|
|
|
|
|
|
|
void wrap_malloc(int** x) {
|
|
|
|
*x = (int*)malloc(sizeof(int));
|
|
|
|
if (!*x) {
|
|
|
|
no_return();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void call_no_return_good() {
|
|
|
|
int* x = NULL;
|
|
|
|
wrap_malloc(&x);
|
|
|
|
*x = 5;
|
|
|
|
free(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
void bug_after_malloc_result_test_bad(int* x) {
|
|
|
|
x = (int*)malloc(sizeof(int));
|
|
|
|
if (x) {
|
|
|
|
int* y = NULL;
|
|
|
|
*y = 42;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void bug_after_abduction_bad(int* x) {
|
|
|
|
*x = 42;
|
|
|
|
int* y = NULL;
|
|
|
|
*y = 42;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bug_with_allocation_bad(int* x) {
|
|
|
|
x = (int*)malloc(sizeof(int*));
|
|
|
|
int* y = NULL;
|
|
|
|
*y = 42;
|
|
|
|
}
|
|
|
|
|
|
|
|
void null_alias_bad(int* x) {
|
|
|
|
int* y = NULL;
|
|
|
|
x = (int*)malloc(sizeof(int*));
|
|
|
|
*x = 42;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dereference(int* p) { *p; }
|
|
|
|
|
|
|
|
void several_dereferences_ok(int* x, int* y, int* z) {
|
|
|
|
int* p = x;
|
|
|
|
*z = 52;
|
|
|
|
dereference(y);
|
|
|
|
*y = 42;
|
|
|
|
*x = 32;
|
|
|
|
*x = 777;
|
|
|
|
*y = 888;
|
|
|
|
*z = 999;
|
|
|
|
}
|
|
|
|
|
|
|
|
void report_correct_error_among_multiple_bad() {
|
|
|
|
int* p = NULL;
|
|
|
|
// the trace should complain about the first access inside the callee
|
|
|
|
several_dereferences_ok(p, p, p);
|
|
|
|
}
|
[pulse] functional unknown functions
Summary:
Unknown functions may create false positives as well as false negatives
for Pulse. Let's consider that unknown functions behave "functionally",
or at least that a functional behaviour is a possible behaviour for
them: when called with the same parameter values, they should return the
same value.
This is implemented purely in the arithmetic domain by recording
`v_return = f_unknown(v1, v2, ..., vN)` for each call to unknown
functions `f_unknown` with values `v1`, `v2`, ..., `vN` (and return
`v_return`). The hope is that this will create more false negatives than
false positives, as several FPs have been observed on real code that
would be suppressed with this heuristic.
The other effect this has on reports is to record hypotheses made on the
return values of unknown functions into the "pruned" part of formulas,
which inhibits reporting on paths whose feasibility depends on the
return value of unknown functions (by making these issues latent
instead). This should allow us to control the amount of FPs until we
model more functions.
Reviewed By: skcho
Differential Revision: D27798275
fbshipit-source-id: d31cfb8b6
4 years ago
|
|
|
|
|
|
|
int unknown(int x);
|
|
|
|
|
|
|
|
void unknown_is_functional_ok() {
|
|
|
|
int* p = NULL;
|
|
|
|
if (unknown(10) != unknown(10)) {
|
|
|
|
*p = 42;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void unknown_with_different_values_bad() {
|
|
|
|
int* p = NULL;
|
|
|
|
if (unknown(32) != unknown(52)) {
|
|
|
|
*p = 42;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void unknown_from_parameters_latent(int x) {
|
|
|
|
int* p = NULL;
|
|
|
|
if (unknown(x) == 999) {
|
|
|
|
*p = 42;
|
|
|
|
}
|
|
|
|
}
|