Skip to content
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

UncheckedRangeDomainPoleErrors: add missing cases #572

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
467 changes: 238 additions & 229 deletions c/common/test/includes/standard-library/math.h

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
| test.c:4:3:4:6 | call to acos | Domain error in call to acos: the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). |
| test.c:8:3:8:6 | call to acos | Domain error in call to acos: the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). |
| test.c:9:3:9:6 | call to asin | Domain error in call to asin: the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). |
| test.c:13:3:13:6 | call to asin | Domain error in call to asin: the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). |
| test.c:14:3:14:7 | call to atanh | Domain error in call to atanh: the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). |
| test.c:18:3:18:7 | call to atanh | Domain error in call to atanh: the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). |
| test.c:19:3:19:7 | call to atan2 | Domain error in call to atan2: both arguments are equal to zero. |
| test.c:23:3:23:5 | call to pow | Domain error in call to pow: both arguments are equal to zero. |
| test.c:27:3:27:5 | call to pow | Domain error in call to pow: both arguments are less than zero. |
| test.c:33:3:33:7 | call to acosh | Domain error in call to acosh: argument is less than 1. |
| test.c:34:3:34:7 | call to ilogb | Domain error in call to ilogb: argument is equal to zero. |
| test.c:37:3:37:5 | call to log | Domain error in call to log: argument is negative. |
| test.c:40:3:40:7 | call to log10 | Domain error in call to log10: argument is negative. |
| test.c:43:3:43:6 | call to log2 | Domain error in call to log2: argument is negative. |
| test.c:46:3:46:6 | call to sqrt | Domain error in call to sqrt: argument is negative. |
| test.c:49:3:49:7 | call to log1p | Domain error in call to log1p: argument is less than 1. |
| test.c:52:3:52:6 | call to logb | Domain error in call to logb: argument is equal to zero. |
| test.c:55:3:55:8 | call to tgamma | Domain error in call to tgamma: argument is equal to zero. |
| test.c:6:3:6:6 | call to acos | Domain error in call to 'acos': the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). |
| test.c:10:3:10:6 | call to acos | Domain error in call to 'acos': the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). |
| test.c:11:3:11:6 | call to asin | Domain error in call to 'asin': the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). |
| test.c:15:3:15:6 | call to asin | Domain error in call to 'asin': the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). |
| test.c:16:3:16:7 | call to atanh | Domain error in call to 'atanh': the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). |
| test.c:18:3:18:7 | call to atanh | Domain error in call to 'atanh': the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). |
| test.c:19:3:19:7 | call to atan2 | Domain error in call to 'atan2': both arguments are equal to zero. |
| test.c:23:3:23:5 | call to pow | Domain error in call to 'pow': both arguments are equal to zero. |
| test.c:27:3:27:5 | call to pow | Domain error in call to 'pow': both arguments are less than zero. |
| test.c:32:3:32:7 | call to acosh | Domain error in call to 'acosh': argument is less than 1. |
| test.c:33:3:33:7 | call to ilogb | Domain error in call to 'ilogb': argument is equal to zero. |
| test.c:36:3:36:5 | call to log | Domain error in call to 'log': argument is negative. |
| test.c:38:3:38:7 | call to log10 | Domain error in call to 'log10': argument is negative. |
| test.c:40:3:40:6 | call to log2 | Domain error in call to 'log2': argument is negative. |
| test.c:42:3:42:6 | call to sqrt | Domain error in call to 'sqrt': argument is negative. |
| test.c:45:3:45:7 | call to log1p | Domain error in call to 'log1p': argument is less than 1. |
| test.c:47:3:47:6 | call to logb | Domain error in call to 'logb': argument is equal to zero. |
| test.c:50:3:50:8 | call to tgamma | Domain error in call to 'tgamma': argument is equal to zero. |
| test.c:56:3:56:5 | call to abs | Range error in call to 'abs': argument is most negative number. |
| test.c:57:3:57:6 | call to fmod | Domain error in call to 'fmod': y is 0. |
| test.c:59:3:59:7 | call to frexp | Unspecified error in call to 'frexp': Arg is Nan or infinity and exp is unspecified as a result. |
| test.c:60:3:60:7 | call to frexp | Unspecified error in call to 'frexp': Arg is Nan or infinity and exp is unspecified as a result. |
| test.c:64:3:64:7 | call to atanh | Pole error in call to 'atanh': argument is plus or minus 1. |
| test.c:65:3:65:7 | call to atanh | Pole error in call to 'atanh': argument is plus or minus 1. |
| test.c:66:3:66:5 | call to log | Pole error in call to 'log': argument is equal to zero. |
| test.c:67:3:67:7 | call to log10 | Pole error in call to 'log10': argument is equal to zero. |
| test.c:68:3:68:6 | call to log2 | Pole error in call to 'log2': argument is equal to zero. |
| test.c:69:3:69:7 | call to log1p | Pole error in call to 'log1p': argument is equal to negative one. |
| test.c:71:3:71:5 | call to pow | Pole error in call to 'pow': base is zero and exp is negative. |
| test.c:72:3:72:8 | call to lgamma | Pole error in call to 'lgamma': argument is equal to zero. |
32 changes: 24 additions & 8 deletions c/common/test/rules/uncheckedrangedomainpoleerrors/test.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <limits.h>
#include <math.h>
#include <stdlib.h>

void test() {
void test_domain_errors() {
acos(-1.1f); // NON_COMPLIANT
acos(-1.0f); // COMPLIANT
acos(0.0f); // COMPLIANT
Expand All @@ -12,9 +14,7 @@ void test() {
asin(1.0f); // COMPLIANT
asin(1.1f); // NON_COMPLIANT
atanh(-1.1f); // NON_COMPLIANT
atanh(-1.0f); // COMPLIANT
atanh(0.0f); // COMPLIANT
atanh(1.0f); // COMPLIANT
atanh(1.1f); // NON_COMPLIANT
atan2(0.0f, 0.0f); // NON_COMPLIANT
atan2(1.0f, 0.0f); // COMPLIANT
Expand All @@ -26,7 +26,6 @@ void test() {
pow(1.0f, 1.0f); // COMPLIANT
pow(-1.0f, -1.0f); // NON_COMPLIANT
pow(-1.0f, 0.0f); // COMPLIANT
pow(0.0f, -1.0f); // COMPLIANT
pow(1.0f, -1.0f); // COMPLIANT
pow(-1.0f, 1.0f); // COMPLIANT
acosh(1.0f); // COMPLIANT
Expand All @@ -35,19 +34,15 @@ void test() {
ilogb(1.0f); // COMPLIANT
ilogb(-1.0f); // COMPLIANT
log(-1.0f); // NON_COMPLIANT
log(0.0f); // COMPLIANT
log(1.0f); // COMPLIANT
log10(-1.0f); // NON_COMPLIANT
log10(0.0f); // COMPLIANT
log10(1.0f); // COMPLIANT
log2(-1.0f); // NON_COMPLIANT
log2(0.0f); // COMPLIANT
log2(1.0f); // COMPLIANT
sqrt(-1.0f); // NON_COMPLIANT
sqrt(0.0f); // COMPLIANT
sqrt(1.0f); // COMPLIANT
log1p(-2.0f); // NON_COMPLIANT
log1p(-1.0f); // COMPLIANT
log1p(0.0f); // COMPLIANT
logb(0.0f); // NON_COMPLIANT
logb(1.0f); // COMPLIANT
Expand All @@ -56,3 +51,24 @@ void test() {
tgamma(1.0f); // COMPLIANT
tgamma(-1.1f); // COMPLIANT
}

void fn_in_193_missing_domain_or_range_cases() {
abs(INT_MIN); // NON_COMPLIANT
fmod(1.0f, 0.0f); // NON_COMPLIANT
int *exp;
frexp(NAN, exp); // NON_COMPLIANT
frexp(INFINITY, exp); // NON_COMPLIANT
}

void test_pole_errors() {
atanh(-1.0f); // NON_COMPLIANT
atanh(1.0f); // NON_COMPLIANT
log(0.0f); // NON_COMPLIANT
log10(0.0f); // NON_COMPLIANT
log2(0.0f); // NON_COMPLIANT
log1p(-1.0f); // NON_COMPLIANT
// logb(x) already covered in domain cases
pow(0.0f, -1.0f); // NON_COMPLIANT
lgamma(0.0f); // NON_COMPLIANT
lgamma(-1); // NON_COMPLIANT[FALSE_NEGATIVE]
}
2 changes: 2 additions & 0 deletions change_notes/2024-04-23-fix-fp-193.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `A0-4-4`,`FLP32-C` - `UncheckedRangeDomainPoleErrors.ql`:
- Fixes #193. Adds missing cases for domain errors, an unspecified result case and pole error cases.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ predicate hasDomainError(FunctionCall fc, string description) {
upperBound(fc.getArgument(0)) < 1.0 and
description = "argument is less than 1"
or
//pole error is the same as domain for logb and tgamma (but not ilogb - no pole error exists)
functionWithDomainError = getMathVariants(["ilogb", "logb", "tgamma"]) and
fc.getArgument(0).getValue().toFloat() = 0 and
description = "argument is equal to zero"
Expand All @@ -53,18 +54,95 @@ predicate hasDomainError(FunctionCall fc, string description) {
functionWithDomainError = getMathVariants("log1p") and
upperBound(fc.getArgument(0)) < -1.0 and
description = "argument is less than 1"
or
functionWithDomainError = getMathVariants("fmod") and
fc.getArgument(1).getValue().toFloat() = 0 and
description = "y is 0"
)
}

predicate hasRangeError(FunctionCall fc, string description) {
exists(Function functionWithRangeError | fc.getTarget() = functionWithRangeError |
functionWithRangeError.hasGlobalOrStdName(["abs", "labs", "llabs", "imaxabs"]) and
fc.getArgument(0) = any(MINMacro m).getAnInvocation().getExpr() and
description = "argument is most negative number"
)
}

predicate hasPoleError(FunctionCall fc, string description) {
exists(Function functionWithPoleError | fc.getTarget() = functionWithPoleError |
functionWithPoleError = getMathVariants("atanh") and
(
fc.getArgument(0).getValue().toFloat() = -1.0
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
fc.getArgument(0).getValue().toFloat() = -1.0
(fc.getArgument(0).getValue().toFloat() + 1.0).abs() < 0.001

Due to the imprecision of floating points we can't compare them like this.
You need to subtract them and check whether their absolute value is smaller than some epsilon.

or
fc.getArgument(0).getValue().toFloat() = 1.0
) and
description = "argument is plus or minus 1"
or
functionWithPoleError = getMathVariants("log1p") and
fc.getArgument(0).getValue().toFloat() = -1 and
description = "argument is equal to negative one"
or
functionWithPoleError = getMathVariants("pow") and
fc.getArgument(0).getValue().toFloat() = 0.0 and
fc.getArgument(1).getValue().toFloat() < 0.0 and
description = "base is zero and exp is negative"
or
functionWithPoleError = getMathVariants("lgamma") and
fc.getArgument(0).getValue().toFloat() = 0 and
description = "argument is equal to zero"
or
functionWithPoleError = getMathVariants(["log", "log10", "log2"]) and
fc.getArgument(0).getValue().toFloat() = 0.0 and
description = "argument is equal to zero"
)
}

predicate unspecifiedValueCases(FunctionCall fc, string description) {
exists(Function functionWithUnspecifiedResultError |
fc.getTarget() = functionWithUnspecifiedResultError
|
functionWithUnspecifiedResultError = getMathVariants("frexp") and
(
fc.getArgument(0) = any(InfinityMacro m).getAnInvocation().getExpr() or
fc.getArgument(0) = any(NanMacro m).getAnInvocation().getExpr()
) and
description = "Arg is Nan or infinity and exp is unspecified as a result"
)
}

/**
* A macro which is representing infinity
*/
class InfinityMacro extends Macro {
InfinityMacro() { this.getName().toLowerCase().matches("infinity") }
}

/**
* A macro which is representing nan
*/
class NanMacro extends Macro {
NanMacro() { this.getName().toLowerCase().matches("nan") }
}

/**
* A macro which is representing INT_MIN or LONG_MIN or LLONG_MIN
*/
class MINMacro extends Macro {
MINMacro() { this.getName().toLowerCase().matches(["int_min", "long_min", "llong_min"]) }
}

/*
* Domain cases not covered by this query:
* - pow - x is finite and negative and y is finite and not an integer value.
* - tgamma - negative integer can't be covered.
* - lrint/llrint/lround/llround - no domain errors checked
* - fmod - no domain errors checked.
* - remainder - no domain errors checked.
* - remquo - no domain errors checked.
*
* Pole cases not covered by this query:
* - lgamma - negative integer can't be covered.
*
* Implementations may also define their own domain errors (as per the C99 standard), which are not
* covered by this query.
*/
Expand All @@ -73,6 +151,16 @@ query predicate problems(FunctionCall fc, string message) {
not isExcluded(fc, getQuery()) and
exists(string description |
hasDomainError(fc, description) and
message = "Domain error in call to " + fc.getTarget().getName() + ": " + description + "."
message = "Domain error in call to '" + fc.getTarget().getName() + "': " + description + "."
or
hasRangeError(fc, description) and
message = "Range error in call to '" + fc.getTarget().getName() + "': " + description + "."
or
hasPoleError(fc, description) and
message = "Pole error in call to '" + fc.getTarget().getName() + "': " + description + "."
or
unspecifiedValueCases(fc, description) and
message =
"Unspecified error in call to '" + fc.getTarget().getName() + "': " + description + "."
)
}
2 changes: 2 additions & 0 deletions cpp/common/test/includes/standard-library/limits.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#define LLONG_MAX 9223372036854775807
#define ULLONG_MIN 0ULL
#define ULLONG_MAX 0xffffffffffffffff
#define NAN (0.0f / 0.0f)
#define INFINITY 1e5000f

namespace std {
template <class T> class numeric_limits;
Expand Down
11 changes: 11 additions & 0 deletions cpp/common/test/includes/standard-library/math.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#ifndef _GHLIBCPP_MATH
#define _GHLIBCPP_MATH
int abs(int x);
long abs(long x);
double acos(double x);
float acosf(float x);
long double acosl(long double x);
Expand All @@ -15,9 +17,18 @@ long double acoshl(long double x);
double atanh(double x);
float atanhf(float x);
long double atanhl(long double x);
double fmod(double x, double y);
float fmodf(float x, float y);
long double fmodl(long double x, long double y);
double frexp(double x, int *y);
float frexpf(float x, int *y);
long double frexpl(long double, int *);
int ilogb(double x);
int ilogbf(float x);
int ilogbl(long double x);
double lgamma(double x);
float lgammaf(float x);
long double lgammal(long double x);
double log(double x);
float logf(float x);
long double logl(long double x);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
| test.cpp:4:3:4:6 | call to acos | Domain error in call to acos: the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). |
| test.cpp:8:3:8:6 | call to acos | Domain error in call to acos: the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). |
| test.cpp:9:3:9:6 | call to asin | Domain error in call to asin: the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). |
| test.cpp:13:3:13:6 | call to asin | Domain error in call to asin: the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). |
| test.cpp:14:3:14:7 | call to atanh | Domain error in call to atanh: the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). |
| test.cpp:18:3:18:7 | call to atanh | Domain error in call to atanh: the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). |
| test.cpp:19:3:19:7 | call to atan2 | Domain error in call to atan2: both arguments are equal to zero. |
| test.cpp:23:3:23:5 | call to pow | Domain error in call to pow: both arguments are equal to zero. |
| test.cpp:27:3:27:5 | call to pow | Domain error in call to pow: both arguments are less than zero. |
| test.cpp:33:3:33:7 | call to acosh | Domain error in call to acosh: argument is less than 1. |
| test.cpp:34:3:34:7 | call to ilogb | Domain error in call to ilogb: argument is equal to zero. |
| test.cpp:37:3:37:5 | call to log | Domain error in call to log: argument is negative. |
| test.cpp:40:3:40:7 | call to log10 | Domain error in call to log10: argument is negative. |
| test.cpp:43:3:43:6 | call to log2 | Domain error in call to log2: argument is negative. |
| test.cpp:46:3:46:6 | call to sqrt | Domain error in call to sqrt: argument is negative. |
| test.cpp:49:3:49:7 | call to log1p | Domain error in call to log1p: argument is less than 1. |
| test.cpp:52:3:52:6 | call to logb | Domain error in call to logb: argument is equal to zero. |
| test.cpp:55:3:55:8 | call to tgamma | Domain error in call to tgamma: argument is equal to zero. |
| test.cpp:5:3:5:6 | call to acos | Domain error in call to 'acos': the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). |
| test.cpp:9:3:9:6 | call to acos | Domain error in call to 'acos': the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). |
| test.cpp:10:3:10:6 | call to asin | Domain error in call to 'asin': the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). |
| test.cpp:14:3:14:6 | call to asin | Domain error in call to 'asin': the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). |
| test.cpp:15:3:15:7 | call to atanh | Domain error in call to 'atanh': the argument has a range -1.1...-1.1 which is outside the domain of this function (-1.0...1.0). |
| test.cpp:17:3:17:7 | call to atanh | Domain error in call to 'atanh': the argument has a range 1.1...1.1 which is outside the domain of this function (-1.0...1.0). |
| test.cpp:18:3:18:7 | call to atan2 | Domain error in call to 'atan2': both arguments are equal to zero. |
| test.cpp:22:3:22:5 | call to pow | Domain error in call to 'pow': both arguments are equal to zero. |
| test.cpp:26:3:26:5 | call to pow | Domain error in call to 'pow': both arguments are less than zero. |
| test.cpp:31:3:31:7 | call to acosh | Domain error in call to 'acosh': argument is less than 1. |
| test.cpp:32:3:32:7 | call to ilogb | Domain error in call to 'ilogb': argument is equal to zero. |
| test.cpp:35:3:35:5 | call to log | Domain error in call to 'log': argument is negative. |
| test.cpp:37:3:37:7 | call to log10 | Domain error in call to 'log10': argument is negative. |
| test.cpp:39:3:39:6 | call to log2 | Domain error in call to 'log2': argument is negative. |
| test.cpp:41:3:41:6 | call to sqrt | Domain error in call to 'sqrt': argument is negative. |
| test.cpp:44:3:44:7 | call to log1p | Domain error in call to 'log1p': argument is less than 1. |
| test.cpp:46:3:46:6 | call to logb | Domain error in call to 'logb': argument is equal to zero. |
| test.cpp:49:3:49:8 | call to tgamma | Domain error in call to 'tgamma': argument is equal to zero. |
| test.cpp:55:3:55:5 | call to abs | Range error in call to 'abs': argument is most negative number. |
| test.cpp:56:3:56:6 | call to fmod | Domain error in call to 'fmod': y is 0. |
| test.cpp:58:3:58:7 | call to frexp | Unspecified error in call to 'frexp': Arg is Nan or infinity and exp is unspecified as a result. |
| test.cpp:59:3:59:7 | call to frexp | Unspecified error in call to 'frexp': Arg is Nan or infinity and exp is unspecified as a result. |
| test.cpp:63:3:63:7 | call to atanh | Pole error in call to 'atanh': argument is plus or minus 1. |
| test.cpp:64:3:64:7 | call to atanh | Pole error in call to 'atanh': argument is plus or minus 1. |
| test.cpp:65:3:65:5 | call to log | Pole error in call to 'log': argument is equal to zero. |
| test.cpp:66:3:66:7 | call to log10 | Pole error in call to 'log10': argument is equal to zero. |
| test.cpp:67:3:67:6 | call to log2 | Pole error in call to 'log2': argument is equal to zero. |
| test.cpp:68:3:68:7 | call to log1p | Pole error in call to 'log1p': argument is equal to negative one. |
| test.cpp:70:3:70:5 | call to pow | Pole error in call to 'pow': base is zero and exp is negative. |
| test.cpp:71:3:71:8 | call to lgamma | Pole error in call to 'lgamma': argument is equal to zero. |