Skip to content

Commit

Permalink
Merge pull request #572 from knewbury01/knewbury01/fix-193
Browse files Browse the repository at this point in the history
UncheckedRangeDomainPoleErrors: add missing cases
  • Loading branch information
knewbury01 authored Jul 11, 2024
2 parents 3fb6ac2 + aab34fd commit d17443b
Show file tree
Hide file tree
Showing 9 changed files with 450 additions and 283 deletions.
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
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. |
Loading

0 comments on commit d17443b

Please sign in to comment.