From efb286df73c616893cd8b267d85e0d6cdc0c94bb Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 7 Nov 2022 09:57:10 +0000 Subject: [PATCH 01/36] EXP32-C: Add test case --- c/cert/test/rules/EXP32-C/test.c | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 c/cert/test/rules/EXP32-C/test.c diff --git a/c/cert/test/rules/EXP32-C/test.c b/c/cert/test/rules/EXP32-C/test.c new file mode 100644 index 0000000000..d9b07ac84d --- /dev/null +++ b/c/cert/test/rules/EXP32-C/test.c @@ -0,0 +1,38 @@ +volatile int *volatile_f(); + +void test_cast_away_volatile() { + volatile int *l1 = volatile_f(); // COMPLIANT + int *l2 = (int *)l1; // NON_COMPLIANT + int *l3 = (int *)volatile_f(); // NON_COMPLIANT + *l2; // Volatile object is accessed through a non-volatile pointer +} + +void test_volatile_lost_by_assignment() { + static volatile int val = 0; + static int *non_compliant_pointer; + static volatile int **compliant_pointer_to_pointer; + compliant_pointer_to_pointer = &non_compliant_pointer; // NON_COMPLIANT + *compliant_pointer_to_pointer = &val; + *non_compliant_pointer; // Volatile object is accessed through a non-volatile + // pointer +} + +void test_volatile_lost_by_assignment_and_cast() { + static volatile int val = 0; + static int *non_compliant_pointer; + static volatile int **compliant_pointer_to_pointer; + compliant_pointer_to_pointer = + (int **)&non_compliant_pointer; // NON_COMPLIANT + *compliant_pointer_to_pointer = &val; + *non_compliant_pointer; // Volatile object is accessed through a non-volatile + // pointer +} + +void test_volatile_not_lost_by_assignment_and_cast() { + static volatile int val = 0; + static volatile int *compliant_pointer; + static volatile int **compliant_pointer_to_pointer; + compliant_pointer_to_pointer = &compliant_pointer; // COMPLIANT + *compliant_pointer_to_pointer = &val; + *compliant_pointer; // Volatile object is accessed through a volatile pointer +} \ No newline at end of file From ee22665b43cad9b28c16bc02a48ba11835deb5c0 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 7 Nov 2022 09:57:27 +0000 Subject: [PATCH 02/36] EXP36-C: Add test case --- c/cert/test/rules/EXP36-C/test.c | 156 +++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 c/cert/test/rules/EXP36-C/test.c diff --git a/c/cert/test/rules/EXP36-C/test.c b/c/cert/test/rules/EXP36-C/test.c new file mode 100644 index 0000000000..7f4230ffa1 --- /dev/null +++ b/c/cert/test/rules/EXP36-C/test.c @@ -0,0 +1,156 @@ +#include +#include + +void test_direct_cast_alignment() { + char c1 = 1; // assuming 1-byte alignment + (char *)&c1; // COMPLIANT + (short *)&c1; // NON_COMPLIANT + (int *)&c1; // NON_COMPLIANT + (long *)&c1; // NON_COMPLIANT + (float *)&c1; // NON_COMPLIANT + (double *)&c1; // NON_COMPLIANT + + short s1 = 1; // assuming 2-byte alignment + (char *)&s1; // COMPLIANT + (short *)&s1; // COMPLIANT + (int *)&s1; // NON_COMPLIANT + (long *)&s1; // NON_COMPLIANT + (float *)&c1; // NON_COMPLIANT + (double *)&c1; // NON_COMPLIANT + + int i1 = 1; // assuming 4-byte alignment + (char *)&i1; // COMPLIANT + (short *)&i1; // COMPLIANT + (int *)&i1; // COMPLIANT + (float *)&c1; // COMPLIANT + (long *)&i1; // NON_COMPLIANT - assuming 8 byte alignment for longs + (double *)&c1; // NON_COMPLIANT + + float f1 = 1; // assuming 4-byte alignment + (char *)&f1; // COMPLIANT + (short *)&f1; // COMPLIANT + (int *)&f1; // COMPLIANT + (float *)&f1; // COMPLIANT + (long *)&f1; // NON_COMPLIANT + (double *)&f1; // NON_COMPLIANT + + long l1 = 1; // assuming 8-byte alignment + (char *)&l1; // COMPLIANT + (short *)&l1; // COMPLIANT + (int *)&l1; // COMPLIANT + (float *)&c1; // COMPLIANT + (long *)&l1; // COMPLIANT + (double *)&c1; // COMPLIANT + + double d1 = 1; // assuming 8-byte alignment + (char *)&d1; // COMPLIANT + (short *)&d1; // COMPLIANT + (int *)&d1; // COMPLIANT + (float *)&d1; // COMPLIANT + (long *)&d1; // COMPLIANT + (double *)&d1; // COMPLIANT +} + +void custom_aligned_types() { + alignas(int) char c1 = 1; + (char *)&c1; // COMPLIANT + (short *)&c1; // COMPLIANT + (int *)&c1; // COMPLIANT + (float *)&c1; // COMPLIANT + (long *)&c1; // NON_COMPLIANT + (double *)&c1; // NON_COMPLIANT + + alignas(32) char c2 = 1; + (char *)&c2; // COMPLIANT + (short *)&c2; // COMPLIANT + (int *)&c2; // COMPLIANT + (float *)&c2; // COMPLIANT + (long *)&c2; // NON_COMPLIANT + (double *)&c2; // NON_COMPLIANT +} + +void test_via_void_direct() { + char c1 = 1; + void *v1 = &c1; + (char *)v1; // COMPLIANT + (short *)v1; // NON_COMPLIANT + (int *)v1; // NON_COMPLIANT + (float *)v1; // NON_COMPLIANT + (long *)v1; // NON_COMPLIANT + (double *)v1; // NON_COMPLIANT + + short s1 = 1; + void *v2 = &s1; + (char *)v2; // COMPLIANT + (short *)v2; // COMPLIANT + (int *)v2; // NON_COMPLIANT + (float *)v2; // NON_COMPLIANT + (long *)v2; // NON_COMPLIANT + (double *)v2; // NON_COMPLIANT + + int i1 = 1; + void *v3 = &i1; + (char *)v3; // COMPLIANT + (short *)v3; // COMPLIANT + (int *)v3; // COMPLIAN + (float *)v3; // COMPLIANT + (long *)v3; // NON_COMPLIANT - assuming 8 byte alignment for longs + (double *)v3; // NON_COMPLIANT - but only on x64 + + float f1 = 1; + void *v4 = &f1; + (char *)v4; // COMPLIANT + (short *)v4; // COMPLIANT + (int *)v4; // COMPLIANT + (float *)v4; // COMPLIANT + (long *)v4; // NON_COMPLIANT - assuming 8 byte alignment for longs + (double *)v4; // NON_COMPLIANT + + long l1 = 1; + void *v5 = &l1; + (char *)v5; // COMPLIANT + (short *)v5; // COMPLIANT + (int *)v5; // COMPLIANT + (float *)v5; // COMPLIANT + (long *)v5; // COMPLIANT + (double *)v5; // COMPLIANT + + double d1 = 1; + void *v6 = &d1; + (char *)v6; // COMPLIANT + (short *)v6; // COMPLIANT + (int *)v6; // COMPLIANT + (float *)v6; // COMPLIANT + (long *)v6; // COMPLIANT + (double *)v6; // COMPLIANT +} + +int *cast_away(void *v) { + return (int *)v; // compliance depends on context +} + +void test_via_void_indirect() { + char c1 = 1; + cast_away((void *)c1); // NON_COMPLIANT + + int i1 = 1; + cast_away((void *)i1); // COMPLIANT +} + +struct S1 { + char c1; + unsigned char data[8]; +}; + +struct S2 { + char c1; + alignas(size_t) unsigned char data[8]; +}; + +void test_struct_alignment() { + S1 s1; + (size_t *)&s1.data; // NON_COMPLIANT + + S2 s2; + (size_t *)&s2.data; // COMPLIANT +} \ No newline at end of file From 94347c4210f0b4a7bed8f6fa5a3ca798d00add3e Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 7 Nov 2022 11:08:52 +0000 Subject: [PATCH 03/36] EXP43-C: Add test case --- c/cert/test/rules/EXP39-C/test.c | 81 ++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 c/cert/test/rules/EXP39-C/test.c diff --git a/c/cert/test/rules/EXP39-C/test.c b/c/cert/test/rules/EXP39-C/test.c new file mode 100644 index 0000000000..c4ccdb45e0 --- /dev/null +++ b/c/cert/test/rules/EXP39-C/test.c @@ -0,0 +1,81 @@ +void test_incompatible_arithmetic() { + float f = 0.0f; + int *p = (int *)&f; // NON_COMPLIANT - arithmetic types are not compatible + // with each other + (*p)++; + + short s[2]; + (int *)&s; // NON_COMPLIANT + + (short(*)[4]) & s; // NON_COMPLIANT - array of size 2 is not compatible with + // array of size 4 (n1570 6.7.6.2 paragraph 7) + (short(*)[2]) & s; // COMPLIANT + + // char may be signed or unsigned, and so is not compatible with either + char c1; + (signed char *)&c1; // NON_COMPLIANT + (unsigned char *)&c1; // NON_COMPLIANT + (char *)&c1; // NON_COMPLIANT + + // int is defined as signed, so is compatible with all the signed versions + // (long, short etc. are similar) + int i1; + (signed int *)&i1; // COMPLIANT + (int *)&i1; // COMPLIANT + (signed *)&i1; // COMPLIANT + (unsigned int *)&i1; // NON_COMPLIANT + (const int *)&i1; // NON_COMPLIANT +} + +struct { + int a; +} * s1; +struct { + int a; +} * s2; +struct S1 { + int a; +} * s3; +struct S1 *s4; + +// TODO test across files +void test_incompatible_structs() { + // s1 and s2 do not have tags, and are therefore not compatible + s1 = s2; // NON_COMPLIANT + // s3 tag is inconsistent with s1 tag + s1 = s3; // NON_COMPLIANT + s3 = s1; // NON_COMPLIANT + // s4 tag is consistent with s3 tag + s3 = s4; // COMPLIANT + s4 = s3; // COMPLIANT +} + +enum E1 { E1A, E1B }; +enum E2 { E2A, E2B }; + +void test_enums() { + enum E1 e1 = E1A; + enum E2 e2 = e1; // COMPLIANT + // Enums are also compatible with one of `char`, a signed integer type or an + // unsigned integer type. It is implementation defined which is used, so + // choose an appropriate type below for this test + (int *)&e1; // COMPLIANT +} + +int *void_cast(void *v) { return (int *)v; } + +void test_indirect_cast() { + float f1 = 0.0f; + void_cast(&f1); // NON_COMPLIANT + int i1 = 0; + void_cast(&i1); // COMPLIANT +} + +signed f(int y) { return y; } +int g(signed int x) { return x; } + +// 6.7.6.3 p15 +void test_compatible_functions() { + signed (*f1)(int) = &g; // COMPLIANT + int (*g1)(signed int) = &f; // COMPLIANT +} \ No newline at end of file From 741ab39798d4945504dfede824c40dd51b9515f9 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 7 Nov 2022 11:12:55 +0000 Subject: [PATCH 04/36] EXP39-C: Add malloc/realloc test --- c/cert/test/rules/EXP39-C/test.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/c/cert/test/rules/EXP39-C/test.c b/c/cert/test/rules/EXP39-C/test.c index c4ccdb45e0..1b67ec028e 100644 --- a/c/cert/test/rules/EXP39-C/test.c +++ b/c/cert/test/rules/EXP39-C/test.c @@ -78,4 +78,22 @@ int g(signed int x) { return x; } void test_compatible_functions() { signed (*f1)(int) = &g; // COMPLIANT int (*g1)(signed int) = &f; // COMPLIANT +} + +struct S2 { + int a; + int b; +}; + +struct S3 { + int a; + int b; +}; + +void test_realloc() { + struct S2 *s2 = (struct S2 *)malloc(sizeof(struct S2)); + struct S3 *s3 = (struct S3 *)realloc(s2, sizeof(struct S3)); + s3->a; // NON_COMPLIANT + memset(s3, 0, sizeof(struct S3)); + s3->a; // COMPLIANT } \ No newline at end of file From 84628447c3ff51d30ab4a6b28f731b94f9d917e4 Mon Sep 17 00:00:00 2001 From: Luke Cartey Date: Mon, 7 Nov 2022 11:48:06 +0000 Subject: [PATCH 05/36] EXP43-C: Add test cases --- c/cert/test/rules/EXP43-C/test.c | 76 ++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 c/cert/test/rules/EXP43-C/test.c diff --git a/c/cert/test/rules/EXP43-C/test.c b/c/cert/test/rules/EXP43-C/test.c new file mode 100644 index 0000000000..8527f7ce84 --- /dev/null +++ b/c/cert/test/rules/EXP43-C/test.c @@ -0,0 +1,76 @@ +#include +#include + +int *restrict g1; +int *restrict g2; + +void test_global_local() { + int *restrict i1 = g1; // COMPLIANT + int *restrict i2 = g2; // COMPLIANT + int *restrict i3 = i2; // NON_COMPLIANT + g1 = g2; // NON_COMPLIANT + i1 = i2; // NON_COMPLIANT +} + +void copy(int *restrict p1, int *restrict p2, size_t s) { + for (size_t i = 0; i < s; ++i) { + p2[i] = p1[i]; + } +} + +void test_restrict_params() { + int i1 = 1; + int i2 = 2; + copy(&i1, &i1, 1); // NON_COMPLIANT + copy(&i1, &i2, 1); // COMPLIANT + + int x[10]; + copy(x[0], x[1], 1); // COMPLIANT - non overlapping + copy(x[0], x[1], 2); // NON_COMPLIANT - overlapping +} + +void test_strcpy() { + char s1[] = "my test string"; + char s2[] = "my other string"; + strcpy(&s1, &s1 + 3); // NON_COMPLIANT + strcpy(&s2, &s1); // COMPLIANT +} + +void test_strcpy_s() { + char s1[] = "my test string"; + char s2[] = "my other string"; + strcpy_s(&s1, &s1 + 3); // NON_COMPLIANT + strcpy_s(&s2, sizeof(s2), &s1); // COMPLIANT +} + +void test_memcpy() { + char s1[] = "my test string"; + char s2[] = "my other string"; + memcpy(&s1, &s1 + 3, 5); // NON_COMPLIANT + memcpy(&s2, &s1 + 3, 5); // COMPLIANT +} + +void test_memcpy_s() { + char s1[] = "my test string"; + char s2[] = "my other string"; + memcpy_s(&s1, sizeof(s1), &s1 + 3, 5); // NON_COMPLIANT + memcpy_s(&s2, sizeof(s2), &s1 + 3, 5); // COMPLIANT +} + +void test_memmove() { + char s1[] = "my test string"; + char s2[] = "my other string"; + memmove(&s1, &s1 + 3, 5); // COMPLIANT + memmove(&s2, &s1 + 3, 5); // COMPLIANT +} + +void test_scanf() { + char s1[200] = "%10s"; + scanf(&s2, &s2 + 4); // NON_COMPLIANT +} + +// TODO also consider the following: +// strncpy(), strncpy_s() +// strcat(), strcat_s() +// strncat(), strncat_s() +// strtok_s() \ No newline at end of file From e0e4181ce6e2c429d082c991ec1b52cc62069ff9 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Wed, 16 Nov 2022 16:21:01 +0100 Subject: [PATCH 06/36] Add Pointers3 package Add rule definition and package files Add Pointers3 to package list in tasks.json --- .vscode/tasks.json | 1 + .../cpp/exclusions/c/Pointers3.qll | 74 +++++++++++++++++ .../cpp/exclusions/c/RuleMetadata.qll | 3 + rule_packages/c/Pointers3.json | 80 +++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll create mode 100644 rule_packages/c/Pointers3.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json index d1f141cced..2e2941222b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -233,6 +233,7 @@ "Pointers", "Pointers1", "Pointers2", + "Pointers3", "Scope", "SideEffects1", "SideEffects2", diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll new file mode 100644 index 0000000000..9c4741f620 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll @@ -0,0 +1,74 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Pointers3Query = + TDoNotAccessVolatileObjectWithNonVolatileReferenceQuery() or + TDoNotCastPointerToMoreStrictlyAlignedPointerTypeQuery() or + TDoNotAccessVariableViaPointerOfIncompatibleTypeQuery() or + TUndefinedBehaviorWithRestrictQualifiedPointersQuery() + +predicate isPointers3QueryMetadata(Query query, string queryId, string ruleId) { + query = + // `Query` instance for the `doNotAccessVolatileObjectWithNonVolatileReference` query + Pointers3Package::doNotAccessVolatileObjectWithNonVolatileReferenceQuery() and + queryId = + // `@id` for the `doNotAccessVolatileObjectWithNonVolatileReference` query + "c/cert/do-not-access-volatile-object-with-non-volatile-reference" and + ruleId = "EXP32-C" + or + query = + // `Query` instance for the `doNotCastPointerToMoreStrictlyAlignedPointerType` query + Pointers3Package::doNotCastPointerToMoreStrictlyAlignedPointerTypeQuery() and + queryId = + // `@id` for the `doNotCastPointerToMoreStrictlyAlignedPointerType` query + "c/cert/do-not-cast-pointer-to-more-strictly-aligned-pointer-type" and + ruleId = "EXP36-C" + or + query = + // `Query` instance for the `doNotAccessVariableViaPointerOfIncompatibleType` query + Pointers3Package::doNotAccessVariableViaPointerOfIncompatibleTypeQuery() and + queryId = + // `@id` for the `doNotAccessVariableViaPointerOfIncompatibleType` query + "c/cert/do-not-access-variable-via-pointer-of-incompatible-type" and + ruleId = "EXP39-C" + or + query = + // `Query` instance for the `undefinedBehaviorWithRestrictQualifiedPointers` query + Pointers3Package::undefinedBehaviorWithRestrictQualifiedPointersQuery() and + queryId = + // `@id` for the `undefinedBehaviorWithRestrictQualifiedPointers` query + "c/cert/undefined-behavior-with-restrict-qualified-pointers" and + ruleId = "EXP43-C" +} + +module Pointers3Package { + Query doNotAccessVolatileObjectWithNonVolatileReferenceQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotAccessVolatileObjectWithNonVolatileReference` query + TQueryC(TPointers3PackageQuery(TDoNotAccessVolatileObjectWithNonVolatileReferenceQuery())) + } + + Query doNotCastPointerToMoreStrictlyAlignedPointerTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotCastPointerToMoreStrictlyAlignedPointerType` query + TQueryC(TPointers3PackageQuery(TDoNotCastPointerToMoreStrictlyAlignedPointerTypeQuery())) + } + + Query doNotAccessVariableViaPointerOfIncompatibleTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotAccessVariableViaPointerOfIncompatibleType` query + TQueryC(TPointers3PackageQuery(TDoNotAccessVariableViaPointerOfIncompatibleTypeQuery())) + } + + Query undefinedBehaviorWithRestrictQualifiedPointersQuery() { + //autogenerate `Query` type + result = + // `Query` type for `undefinedBehaviorWithRestrictQualifiedPointers` query + TQueryC(TPointers3PackageQuery(TUndefinedBehaviorWithRestrictQualifiedPointersQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index ad05d9b737..1a0ac56434 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -24,6 +24,7 @@ import Language1 import Misc import Pointers1 import Pointers2 +import Pointers3 import Preprocessor1 import Preprocessor2 import Preprocessor3 @@ -60,6 +61,7 @@ newtype TCQuery = TMiscPackageQuery(MiscQuery q) or TPointers1PackageQuery(Pointers1Query q) or TPointers2PackageQuery(Pointers2Query q) or + TPointers3PackageQuery(Pointers3Query q) or TPreprocessor1PackageQuery(Preprocessor1Query q) or TPreprocessor2PackageQuery(Preprocessor2Query q) or TPreprocessor3PackageQuery(Preprocessor3Query q) or @@ -96,6 +98,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId) { isMiscQueryMetadata(query, queryId, ruleId) or isPointers1QueryMetadata(query, queryId, ruleId) or isPointers2QueryMetadata(query, queryId, ruleId) or + isPointers3QueryMetadata(query, queryId, ruleId) or isPreprocessor1QueryMetadata(query, queryId, ruleId) or isPreprocessor2QueryMetadata(query, queryId, ruleId) or isPreprocessor3QueryMetadata(query, queryId, ruleId) or diff --git a/rule_packages/c/Pointers3.json b/rule_packages/c/Pointers3.json new file mode 100644 index 0000000000..e395543690 --- /dev/null +++ b/rule_packages/c/Pointers3.json @@ -0,0 +1,80 @@ +{ + "CERT-C": { + "EXP32-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "If an an object defined with a volatile-qualified type is referred to with an lvalue of a non-volatile-qualified type, the behavior is undefined.", + "kind": "problem", + "name": "Do not access a volatile object through a nonvolatile reference", + "precision": "high", + "severity": "error", + "short_name": "DoNotAccessVolatileObjectWithNonVolatileReference", + "tags": [ + "correctness" + ] + } + ], + "title": "Do not access a volatile object through a nonvolatile reference" + }, + "EXP36-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Converting a pointer to a different type results in undefined behavior if the pointer is not correctly aligned for the new type.", + "kind": "path-problem", + "name": "Do not cast pointers into more strictly aligned pointer types", + "precision": "high", + "severity": "error", + "short_name": "DoNotCastPointerToMoreStrictlyAlignedPointerType", + "tags": [ + "correctness" + ] + } + ], + "title": "Do not cast pointers into more strictly aligned pointer types" + }, + "EXP39-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Modifying underlying pointer data through a pointer of an incompatible type can lead to unpredictable results.", + "kind": "problem", + "name": "Do not access a variable through a pointer of an incompatible type", + "precision": "very-high", + "severity": "error", + "short_name": "DoNotAccessVariableViaPointerOfIncompatibleType", + "tags": [ + "correctness" + ] + } + ], + "title": "Do not access a variable through a pointer of an incompatible type" + }, + "EXP43-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "", + "kind": "problem", + "name": "Avoid undefined behavior when using restrict-qualified pointers", + "precision": "very-high", + "severity": "error", + "short_name": "UndefinedBehaviorWithRestrictQualifiedPointers", + "tags": [ + "correctness" + ] + } + ], + "title": "Avoid undefined behavior when using restrict-qualified pointers" + } + } +} \ No newline at end of file From 1bfa1b125a6a7bc3de43f5f475731ec5f012dc2d Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Wed, 16 Nov 2022 16:22:23 +0100 Subject: [PATCH 07/36] Fix RULE-11-7 query output message The previous message referenced a "pointer to void type" rather than "pointer to object type" as specified by the query. --- .../CastBetweenPointerToObjectAndNonIntArithmeticType.ql | 2 +- ...stBetweenPointerToObjectAndNonIntArithmeticType.expected | 6 +++--- change_notes/2022-11-14-fix-RULE-11-7-message.md | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 change_notes/2022-11-14-fix-RULE-11-7-message.md diff --git a/c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql b/c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql index 2aa49ae2a0..3be7644b9d 100644 --- a/c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql +++ b/c/misra/src/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.ql @@ -32,4 +32,4 @@ where [typeFrom, typeTo] instanceof MisraNonIntegerArithmeticType and [typeFrom, typeTo] instanceof PointerToObjectType select cast, - "Cast performed between a pointer to void type and a non-integer arithmetic type." \ No newline at end of file + "Cast performed between a pointer to object type and a non-integer arithmetic type." \ No newline at end of file diff --git a/c/misra/test/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.expected b/c/misra/test/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.expected index 0b96b3c747..133e568499 100644 --- a/c/misra/test/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.expected +++ b/c/misra/test/rules/RULE-11-7/CastBetweenPointerToObjectAndNonIntArithmeticType.expected @@ -1,3 +1,3 @@ -| test.c:5:13:5:20 | (bool)... | Cast performed between a pointer to void type and a non-integer arithmetic type. | -| test.c:7:21:7:28 | (bool)... | Cast performed between a pointer to void type and a non-integer arithmetic type. | -| test.c:8:8:8:16 | (int *)... | Cast performed between a pointer to void type and a non-integer arithmetic type. | +| test.c:5:13:5:20 | (bool)... | Cast performed between a pointer to object type and a non-integer arithmetic type. | +| test.c:7:21:7:28 | (bool)... | Cast performed between a pointer to object type and a non-integer arithmetic type. | +| test.c:8:8:8:16 | (int *)... | Cast performed between a pointer to object type and a non-integer arithmetic type. | diff --git a/change_notes/2022-11-14-fix-RULE-11-7-message.md b/change_notes/2022-11-14-fix-RULE-11-7-message.md new file mode 100644 index 0000000000..f1cb253f3b --- /dev/null +++ b/change_notes/2022-11-14-fix-RULE-11-7-message.md @@ -0,0 +1,2 @@ + - `RULE-11-7` - `CastBetweenPointerToObjectAndNonIntArithmeticType.ql` + - Corrected the query output message to describe a cast involving a pointer to an object rather than a void pointer. \ No newline at end of file From 833d834a4aee13bfd01a3c25471f83e89f92292a Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Wed, 16 Nov 2022 16:24:18 +0100 Subject: [PATCH 08/36] Implement EXP32-C and EXP36-C Additionally updated the `EXP36-C` test-case and refactored `MEM57-CPP` to move certain classes and predicates to the `Alignment.qll` library for re-use in `EXP36-C`. --- ...sVolatileObjectWithNonVolatileReference.md | 18 ++ ...sVolatileObjectWithNonVolatileReference.ql | 65 ++++ ...PointerToMoreStrictlyAlignedPointerType.md | 18 ++ ...PointerToMoreStrictlyAlignedPointerType.ql | 170 +++++++++++ ...ileObjectWithNonVolatileReference.expected | 4 + ...latileObjectWithNonVolatileReference.qlref | 1 + ...rToMoreStrictlyAlignedPointerType.expected | 282 ++++++++++++++++++ ...nterToMoreStrictlyAlignedPointerType.qlref | 1 + c/cert/test/rules/EXP36-C/test.c | 101 ++++++- ...ngDefaultOperatorNewForOverAlignedTypes.ql | 21 +- .../src/codingstandards/cpp/Alignment.qll | 25 ++ 11 files changed, 672 insertions(+), 34 deletions(-) create mode 100644 c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md create mode 100644 c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql create mode 100644 c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md create mode 100644 c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql create mode 100644 c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.expected create mode 100644 c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.qlref create mode 100644 c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected create mode 100644 c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.qlref create mode 100644 cpp/common/src/codingstandards/cpp/Alignment.qll diff --git a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md new file mode 100644 index 0000000000..449644423b --- /dev/null +++ b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md @@ -0,0 +1,18 @@ +# EXP32-C: Do not access a volatile object through a nonvolatile reference + +This query implements the CERT-C rule EXP32-C: + +> Do not access a volatile object through a nonvolatile reference + + +## CERT + +** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` ** + +## Implementation notes + +None + +## References + +* CERT-C: [EXP32-C: Do not access a volatile object through a nonvolatile reference](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql new file mode 100644 index 0000000000..fe6acfb44b --- /dev/null +++ b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql @@ -0,0 +1,65 @@ +/** + * @id c/cert/do-not-access-volatile-object-with-non-volatile-reference + * @name EXP32-C: Do not access a volatile object through a nonvolatile reference + * @description If an an object defined with a volatile-qualified type is referred to with an lvalue + * of a non-volatile-qualified type, the behavior is undefined. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/exp32-c + * correctness + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert + +/** + * A `Cast` which converts from a pointer to a volatile-qualified type + * to a pointer to a non-volatile-qualified type. + */ +class CastFromVolatileToNonVolatileBaseType extends Cast { + CastFromVolatileToNonVolatileBaseType() { + this.getExpr().getType().(PointerType).getBaseType*().isVolatile() and + this.getActualType() instanceof PointerType and + not this.getActualType().(PointerType).getBaseType*().isVolatile() + } +} + +/** + * An `AssignExpr` with an *lvalue* that is a pointer to a volatile base type and + * and *rvalue* that is not also a pointer to a volatile base type. + */ +class NonVolatileObjectAssignedToVolatilePointer extends AssignExpr { + NonVolatileObjectAssignedToVolatilePointer() { + this.getLValue().getType().(DerivedType).getBaseType*().isVolatile() and + not this.getRValue().getUnconverted().getType().(DerivedType).getBaseType*().isVolatile() + } + + /** + * All `VariableAccess` expressions which are transitive successors of + * this `Expr` and which access the variable accessed in the *rvalue* of this `Expr` + */ + Expr getASubsequentAccessOfAssignedObject() { + result = + any(VariableAccess va | + va = this.getRValue().getAChild*().(VariableAccess).getTarget().getAnAccess() and + this.getASuccessor+() = va + | + va + ) + } +} + +from Expr e, string message +where + not isExcluded(e, Pointers3Package::doNotAccessVolatileObjectWithNonVolatileReferenceQuery()) and + ( + e instanceof CastFromVolatileToNonVolatileBaseType and + message = "Cast of object with a volatile-qualified type to a non-volatile-qualified type." + or + exists(e.(NonVolatileObjectAssignedToVolatilePointer).getASubsequentAccessOfAssignedObject()) and + message = + "Non-volatile object referenced via pointer to volatile type and later accessed via its original object of a non-volatile-qualified type." + ) +select e, message diff --git a/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md new file mode 100644 index 0000000000..870ae704aa --- /dev/null +++ b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md @@ -0,0 +1,18 @@ +# EXP36-C: Do not cast pointers into more strictly aligned pointer types + +This query implements the CERT-C rule EXP36-C: + +> Do not cast pointers into more strictly aligned pointer types + + +## CERT + +** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` ** + +## Implementation notes + +None + +## References + +* CERT-C: [EXP36-C: Do not cast pointers into more strictly aligned pointer types](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql new file mode 100644 index 0000000000..b1c2b6e305 --- /dev/null +++ b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql @@ -0,0 +1,170 @@ +/** + * @id c/cert/do-not-cast-pointer-to-more-strictly-aligned-pointer-type + * @name EXP36-C: Do not cast pointers into more strictly aligned pointer types + * @description Converting a pointer to a different type results in undefined behavior if the + * pointer is not correctly aligned for the new type. + * @kind path-problem + * @precision high + * @problem.severity error + * @tags external/cert/id/exp36-c + * correctness + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert +import codingstandards.cpp.Alignment +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +import DataFlow::PathGraph + +/** + * An expression with a type that has defined alignment requirements + */ +abstract class ExprWithAlignment extends Expr { + /** + * Gets the alignment requirements in bytes for the underlying `Expr` + */ + abstract int getAlignment(); + + /** + * Gets a descriptive string describing the type of expression + */ + abstract string getKind(); +} + +/** + * A class extending `AddressOfExpr` and `ExprWithAlignment` to reason about the + * alignment of base types addressed with C address-of expressions + */ +class AddressOfAlignedVariableExpr extends AddressOfExpr, ExprWithAlignment { + AddressOfAlignedVariableExpr() { this.getAddressable() instanceof Variable } + + AlignAs alignAsAttribute() { result = this.getAddressable().(Variable).getAnAttribute() } + + override int getAlignment() { + result = alignAsAttribute().getArgument(0).getValueInt() + or + result = alignAsAttribute().getArgument(0).getValueType().getSize() + or + not exists(alignAsAttribute()) and + result = this.getAddressable().(Variable).getType().getAlignment() + } + + override string getKind() { result = "address-of expression" } +} + +/** + * A class extending `FunctionCall` and `ExprWithAlignment` to reason about the + * alignment of pointers allocated with calls to C standard library allocation functions + */ +class DefinedAlignmentAllocationExpr extends FunctionCall, ExprWithAlignment { + int alignment; + + DefinedAlignmentAllocationExpr() { + this.getTarget().getName() = "aligned_alloc" and + lowerBound(this.getArgument(0)) = upperBound(this.getArgument(0)) and + alignment = upperBound(this.getArgument(0)) + or + this.getTarget().getName() = ["malloc", "calloc", "realloc"] and + alignment = getGlobalMaxAlignT() + } + + override int getAlignment() { result = alignment } + + override string getKind() { result = "call to " + this.getTarget().getName() } +} + +/** + * A class extending `VariableAccess` and `ExprWithAlignment` to reason about the + * alignment of pointers accessed based solely on the pointers' base types. + */ +class DefaultAlignedPointerAccessExpr extends VariableAccess, ExprWithAlignment { + DefaultAlignedPointerAccessExpr() { + this.getTarget().getUnspecifiedType() instanceof PointerType and + not this.getTarget().getUnspecifiedType() instanceof VoidPointerType + } + + override int getAlignment() { + result = this.getTarget().getType().(PointerType).getBaseType().getAlignment() + } + + override string getKind() { result = "pointer base type" } +} + +/** + * A data-flow configuration for analysing the flow of `ExprWithAlignment` pointer expressions + * to casts which perform pointer type conversions and potentially create pointer alignment issues. + */ +class ExprWithAlignmentToCStyleCastConfiguration extends DataFlow::Configuration { + ExprWithAlignmentToCStyleCastConfiguration() { + this = "ExprWithAlignmentToCStyleCastConfiguration" + } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof ExprWithAlignment + } + + override predicate isSink(DataFlow::Node sink) { + exists(CStyleCast cast | + cast.getUnderlyingType() instanceof PointerType and + cast.getUnconverted() = sink.asExpr() + ) + } +} + +/** + * A data-flow configuration for tracking flow from `AddressOfExpr` which provide + * most reliable or explicitly defined alignment information to the less reliable + * `DefaultAlignedPointerAccessExpr` expressions. + * + * This data-flow configuration is used + * to exclude an `DefaultAlignedPointerAccessExpr` as a source if a preceding source + * defined by this configuration provides more accurate alignment information. + */ +class AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig extends DataFlow::Configuration { + AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig() { + this = "AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig" + } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof AddressOfAlignedVariableExpr or + source.asExpr() instanceof DefinedAlignmentAllocationExpr + } + + override predicate isSink(DataFlow::Node sink) { + sink.asExpr() instanceof DefaultAlignedPointerAccessExpr + } +} + +from + DataFlow::PathNode source, DataFlow::PathNode sink, ExprWithAlignment expr, CStyleCast cast, + Type toBaseType, int alignmentFrom, int alignmentTo +where + not isExcluded(cast, Pointers3Package::doNotCastPointerToMoreStrictlyAlignedPointerTypeQuery()) and + any(ExprWithAlignmentToCStyleCastConfiguration config).hasFlowPath(source, sink) and + source.getNode().asExpr() = expr and + sink.getNode().asExpr() = cast.getUnconverted() and + ( + // possibility 1: the source node (ExprWithAlignment) is NOT a DefaultAlignedPointerAccessExpr + // meaning that its alignment info is accurate regardless of any preceding ExprWithAlignment nodes + expr instanceof DefaultAlignedPointerAccessExpr + implies + ( + // possibility 2: the source node (ExprWithAlignment) IS a DefaultAlignedPointerAccessExpr + // meaning that its alignment info is only accurate if no preceding ExprWithAlignment nodes exist + not any(AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig config) + .hasFlowTo(source.getNode()) and + expr instanceof DefaultAlignedPointerAccessExpr and + cast.getUnconverted() instanceof VariableAccess + ) + ) and + toBaseType = cast.getActualType().(PointerType).getBaseType() and + alignmentTo = toBaseType.getAlignment() and + alignmentFrom = expr.getAlignment() and + // only flag cases where the cast's target type has stricter alignment requirements than the source + alignmentFrom < alignmentTo +select sink, source, sink, + "Cast from pointer with " + alignmentFrom + + "-byte alignment (defined by $@) to pointer with base type " + toBaseType.getUnderlyingType() + + " with " + alignmentTo + "-byte alignment.", expr.getUnconverted(), expr.getKind() diff --git a/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.expected b/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.expected new file mode 100644 index 0000000000..d8fd01bbae --- /dev/null +++ b/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.expected @@ -0,0 +1,4 @@ +| test.c:5:13:5:21 | (int *)... | Cast of object with a volatile-qualified type to a non-volatile-qualified type. | +| test.c:6:13:6:31 | (int *)... | Cast of object with a volatile-qualified type to a non-volatile-qualified type. | +| test.c:14:3:14:55 | ... = ... | Non-volatile object referenced via pointer to volatile type and later accessed via its original object of a non-volatile-qualified type. | +| test.c:24:3:25:36 | ... = ... | Non-volatile object referenced via pointer to volatile type and later accessed via its original object of a non-volatile-qualified type. | diff --git a/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.qlref b/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.qlref new file mode 100644 index 0000000000..90635c935e --- /dev/null +++ b/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.qlref @@ -0,0 +1 @@ +rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected b/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected new file mode 100644 index 0000000000..e523be08fb --- /dev/null +++ b/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected @@ -0,0 +1,282 @@ +edges +| test.c:75:14:75:16 | & ... | test.c:76:11:76:12 | v1 | +| test.c:75:14:75:16 | & ... | test.c:77:12:77:13 | v1 | +| test.c:75:14:75:16 | & ... | test.c:78:10:78:11 | v1 | +| test.c:75:14:75:16 | & ... | test.c:79:12:79:13 | v1 | +| test.c:75:14:75:16 | & ... | test.c:80:11:80:12 | v1 | +| test.c:75:14:75:16 | & ... | test.c:81:13:81:14 | v1 | +| test.c:84:14:84:16 | & ... | test.c:85:11:85:12 | v2 | +| test.c:84:14:84:16 | & ... | test.c:86:12:86:13 | v2 | +| test.c:84:14:84:16 | & ... | test.c:87:10:87:11 | v2 | +| test.c:84:14:84:16 | & ... | test.c:88:12:88:13 | v2 | +| test.c:84:14:84:16 | & ... | test.c:89:11:89:12 | v2 | +| test.c:84:14:84:16 | & ... | test.c:90:13:90:14 | v2 | +| test.c:93:14:93:16 | & ... | test.c:94:11:94:12 | v3 | +| test.c:93:14:93:16 | & ... | test.c:95:12:95:13 | v3 | +| test.c:93:14:93:16 | & ... | test.c:96:10:96:11 | v3 | +| test.c:93:14:93:16 | & ... | test.c:97:12:97:13 | v3 | +| test.c:93:14:93:16 | & ... | test.c:98:11:98:12 | v3 | +| test.c:93:14:93:16 | & ... | test.c:99:13:99:14 | v3 | +| test.c:102:14:102:16 | & ... | test.c:103:11:103:12 | v4 | +| test.c:102:14:102:16 | & ... | test.c:104:12:104:13 | v4 | +| test.c:102:14:102:16 | & ... | test.c:105:10:105:11 | v4 | +| test.c:102:14:102:16 | & ... | test.c:106:12:106:13 | v4 | +| test.c:102:14:102:16 | & ... | test.c:107:11:107:12 | v4 | +| test.c:102:14:102:16 | & ... | test.c:108:13:108:14 | v4 | +| test.c:111:14:111:16 | & ... | test.c:112:11:112:12 | v5 | +| test.c:111:14:111:16 | & ... | test.c:113:12:113:13 | v5 | +| test.c:111:14:111:16 | & ... | test.c:114:10:114:11 | v5 | +| test.c:111:14:111:16 | & ... | test.c:115:12:115:13 | v5 | +| test.c:111:14:111:16 | & ... | test.c:116:11:116:12 | v5 | +| test.c:111:14:111:16 | & ... | test.c:117:13:117:14 | v5 | +| test.c:120:14:120:16 | & ... | test.c:121:11:121:12 | v6 | +| test.c:120:14:120:16 | & ... | test.c:122:12:122:13 | v6 | +| test.c:120:14:120:16 | & ... | test.c:123:10:123:11 | v6 | +| test.c:120:14:120:16 | & ... | test.c:124:12:124:13 | v6 | +| test.c:120:14:120:16 | & ... | test.c:125:11:125:12 | v6 | +| test.c:120:14:120:16 | & ... | test.c:126:13:126:14 | v6 | +| test.c:129:22:129:22 | v | test.c:129:22:129:22 | v | +| test.c:129:22:129:22 | v | test.c:130:17:130:17 | v | +| test.c:135:21:135:23 | & ... | test.c:129:22:129:22 | v | +| test.c:138:21:138:23 | & ... | test.c:129:22:129:22 | v | +| test.c:166:24:166:29 | call to malloc | test.c:167:13:167:15 | & ... | +| test.c:166:24:166:29 | call to malloc | test.c:167:14:167:15 | s1 | +| test.c:166:24:166:29 | call to malloc | test.c:168:16:168:18 | & ... | +| test.c:166:24:166:29 | call to malloc | test.c:168:17:168:18 | s1 | +| test.c:166:24:166:29 | call to malloc | test.c:169:13:169:14 | s1 | +| test.c:166:24:166:29 | call to malloc | test.c:169:13:169:14 | s1 | +| test.c:166:24:166:29 | call to malloc | test.c:169:13:169:14 | s1 | +| test.c:166:24:166:29 | call to malloc | test.c:169:13:169:14 | s1 | +| test.c:166:24:166:29 | call to malloc | test.c:171:15:171:16 | s1 | +| test.c:166:24:166:29 | call to malloc | test.c:176:16:176:17 | s1 | +| test.c:167:14:167:15 | s1 | test.c:167:13:167:15 | & ... | +| test.c:168:17:168:18 | s1 | test.c:168:16:168:18 | & ... | +| test.c:169:13:169:14 | ref arg s1 | test.c:171:15:171:16 | s1 | +| test.c:169:13:169:14 | ref arg s1 | test.c:176:16:176:17 | s1 | +| test.c:169:13:169:14 | s1 | test.c:129:22:129:22 | v | +| test.c:169:13:169:14 | s1 | test.c:129:22:129:22 | v | +| test.c:169:13:169:14 | s1 | test.c:169:13:169:14 | ref arg s1 | +| test.c:172:14:172:15 | s2 | test.c:172:13:172:15 | & ... | +| test.c:173:12:173:13 | s2 | test.c:173:11:173:13 | & ... | +| test.c:174:13:174:14 | s2 | test.c:129:22:129:22 | v | +| test.c:177:14:177:15 | s3 | test.c:177:13:177:15 | & ... | +| test.c:178:12:178:13 | s3 | test.c:178:11:178:13 | & ... | +| test.c:179:13:179:14 | s3 | test.c:129:22:129:22 | v | +| test.c:183:14:183:26 | call to aligned_alloc | test.c:184:11:184:12 | v1 | +| test.c:183:14:183:26 | call to aligned_alloc | test.c:185:10:185:11 | v1 | +| test.c:183:14:183:26 | call to aligned_alloc | test.c:186:13:186:14 | v1 | +| test.c:183:14:183:26 | call to aligned_alloc | test.c:187:13:187:14 | v1 | +| test.c:187:13:187:14 | v1 | test.c:129:22:129:22 | v | +| test.c:189:14:189:26 | call to aligned_alloc | test.c:190:13:190:14 | v2 | +| test.c:190:13:190:14 | v2 | test.c:129:22:129:22 | v | +| test.c:222:8:222:9 | p2 | test.c:223:11:223:12 | v1 | +| test.c:222:8:222:9 | p2 | test.c:224:12:224:13 | v1 | +| test.c:222:8:222:9 | p2 | test.c:225:10:225:11 | v1 | +| test.c:222:8:222:9 | p2 | test.c:226:12:226:13 | v1 | +| test.c:222:8:222:9 | p2 | test.c:227:11:227:12 | v1 | +| test.c:222:8:222:9 | p2 | test.c:228:13:228:14 | v1 | +nodes +| test.c:7:11:7:13 | & ... | semmle.label | & ... | +| test.c:8:12:8:14 | & ... | semmle.label | & ... | +| test.c:9:10:9:12 | & ... | semmle.label | & ... | +| test.c:10:11:10:13 | & ... | semmle.label | & ... | +| test.c:11:12:11:14 | & ... | semmle.label | & ... | +| test.c:12:13:12:15 | & ... | semmle.label | & ... | +| test.c:15:11:15:13 | & ... | semmle.label | & ... | +| test.c:16:12:16:14 | & ... | semmle.label | & ... | +| test.c:17:10:17:12 | & ... | semmle.label | & ... | +| test.c:18:11:18:13 | & ... | semmle.label | & ... | +| test.c:19:12:19:14 | & ... | semmle.label | & ... | +| test.c:20:13:20:15 | & ... | semmle.label | & ... | +| test.c:23:11:23:13 | & ... | semmle.label | & ... | +| test.c:24:12:24:14 | & ... | semmle.label | & ... | +| test.c:25:10:25:12 | & ... | semmle.label | & ... | +| test.c:26:12:26:14 | & ... | semmle.label | & ... | +| test.c:27:11:27:13 | & ... | semmle.label | & ... | +| test.c:28:13:28:15 | & ... | semmle.label | & ... | +| test.c:31:11:31:13 | & ... | semmle.label | & ... | +| test.c:32:12:32:14 | & ... | semmle.label | & ... | +| test.c:33:10:33:12 | & ... | semmle.label | & ... | +| test.c:34:12:34:14 | & ... | semmle.label | & ... | +| test.c:35:11:35:13 | & ... | semmle.label | & ... | +| test.c:36:13:36:15 | & ... | semmle.label | & ... | +| test.c:39:11:39:13 | & ... | semmle.label | & ... | +| test.c:40:12:40:14 | & ... | semmle.label | & ... | +| test.c:41:10:41:12 | & ... | semmle.label | & ... | +| test.c:42:12:42:14 | & ... | semmle.label | & ... | +| test.c:43:11:43:13 | & ... | semmle.label | & ... | +| test.c:44:13:44:15 | & ... | semmle.label | & ... | +| test.c:47:11:47:13 | & ... | semmle.label | & ... | +| test.c:48:12:48:14 | & ... | semmle.label | & ... | +| test.c:49:10:49:12 | & ... | semmle.label | & ... | +| test.c:50:12:50:14 | & ... | semmle.label | & ... | +| test.c:51:11:51:13 | & ... | semmle.label | & ... | +| test.c:52:13:52:15 | & ... | semmle.label | & ... | +| test.c:57:11:57:13 | & ... | semmle.label | & ... | +| test.c:58:12:58:14 | & ... | semmle.label | & ... | +| test.c:59:10:59:12 | & ... | semmle.label | & ... | +| test.c:60:12:60:14 | & ... | semmle.label | & ... | +| test.c:61:11:61:13 | & ... | semmle.label | & ... | +| test.c:62:13:62:15 | & ... | semmle.label | & ... | +| test.c:65:11:65:13 | & ... | semmle.label | & ... | +| test.c:66:12:66:14 | & ... | semmle.label | & ... | +| test.c:67:10:67:12 | & ... | semmle.label | & ... | +| test.c:68:12:68:14 | & ... | semmle.label | & ... | +| test.c:69:11:69:13 | & ... | semmle.label | & ... | +| test.c:70:13:70:15 | & ... | semmle.label | & ... | +| test.c:75:14:75:16 | & ... | semmle.label | & ... | +| test.c:75:14:75:16 | & ... | semmle.label | & ... | +| test.c:76:11:76:12 | v1 | semmle.label | v1 | +| test.c:77:12:77:13 | v1 | semmle.label | v1 | +| test.c:78:10:78:11 | v1 | semmle.label | v1 | +| test.c:79:12:79:13 | v1 | semmle.label | v1 | +| test.c:80:11:80:12 | v1 | semmle.label | v1 | +| test.c:81:13:81:14 | v1 | semmle.label | v1 | +| test.c:84:14:84:16 | & ... | semmle.label | & ... | +| test.c:84:14:84:16 | & ... | semmle.label | & ... | +| test.c:85:11:85:12 | v2 | semmle.label | v2 | +| test.c:86:12:86:13 | v2 | semmle.label | v2 | +| test.c:87:10:87:11 | v2 | semmle.label | v2 | +| test.c:88:12:88:13 | v2 | semmle.label | v2 | +| test.c:89:11:89:12 | v2 | semmle.label | v2 | +| test.c:90:13:90:14 | v2 | semmle.label | v2 | +| test.c:93:14:93:16 | & ... | semmle.label | & ... | +| test.c:93:14:93:16 | & ... | semmle.label | & ... | +| test.c:94:11:94:12 | v3 | semmle.label | v3 | +| test.c:95:12:95:13 | v3 | semmle.label | v3 | +| test.c:96:10:96:11 | v3 | semmle.label | v3 | +| test.c:97:12:97:13 | v3 | semmle.label | v3 | +| test.c:98:11:98:12 | v3 | semmle.label | v3 | +| test.c:99:13:99:14 | v3 | semmle.label | v3 | +| test.c:102:14:102:16 | & ... | semmle.label | & ... | +| test.c:102:14:102:16 | & ... | semmle.label | & ... | +| test.c:103:11:103:12 | v4 | semmle.label | v4 | +| test.c:104:12:104:13 | v4 | semmle.label | v4 | +| test.c:105:10:105:11 | v4 | semmle.label | v4 | +| test.c:106:12:106:13 | v4 | semmle.label | v4 | +| test.c:107:11:107:12 | v4 | semmle.label | v4 | +| test.c:108:13:108:14 | v4 | semmle.label | v4 | +| test.c:111:14:111:16 | & ... | semmle.label | & ... | +| test.c:111:14:111:16 | & ... | semmle.label | & ... | +| test.c:112:11:112:12 | v5 | semmle.label | v5 | +| test.c:113:12:113:13 | v5 | semmle.label | v5 | +| test.c:114:10:114:11 | v5 | semmle.label | v5 | +| test.c:115:12:115:13 | v5 | semmle.label | v5 | +| test.c:116:11:116:12 | v5 | semmle.label | v5 | +| test.c:117:13:117:14 | v5 | semmle.label | v5 | +| test.c:120:14:120:16 | & ... | semmle.label | & ... | +| test.c:120:14:120:16 | & ... | semmle.label | & ... | +| test.c:121:11:121:12 | v6 | semmle.label | v6 | +| test.c:122:12:122:13 | v6 | semmle.label | v6 | +| test.c:123:10:123:11 | v6 | semmle.label | v6 | +| test.c:124:12:124:13 | v6 | semmle.label | v6 | +| test.c:125:11:125:12 | v6 | semmle.label | v6 | +| test.c:126:13:126:14 | v6 | semmle.label | v6 | +| test.c:129:22:129:22 | v | semmle.label | v | +| test.c:129:22:129:22 | v | semmle.label | v | +| test.c:129:22:129:22 | v | semmle.label | v | +| test.c:130:17:130:17 | v | semmle.label | v | +| test.c:135:21:135:23 | & ... | semmle.label | & ... | +| test.c:135:21:135:23 | & ... | semmle.label | & ... | +| test.c:138:21:138:23 | & ... | semmle.label | & ... | +| test.c:138:21:138:23 | & ... | semmle.label | & ... | +| test.c:158:13:158:20 | & ... | semmle.label | & ... | +| test.c:161:13:161:20 | & ... | semmle.label | & ... | +| test.c:162:16:162:18 | & ... | semmle.label | & ... | +| test.c:166:24:166:29 | call to malloc | semmle.label | call to malloc | +| test.c:166:24:166:29 | call to malloc | semmle.label | call to malloc | +| test.c:166:24:166:29 | call to malloc | semmle.label | call to malloc | +| test.c:167:13:167:15 | & ... | semmle.label | & ... | +| test.c:167:14:167:15 | s1 | semmle.label | s1 | +| test.c:167:14:167:15 | s1 | semmle.label | s1 | +| test.c:168:16:168:18 | & ... | semmle.label | & ... | +| test.c:168:17:168:18 | s1 | semmle.label | s1 | +| test.c:168:17:168:18 | s1 | semmle.label | s1 | +| test.c:169:13:169:14 | ref arg s1 | semmle.label | ref arg s1 | +| test.c:169:13:169:14 | s1 | semmle.label | s1 | +| test.c:169:13:169:14 | s1 | semmle.label | s1 | +| test.c:169:13:169:14 | s1 | semmle.label | s1 | +| test.c:169:13:169:14 | s1 | semmle.label | s1 | +| test.c:171:15:171:16 | s1 | semmle.label | s1 | +| test.c:172:13:172:15 | & ... | semmle.label | & ... | +| test.c:172:14:172:15 | s2 | semmle.label | s2 | +| test.c:173:11:173:13 | & ... | semmle.label | & ... | +| test.c:173:12:173:13 | s2 | semmle.label | s2 | +| test.c:174:13:174:14 | s2 | semmle.label | s2 | +| test.c:174:13:174:14 | s2 | semmle.label | s2 | +| test.c:176:16:176:17 | s1 | semmle.label | s1 | +| test.c:177:13:177:15 | & ... | semmle.label | & ... | +| test.c:177:14:177:15 | s3 | semmle.label | s3 | +| test.c:178:11:178:13 | & ... | semmle.label | & ... | +| test.c:178:12:178:13 | s3 | semmle.label | s3 | +| test.c:179:13:179:14 | s3 | semmle.label | s3 | +| test.c:179:13:179:14 | s3 | semmle.label | s3 | +| test.c:183:14:183:26 | call to aligned_alloc | semmle.label | call to aligned_alloc | +| test.c:184:11:184:12 | v1 | semmle.label | v1 | +| test.c:185:10:185:11 | v1 | semmle.label | v1 | +| test.c:186:13:186:14 | v1 | semmle.label | v1 | +| test.c:187:13:187:14 | v1 | semmle.label | v1 | +| test.c:189:14:189:26 | call to aligned_alloc | semmle.label | call to aligned_alloc | +| test.c:190:13:190:14 | v2 | semmle.label | v2 | +| test.c:214:11:214:12 | p2 | semmle.label | p2 | +| test.c:215:12:215:13 | p2 | semmle.label | p2 | +| test.c:216:10:216:11 | p2 | semmle.label | p2 | +| test.c:217:11:217:12 | p2 | semmle.label | p2 | +| test.c:218:12:218:13 | p2 | semmle.label | p2 | +| test.c:219:13:219:14 | p2 | semmle.label | p2 | +| test.c:222:8:222:9 | p2 | semmle.label | p2 | +| test.c:222:8:222:9 | p2 | semmle.label | p2 | +| test.c:223:11:223:12 | v1 | semmle.label | v1 | +| test.c:224:12:224:13 | v1 | semmle.label | v1 | +| test.c:225:10:225:11 | v1 | semmle.label | v1 | +| test.c:226:12:226:13 | v1 | semmle.label | v1 | +| test.c:227:11:227:12 | v1 | semmle.label | v1 | +| test.c:228:13:228:14 | v1 | semmle.label | v1 | +subpaths +| test.c:169:13:169:14 | s1 | test.c:129:22:129:22 | v | test.c:129:22:129:22 | v | test.c:169:13:169:14 | ref arg s1 | +#select +| test.c:8:12:8:14 | & ... | test.c:8:12:8:14 | & ... | test.c:8:12:8:14 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type short with 2-byte alignment. | test.c:8:12:8:14 | & ... | address-of expression | +| test.c:9:10:9:12 | & ... | test.c:9:10:9:12 | & ... | test.c:9:10:9:12 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:9:10:9:12 | & ... | address-of expression | +| test.c:10:11:10:13 | & ... | test.c:10:11:10:13 | & ... | test.c:10:11:10:13 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:10:11:10:13 | & ... | address-of expression | +| test.c:11:12:11:14 | & ... | test.c:11:12:11:14 | & ... | test.c:11:12:11:14 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:11:12:11:14 | & ... | address-of expression | +| test.c:12:13:12:15 | & ... | test.c:12:13:12:15 | & ... | test.c:12:13:12:15 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:12:13:12:15 | & ... | address-of expression | +| test.c:17:10:17:12 | & ... | test.c:17:10:17:12 | & ... | test.c:17:10:17:12 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:17:10:17:12 | & ... | address-of expression | +| test.c:18:11:18:13 | & ... | test.c:18:11:18:13 | & ... | test.c:18:11:18:13 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:18:11:18:13 | & ... | address-of expression | +| test.c:19:12:19:14 | & ... | test.c:19:12:19:14 | & ... | test.c:19:12:19:14 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:19:12:19:14 | & ... | address-of expression | +| test.c:20:13:20:15 | & ... | test.c:20:13:20:15 | & ... | test.c:20:13:20:15 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:20:13:20:15 | & ... | address-of expression | +| test.c:27:11:27:13 | & ... | test.c:27:11:27:13 | & ... | test.c:27:11:27:13 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:27:11:27:13 | & ... | address-of expression | +| test.c:28:13:28:15 | & ... | test.c:28:13:28:15 | & ... | test.c:28:13:28:15 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:28:13:28:15 | & ... | address-of expression | +| test.c:35:11:35:13 | & ... | test.c:35:11:35:13 | & ... | test.c:35:11:35:13 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:35:11:35:13 | & ... | address-of expression | +| test.c:36:13:36:15 | & ... | test.c:36:13:36:15 | & ... | test.c:36:13:36:15 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:36:13:36:15 | & ... | address-of expression | +| test.c:61:11:61:13 | & ... | test.c:61:11:61:13 | & ... | test.c:61:11:61:13 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:61:11:61:13 | & ... | address-of expression | +| test.c:62:13:62:15 | & ... | test.c:62:13:62:15 | & ... | test.c:62:13:62:15 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:62:13:62:15 | & ... | address-of expression | +| test.c:77:12:77:13 | v1 | test.c:75:14:75:16 | & ... | test.c:77:12:77:13 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type short with 2-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:78:10:78:11 | v1 | test.c:75:14:75:16 | & ... | test.c:78:10:78:11 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:79:12:79:13 | v1 | test.c:75:14:75:16 | & ... | test.c:79:12:79:13 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:80:11:80:12 | v1 | test.c:75:14:75:16 | & ... | test.c:80:11:80:12 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:81:13:81:14 | v1 | test.c:75:14:75:16 | & ... | test.c:81:13:81:14 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:87:10:87:11 | v2 | test.c:84:14:84:16 | & ... | test.c:87:10:87:11 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | +| test.c:88:12:88:13 | v2 | test.c:84:14:84:16 | & ... | test.c:88:12:88:13 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | +| test.c:89:11:89:12 | v2 | test.c:84:14:84:16 | & ... | test.c:89:11:89:12 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | +| test.c:90:13:90:14 | v2 | test.c:84:14:84:16 | & ... | test.c:90:13:90:14 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | +| test.c:98:11:98:12 | v3 | test.c:93:14:93:16 | & ... | test.c:98:11:98:12 | v3 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:93:14:93:16 | & ... | address-of expression | +| test.c:99:13:99:14 | v3 | test.c:93:14:93:16 | & ... | test.c:99:13:99:14 | v3 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:93:14:93:16 | & ... | address-of expression | +| test.c:107:11:107:12 | v4 | test.c:102:14:102:16 | & ... | test.c:107:11:107:12 | v4 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:102:14:102:16 | & ... | address-of expression | +| test.c:108:13:108:14 | v4 | test.c:102:14:102:16 | & ... | test.c:108:13:108:14 | v4 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:102:14:102:16 | & ... | address-of expression | +| test.c:130:17:130:17 | v | test.c:135:21:135:23 | & ... | test.c:130:17:130:17 | v | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:135:21:135:23 | & ... | address-of expression | +| test.c:130:17:130:17 | v | test.c:174:13:174:14 | s2 | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:174:13:174:14 | s2 | pointer base type | +| test.c:130:17:130:17 | v | test.c:179:13:179:14 | s3 | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:179:13:179:14 | s3 | pointer base type | +| test.c:130:17:130:17 | v | test.c:189:14:189:26 | call to aligned_alloc | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:189:14:189:26 | call to aligned_alloc | call to aligned_alloc | +| test.c:158:13:158:20 | & ... | test.c:158:13:158:20 | & ... | test.c:158:13:158:20 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:158:13:158:20 | & ... | address-of expression | +| test.c:162:16:162:18 | & ... | test.c:162:16:162:18 | & ... | test.c:162:16:162:18 | & ... | Cast from pointer with 8-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:162:16:162:18 | & ... | address-of expression | +| test.c:168:16:168:18 | & ... | test.c:166:24:166:29 | call to malloc | test.c:168:16:168:18 | & ... | Cast from pointer with 16-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:166:24:166:29 | call to malloc | call to malloc | +| test.c:168:16:168:18 | & ... | test.c:168:16:168:18 | & ... | test.c:168:16:168:18 | & ... | Cast from pointer with 8-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:168:16:168:18 | & ... | address-of expression | +| test.c:186:13:186:14 | v1 | test.c:183:14:183:26 | call to aligned_alloc | test.c:186:13:186:14 | v1 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:183:14:183:26 | call to aligned_alloc | call to aligned_alloc | +| test.c:216:10:216:11 | p2 | test.c:216:10:216:11 | p2 | test.c:216:10:216:11 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:216:10:216:11 | p2 | pointer base type | +| test.c:217:11:217:12 | p2 | test.c:217:11:217:12 | p2 | test.c:217:11:217:12 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:217:11:217:12 | p2 | pointer base type | +| test.c:218:12:218:13 | p2 | test.c:218:12:218:13 | p2 | test.c:218:12:218:13 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:218:12:218:13 | p2 | pointer base type | +| test.c:219:13:219:14 | p2 | test.c:219:13:219:14 | p2 | test.c:219:13:219:14 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:219:13:219:14 | p2 | pointer base type | +| test.c:225:10:225:11 | v1 | test.c:222:8:222:9 | p2 | test.c:225:10:225:11 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type | +| test.c:226:12:226:13 | v1 | test.c:222:8:222:9 | p2 | test.c:226:12:226:13 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type | +| test.c:227:11:227:12 | v1 | test.c:222:8:222:9 | p2 | test.c:227:11:227:12 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type | +| test.c:228:13:228:14 | v1 | test.c:222:8:222:9 | p2 | test.c:228:13:228:14 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type | diff --git a/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.qlref b/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.qlref new file mode 100644 index 0000000000..9e655176f7 --- /dev/null +++ b/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.qlref @@ -0,0 +1 @@ +rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP36-C/test.c b/c/cert/test/rules/EXP36-C/test.c index 7f4230ffa1..b32e2ab80f 100644 --- a/c/cert/test/rules/EXP36-C/test.c +++ b/c/cert/test/rules/EXP36-C/test.c @@ -1,5 +1,6 @@ #include #include +#include void test_direct_cast_alignment() { char c1 = 1; // assuming 1-byte alignment @@ -15,16 +16,16 @@ void test_direct_cast_alignment() { (short *)&s1; // COMPLIANT (int *)&s1; // NON_COMPLIANT (long *)&s1; // NON_COMPLIANT - (float *)&c1; // NON_COMPLIANT - (double *)&c1; // NON_COMPLIANT + (float *)&s1; // NON_COMPLIANT + (double *)&s1; // NON_COMPLIANT int i1 = 1; // assuming 4-byte alignment (char *)&i1; // COMPLIANT (short *)&i1; // COMPLIANT (int *)&i1; // COMPLIANT - (float *)&c1; // COMPLIANT + (float *)&i1; // COMPLIANT (long *)&i1; // NON_COMPLIANT - assuming 8 byte alignment for longs - (double *)&c1; // NON_COMPLIANT + (double *)&i1; // NON_COMPLIANT float f1 = 1; // assuming 4-byte alignment (char *)&f1; // COMPLIANT @@ -38,9 +39,9 @@ void test_direct_cast_alignment() { (char *)&l1; // COMPLIANT (short *)&l1; // COMPLIANT (int *)&l1; // COMPLIANT - (float *)&c1; // COMPLIANT + (float *)&l1; // COMPLIANT (long *)&l1; // COMPLIANT - (double *)&c1; // COMPLIANT + (double *)&l1; // COMPLIANT double d1 = 1; // assuming 8-byte alignment (char *)&d1; // COMPLIANT @@ -65,11 +66,11 @@ void custom_aligned_types() { (short *)&c2; // COMPLIANT (int *)&c2; // COMPLIANT (float *)&c2; // COMPLIANT - (long *)&c2; // NON_COMPLIANT - (double *)&c2; // NON_COMPLIANT + (long *)&c2; // COMPLIANT + (double *)&c2; // COMPLIANT } -void test_via_void_direct() { +void test_via_void_ptr_var_direct() { char c1 = 1; void *v1 = &c1; (char *)v1; // COMPLIANT @@ -92,7 +93,7 @@ void test_via_void_direct() { void *v3 = &i1; (char *)v3; // COMPLIANT (short *)v3; // COMPLIANT - (int *)v3; // COMPLIAN + (int *)v3; // COMPLIANT (float *)v3; // COMPLIANT (long *)v3; // NON_COMPLIANT - assuming 8 byte alignment for longs (double *)v3; // NON_COMPLIANT - but only on x64 @@ -131,10 +132,10 @@ int *cast_away(void *v) { void test_via_void_indirect() { char c1 = 1; - cast_away((void *)c1); // NON_COMPLIANT + cast_away((void *)&c1); // NON_COMPLIANT int i1 = 1; - cast_away((void *)i1); // COMPLIANT + cast_away((void *)&i1); // COMPLIANT } struct S1 { @@ -147,10 +148,82 @@ struct S2 { alignas(size_t) unsigned char data[8]; }; +struct S3 { + char c1; + alignas(64) unsigned char data[8]; +}; + void test_struct_alignment() { - S1 s1; + struct S1 s1; (size_t *)&s1.data; // NON_COMPLIANT - S2 s2; + struct S2 s2; (size_t *)&s2.data; // COMPLIANT + (struct S3 *)&s2; // NON_COMPLIANT +} + +void test_malloc_alignment_and_pointer_arithmetic() { + short *s1 = (short *)malloc(64); + (size_t *)&s1; // COMPLIANT + (struct S3 *)&s1; // NON_COMPLIANT - over-aligned struct + cast_away(s1); // COMPLIANT + + short *s2 = s1 + 1; + (size_t *)&s2; // NON_COMPLIANT[FALSE_NEGATIVE] + (char *)&s2; // COMPLIANT + cast_away(s2); // NON_COMPLIANT + + short *s3 = &s1[1]; + (size_t *)&s3; // NON_COMPLIANT[FALSE_NEGATIVE] + (char *)&s3; // COMPLIANT + cast_away(s3); // NON_COMPLIANT +} + +void test_aligned_alloc_alignment() { + void *v1 = aligned_alloc(4, 8); + (char *)v1; // COMPLIANT + (int *)v1; // COMPLIANT + (size_t *)v1; // NON_COMPLIANT + cast_away(v1); // COMPLIANT + + void *v2 = aligned_alloc(2, 8); + cast_away(v2); // NON_COMPLIANT +} + +void test_standalone_pointer_cast_alignment(void *p1, short *p2) { + void *v1; + + // void* direct + (char *)p1; // COMPLIANT + (short *)p1; // COMPLIANT + (int *)p1; // COMPLIANT + (float *)p1; // COMPLIANT + (long *)p1; // COMPLIANT + (double *)p1; // COMPLIANT + + // void* indirect via void* + v1 = p1; // COMPLIANT + (char *)v1; // COMPLIANT + (short *)v1; // COMPLIANT + (int *)v1; // COMPLIANT + (float *)v1; // COMPLIANT + (long *)v1; // COMPLIANT + (double *)v1; // COMPLIANT + + // short* direct + (char *)p2; // COMPLIANT + (short *)p2; // COMPLIANT + (int *)p2; // NON_COMPLIANT + (long *)p2; // NON_COMPLIANT + (float *)p2; // NON_COMPLIANT + (double *)p2; // NON_COMPLIANT + + // short* indirect via void* + v1 = p2; // COMPLIANT + (char *)v1; // COMPLIANT + (short *)v1; // COMPLIANT + (int *)v1; // NON_COMPLIANT + (float *)v1; // NON_COMPLIANT + (long *)v1; // NON_COMPLIANT + (double *)v1; // NON_COMPLIANT } \ No newline at end of file diff --git a/cpp/cert/src/rules/MEM57-CPP/UsingDefaultOperatorNewForOverAlignedTypes.ql b/cpp/cert/src/rules/MEM57-CPP/UsingDefaultOperatorNewForOverAlignedTypes.ql index d0210791d4..8fc33f8457 100644 --- a/cpp/cert/src/rules/MEM57-CPP/UsingDefaultOperatorNewForOverAlignedTypes.ql +++ b/cpp/cert/src/rules/MEM57-CPP/UsingDefaultOperatorNewForOverAlignedTypes.ql @@ -14,26 +14,7 @@ import cpp import codingstandards.cpp.cert - -/* - * In theory each compilation of each file can have a different `max_align_t` value (for example, - * if the same file is compiled under different compilers in the same database). We don't have the - * fine-grained data to determine which compilation each operator new call is from, so we instead - * report only in cases where there's a single clear alignment for the whole database. - */ - -class MaxAlignT extends TypedefType { - MaxAlignT() { getName() = "max_align_t" } -} - -/** - * Gets the alignment for `max_align_t`, assuming there is a single consistent alignment for the - * database. - */ -int getGlobalMaxAlignT() { - count(MaxAlignT m | | m.getAlignment()) = 1 and - result = any(MaxAlignT t).getAlignment() -} +import codingstandards.cpp.Alignment from NewOrNewArrayExpr newExpr, Type overAlignedType where diff --git a/cpp/common/src/codingstandards/cpp/Alignment.qll b/cpp/common/src/codingstandards/cpp/Alignment.qll new file mode 100644 index 0000000000..c254d7909a --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/Alignment.qll @@ -0,0 +1,25 @@ +/** + * Provides a library with additional modeling for C and C++ memory alignment constructs. + */ + +import cpp + +/* + * In theory each compilation of each file can have a different `max_align_t` value (for example, + * if the same file is compiled under different compilers in the same database). We don't have the + * fine-grained data to determine which compilation each operator new call is from, so we instead + * report only in cases where there's a single clear alignment for the whole database. + */ + +class MaxAlignT extends TypedefType { + MaxAlignT() { getName() = "max_align_t" } +} + +/** + * Gets the alignment for `max_align_t`, assuming there is a single consistent alignment for the + * database. + */ +int getGlobalMaxAlignT() { + count(MaxAlignT m | | m.getAlignment()) = 1 and + result = any(MaxAlignT t).getAlignment() +} From d483c3c1e6894a3fa47d26cd355879c1e8355f59 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Thu, 17 Nov 2022 14:08:43 +0100 Subject: [PATCH 09/36] Update EXP32-C query and test --- ...sVolatileObjectWithNonVolatileReference.ql | 68 +++++++++++-------- ...ileObjectWithNonVolatileReference.expected | 5 +- c/cert/test/rules/EXP32-C/test.c | 7 ++ 3 files changed, 48 insertions(+), 32 deletions(-) diff --git a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql index fe6acfb44b..346cd41621 100644 --- a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql +++ b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql @@ -13,16 +13,36 @@ import cpp import codingstandards.c.cert +import semmle.code.cpp.controlflow.Dereferenced +import semmle.code.cpp.controlflow.StackVariableReachability + +abstract class UndefinedVolatilePointerExpr extends Expr { + abstract string getMessage(); +} + +/** + * Gets the depth of a pointer's base type's volatile qualifier + */ +int getAVolatileDepth(PointerType pt) { + pt.getBaseType().isVolatile() and result = 1 + or + result = getAVolatileDepth(pt.getBaseType()) + 1 +} /** * A `Cast` which converts from a pointer to a volatile-qualified type * to a pointer to a non-volatile-qualified type. */ -class CastFromVolatileToNonVolatileBaseType extends Cast { +class CastFromVolatileToNonVolatileBaseType extends Cast, UndefinedVolatilePointerExpr { CastFromVolatileToNonVolatileBaseType() { - this.getExpr().getType().(PointerType).getBaseType*().isVolatile() and - this.getActualType() instanceof PointerType and - not this.getActualType().(PointerType).getBaseType*().isVolatile() + exists(int i | + i = getAVolatileDepth(this.getExpr().getType()) and + not i = getAVolatileDepth(this.getActualType()) + ) + } + + override string getMessage() { + result = "Cast of object with a volatile-qualified type to a non-volatile-qualified type." } } @@ -30,36 +50,24 @@ class CastFromVolatileToNonVolatileBaseType extends Cast { * An `AssignExpr` with an *lvalue* that is a pointer to a volatile base type and * and *rvalue* that is not also a pointer to a volatile base type. */ -class NonVolatileObjectAssignedToVolatilePointer extends AssignExpr { +class NonVolatileObjectAssignedToVolatilePointer extends AssignExpr, UndefinedVolatilePointerExpr { NonVolatileObjectAssignedToVolatilePointer() { - this.getLValue().getType().(DerivedType).getBaseType*().isVolatile() and - not this.getRValue().getUnconverted().getType().(DerivedType).getBaseType*().isVolatile() + exists(int i | + not i = getAVolatileDepth(this.getRValue().getType()) and + i = getAVolatileDepth(this.getLValue().(VariableAccess).getTarget().getType()) + ) and + exists(VariableAccess va | + va = this.getRValue().getAChild*().(VariableAccess).getTarget().getAnAccess() and + this.getASuccessor+() = va + ) } - /** - * All `VariableAccess` expressions which are transitive successors of - * this `Expr` and which access the variable accessed in the *rvalue* of this `Expr` - */ - Expr getASubsequentAccessOfAssignedObject() { + override string getMessage() { result = - any(VariableAccess va | - va = this.getRValue().getAChild*().(VariableAccess).getTarget().getAnAccess() and - this.getASuccessor+() = va - | - va - ) + "Assignment indicates a volatile object, but a later access of the object occurs via a non-volatile pointer." } } -from Expr e, string message -where - not isExcluded(e, Pointers3Package::doNotAccessVolatileObjectWithNonVolatileReferenceQuery()) and - ( - e instanceof CastFromVolatileToNonVolatileBaseType and - message = "Cast of object with a volatile-qualified type to a non-volatile-qualified type." - or - exists(e.(NonVolatileObjectAssignedToVolatilePointer).getASubsequentAccessOfAssignedObject()) and - message = - "Non-volatile object referenced via pointer to volatile type and later accessed via its original object of a non-volatile-qualified type." - ) -select e, message +from UndefinedVolatilePointerExpr e +where not isExcluded(e, Pointers3Package::doNotAccessVolatileObjectWithNonVolatileReferenceQuery()) +select e, e.getMessage() diff --git a/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.expected b/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.expected index d8fd01bbae..f5ea6e8d4b 100644 --- a/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.expected +++ b/c/cert/test/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.expected @@ -1,4 +1,5 @@ | test.c:5:13:5:21 | (int *)... | Cast of object with a volatile-qualified type to a non-volatile-qualified type. | | test.c:6:13:6:31 | (int *)... | Cast of object with a volatile-qualified type to a non-volatile-qualified type. | -| test.c:14:3:14:55 | ... = ... | Non-volatile object referenced via pointer to volatile type and later accessed via its original object of a non-volatile-qualified type. | -| test.c:24:3:25:36 | ... = ... | Non-volatile object referenced via pointer to volatile type and later accessed via its original object of a non-volatile-qualified type. | +| test.c:14:3:14:55 | ... = ... | Assignment indicates a volatile object, but a later access of the object occurs via a non-volatile pointer. | +| test.c:24:3:25:36 | ... = ... | Assignment indicates a volatile object, but a later access of the object occurs via a non-volatile pointer. | +| test.c:42:24:42:41 | (int *)... | Cast of object with a volatile-qualified type to a non-volatile-qualified type. | diff --git a/c/cert/test/rules/EXP32-C/test.c b/c/cert/test/rules/EXP32-C/test.c index d9b07ac84d..5a688848a4 100644 --- a/c/cert/test/rules/EXP32-C/test.c +++ b/c/cert/test/rules/EXP32-C/test.c @@ -35,4 +35,11 @@ void test_volatile_not_lost_by_assignment_and_cast() { compliant_pointer_to_pointer = &compliant_pointer; // COMPLIANT *compliant_pointer_to_pointer = &val; *compliant_pointer; // Volatile object is accessed through a volatile pointer +} + +void test_volatile_lost_by_assignment_and_cast_2() { + volatile int *ptr = 0; + int *volatile ptr2 = (int *volatile)ptr; // NON_COMPLIANT + *ptr2; // Volatile object dereferenced through volatile pointer to + // non-volatile object } \ No newline at end of file From 6733786321af7a09079485c01d5c97a137e667b5 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Thu, 17 Nov 2022 14:10:36 +0100 Subject: [PATCH 10/36] Add comments to EXP32-C query --- .../DoNotAccessVolatileObjectWithNonVolatileReference.ql | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql index 346cd41621..0bc200bf5e 100644 --- a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql +++ b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql @@ -16,7 +16,13 @@ import codingstandards.c.cert import semmle.code.cpp.controlflow.Dereferenced import semmle.code.cpp.controlflow.StackVariableReachability +/** + * An expression involving volatile-qualified types that results in undefined behavior. + */ abstract class UndefinedVolatilePointerExpr extends Expr { + /** + * Gets a descriptive string describing the type of expression and undefined behavior. + */ abstract string getMessage(); } From a15708955de16c9f62638842b6e2aaafdb5a0d37 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Thu, 17 Nov 2022 14:20:52 +0100 Subject: [PATCH 11/36] Add comments and implementation scope for EXP32-C --- .../DoNotAccessVolatileObjectWithNonVolatileReference.ql | 8 ++++++-- rule_packages/c/Pointers3.json | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql index 0bc200bf5e..407561f532 100644 --- a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql +++ b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql @@ -14,7 +14,6 @@ import cpp import codingstandards.c.cert import semmle.code.cpp.controlflow.Dereferenced -import semmle.code.cpp.controlflow.StackVariableReachability /** * An expression involving volatile-qualified types that results in undefined behavior. @@ -62,9 +61,14 @@ class NonVolatileObjectAssignedToVolatilePointer extends AssignExpr, UndefinedVo not i = getAVolatileDepth(this.getRValue().getType()) and i = getAVolatileDepth(this.getLValue().(VariableAccess).getTarget().getType()) ) and + // Checks for subsequent accesses to the underlying object via the original non-volatile + // pointer assigned to the volatile pointer. This heuristic can cause false-positives + // in certain instances which require more advanced reachability analysis, e.g. loops and scope + // considerations that this simple forward traversal of the control-flow graph does not account for. exists(VariableAccess va | va = this.getRValue().getAChild*().(VariableAccess).getTarget().getAnAccess() and - this.getASuccessor+() = va + this.getASuccessor+() = va and + dereferenced(va) ) } diff --git a/rule_packages/c/Pointers3.json b/rule_packages/c/Pointers3.json index e395543690..1e9038ebd7 100644 --- a/rule_packages/c/Pointers3.json +++ b/rule_packages/c/Pointers3.json @@ -14,7 +14,10 @@ "short_name": "DoNotAccessVolatileObjectWithNonVolatileReference", "tags": [ "correctness" - ] + ], + "implementation_scope": { + "description": "In limited cases, this query can raise false-positives for assignment of volatile objects and subsequent accesses of those objects via non-volatile pointers." + } } ], "title": "Do not access a volatile object through a nonvolatile reference" From 03bac50cbf5d93c770b02d5c320d6d60d0b765b9 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Thu, 17 Nov 2022 18:34:41 +0100 Subject: [PATCH 12/36] Update EXP36-C query and test --- ...PointerToMoreStrictlyAlignedPointerType.ql | 113 ++++++++++-------- ...rToMoreStrictlyAlignedPointerType.expected | 82 ++++++------- c/cert/test/rules/EXP36-C/test.c | 37 +++++- 3 files changed, 132 insertions(+), 100 deletions(-) diff --git a/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql index b1c2b6e305..32579dd250 100644 --- a/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql +++ b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql @@ -15,6 +15,7 @@ import cpp import codingstandards.c.cert import codingstandards.cpp.Alignment import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.dataflow.DataFlow2 import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis import DataFlow::PathGraph @@ -40,14 +41,14 @@ abstract class ExprWithAlignment extends Expr { class AddressOfAlignedVariableExpr extends AddressOfExpr, ExprWithAlignment { AddressOfAlignedVariableExpr() { this.getAddressable() instanceof Variable } - AlignAs alignAsAttribute() { result = this.getAddressable().(Variable).getAnAttribute() } + AlignAs getAlignAsAttribute() { result = this.getAddressable().(Variable).getAnAttribute() } override int getAlignment() { - result = alignAsAttribute().getArgument(0).getValueInt() + result = getAlignAsAttribute().getArgument(0).getValueInt() or - result = alignAsAttribute().getArgument(0).getValueType().getSize() + result = getAlignAsAttribute().getArgument(0).getValueType().getSize() or - not exists(alignAsAttribute()) and + not exists(getAlignAsAttribute()) and result = this.getAddressable().(Variable).getType().getAlignment() } @@ -76,40 +77,35 @@ class DefinedAlignmentAllocationExpr extends FunctionCall, ExprWithAlignment { } /** - * A class extending `VariableAccess` and `ExprWithAlignment` to reason about the - * alignment of pointers accessed based solely on the pointers' base types. + * An `Expr` of type `PointerType` but not `VoidPointerType` + * which is the unique non-`Conversion` expression of a `Cast`. */ -class DefaultAlignedPointerAccessExpr extends VariableAccess, ExprWithAlignment { - DefaultAlignedPointerAccessExpr() { - this.getTarget().getUnspecifiedType() instanceof PointerType and - not this.getTarget().getUnspecifiedType() instanceof VoidPointerType - } - - override int getAlignment() { - result = this.getTarget().getType().(PointerType).getBaseType().getAlignment() +class UnconvertedCastFromNonVoidPointerExpr extends Expr { + UnconvertedCastFromNonVoidPointerExpr() { + exists(CStyleCast cast | + cast.getUnconverted() = this and + this.getUnspecifiedType() instanceof PointerType and + not this.getUnspecifiedType() instanceof VoidPointerType + ) } - - override string getKind() { result = "pointer base type" } } /** - * A data-flow configuration for analysing the flow of `ExprWithAlignment` pointer expressions - * to casts which perform pointer type conversions and potentially create pointer alignment issues. + * A class extending `UnconvertedCastFromNonVoidPointerExpr` and `ExprWithAlignment` to reason + * about the alignment of pointers accessed based solely on the pointers' base types. */ -class ExprWithAlignmentToCStyleCastConfiguration extends DataFlow::Configuration { - ExprWithAlignmentToCStyleCastConfiguration() { - this = "ExprWithAlignmentToCStyleCastConfiguration" +class DefaultAlignedPointerExpr extends UnconvertedCastFromNonVoidPointerExpr, ExprWithAlignment { + DefaultAlignedPointerExpr() { + not any(AllocationOrAddressOfExprToUnconvertedCastFromNonVoidPointerExprConfig config) + .hasFlowTo(DataFlow::exprNode(this)) } - override predicate isSource(DataFlow::Node source) { - source.asExpr() instanceof ExprWithAlignment - } + override int getAlignment() { result = this.getType().(PointerType).getBaseType().getAlignment() } - override predicate isSink(DataFlow::Node sink) { - exists(CStyleCast cast | - cast.getUnderlyingType() instanceof PointerType and - cast.getUnconverted() = sink.asExpr() - ) + override string getKind() { + result = + "pointer base type " + + this.getType().(PointerType).getBaseType().getUnspecifiedType().getName() } } @@ -122,9 +118,9 @@ class ExprWithAlignmentToCStyleCastConfiguration extends DataFlow::Configuration * to exclude an `DefaultAlignedPointerAccessExpr` as a source if a preceding source * defined by this configuration provides more accurate alignment information. */ -class AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig extends DataFlow::Configuration { - AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig() { - this = "AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig" +class AllocationOrAddressOfExprToUnconvertedCastFromNonVoidPointerExprConfig extends DataFlow2::Configuration { + AllocationOrAddressOfExprToUnconvertedCastFromNonVoidPointerExprConfig() { + this = "AllocationOrAddressOfExprToUnconvertedCastFromNonVoidPointerExprConfig" } override predicate isSource(DataFlow::Node source) { @@ -133,7 +129,42 @@ class AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig extends DataF } override predicate isSink(DataFlow::Node sink) { - sink.asExpr() instanceof DefaultAlignedPointerAccessExpr + sink.asExpr() instanceof UnconvertedCastFromNonVoidPointerExpr + } +} + +/** + * A data-flow configuration for analysing the flow of `ExprWithAlignment` pointer expressions + * to casts which perform pointer type conversions and potentially create pointer alignment issues. + */ +class ExprWithAlignmentToCStyleCastConfiguration extends DataFlow::Configuration { + ExprWithAlignmentToCStyleCastConfiguration() { + this = "ExprWithAlignmentToCStyleCastConfiguration" + } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof ExprWithAlignment + } + + override predicate isSink(DataFlow::Node sink) { + exists(CStyleCast cast | + cast.getUnderlyingType() instanceof PointerType and + cast.getUnconverted() = sink.asExpr() + ) + } + + override predicate isBarrierOut(DataFlow::Node node) { + // the default interprocedural data-flow model flows through any array assignment expressions + // to the qualifier (array base or pointer dereferenced) instead of the individual element + // that the assignment modifies. this default behaviour causes false positives for any future + // cast of the array base, so remove the assignment edge at the expense of false-negatives. + exists(AssignExpr a | + node.asExpr() = a.getRValue() and + ( + a.getLValue() instanceof ArrayExpr or + a.getLValue() instanceof PointerDereferenceExpr + ) + ) } } @@ -145,24 +176,10 @@ where any(ExprWithAlignmentToCStyleCastConfiguration config).hasFlowPath(source, sink) and source.getNode().asExpr() = expr and sink.getNode().asExpr() = cast.getUnconverted() and - ( - // possibility 1: the source node (ExprWithAlignment) is NOT a DefaultAlignedPointerAccessExpr - // meaning that its alignment info is accurate regardless of any preceding ExprWithAlignment nodes - expr instanceof DefaultAlignedPointerAccessExpr - implies - ( - // possibility 2: the source node (ExprWithAlignment) IS a DefaultAlignedPointerAccessExpr - // meaning that its alignment info is only accurate if no preceding ExprWithAlignment nodes exist - not any(AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig config) - .hasFlowTo(source.getNode()) and - expr instanceof DefaultAlignedPointerAccessExpr and - cast.getUnconverted() instanceof VariableAccess - ) - ) and toBaseType = cast.getActualType().(PointerType).getBaseType() and alignmentTo = toBaseType.getAlignment() and alignmentFrom = expr.getAlignment() and - // only flag cases where the cast's target type has stricter alignment requirements than the source + // flag cases where the cast's target type has stricter alignment requirements than the source alignmentFrom < alignmentTo select sink, source, sink, "Cast from pointer with " + alignmentFrom + diff --git a/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected b/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected index e523be08fb..b70d88fe3f 100644 --- a/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected +++ b/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected @@ -35,32 +35,15 @@ edges | test.c:120:14:120:16 | & ... | test.c:124:12:124:13 | v6 | | test.c:120:14:120:16 | & ... | test.c:125:11:125:12 | v6 | | test.c:120:14:120:16 | & ... | test.c:126:13:126:14 | v6 | -| test.c:129:22:129:22 | v | test.c:129:22:129:22 | v | | test.c:129:22:129:22 | v | test.c:130:17:130:17 | v | | test.c:135:21:135:23 | & ... | test.c:129:22:129:22 | v | | test.c:138:21:138:23 | & ... | test.c:129:22:129:22 | v | | test.c:166:24:166:29 | call to malloc | test.c:167:13:167:15 | & ... | -| test.c:166:24:166:29 | call to malloc | test.c:167:14:167:15 | s1 | | test.c:166:24:166:29 | call to malloc | test.c:168:16:168:18 | & ... | -| test.c:166:24:166:29 | call to malloc | test.c:168:17:168:18 | s1 | | test.c:166:24:166:29 | call to malloc | test.c:169:13:169:14 | s1 | | test.c:166:24:166:29 | call to malloc | test.c:169:13:169:14 | s1 | -| test.c:166:24:166:29 | call to malloc | test.c:169:13:169:14 | s1 | -| test.c:166:24:166:29 | call to malloc | test.c:169:13:169:14 | s1 | -| test.c:166:24:166:29 | call to malloc | test.c:171:15:171:16 | s1 | -| test.c:166:24:166:29 | call to malloc | test.c:176:16:176:17 | s1 | -| test.c:167:14:167:15 | s1 | test.c:167:13:167:15 | & ... | -| test.c:168:17:168:18 | s1 | test.c:168:16:168:18 | & ... | -| test.c:169:13:169:14 | ref arg s1 | test.c:171:15:171:16 | s1 | -| test.c:169:13:169:14 | ref arg s1 | test.c:176:16:176:17 | s1 | | test.c:169:13:169:14 | s1 | test.c:129:22:129:22 | v | -| test.c:169:13:169:14 | s1 | test.c:129:22:129:22 | v | -| test.c:169:13:169:14 | s1 | test.c:169:13:169:14 | ref arg s1 | -| test.c:172:14:172:15 | s2 | test.c:172:13:172:15 | & ... | -| test.c:173:12:173:13 | s2 | test.c:173:11:173:13 | & ... | | test.c:174:13:174:14 | s2 | test.c:129:22:129:22 | v | -| test.c:177:14:177:15 | s3 | test.c:177:13:177:15 | & ... | -| test.c:178:12:178:13 | s3 | test.c:178:11:178:13 | & ... | | test.c:179:13:179:14 | s3 | test.c:129:22:129:22 | v | | test.c:183:14:183:26 | call to aligned_alloc | test.c:184:11:184:12 | v1 | | test.c:183:14:183:26 | call to aligned_alloc | test.c:185:10:185:11 | v1 | @@ -75,6 +58,10 @@ edges | test.c:222:8:222:9 | p2 | test.c:226:12:226:13 | v1 | | test.c:222:8:222:9 | p2 | test.c:227:11:227:12 | v1 | | test.c:222:8:222:9 | p2 | test.c:228:13:228:14 | v1 | +| test.c:238:13:238:14 | & ... | test.c:244:12:244:13 | ip | +| test.c:241:15:241:18 | & ... | test.c:247:9:247:12 | & ... | +| test.c:252:16:252:18 | & ... | test.c:254:11:254:13 | ps1 | +| test.c:252:16:252:18 | & ... | test.c:256:10:256:12 | ps1 | nodes | test.c:7:11:7:13 | & ... | semmle.label | & ... | | test.c:8:12:8:14 | & ... | semmle.label | & ... | @@ -173,8 +160,6 @@ nodes | test.c:125:11:125:12 | v6 | semmle.label | v6 | | test.c:126:13:126:14 | v6 | semmle.label | v6 | | test.c:129:22:129:22 | v | semmle.label | v | -| test.c:129:22:129:22 | v | semmle.label | v | -| test.c:129:22:129:22 | v | semmle.label | v | | test.c:130:17:130:17 | v | semmle.label | v | | test.c:135:21:135:23 | & ... | semmle.label | & ... | | test.c:135:21:135:23 | & ... | semmle.label | & ... | @@ -185,30 +170,16 @@ nodes | test.c:162:16:162:18 | & ... | semmle.label | & ... | | test.c:166:24:166:29 | call to malloc | semmle.label | call to malloc | | test.c:166:24:166:29 | call to malloc | semmle.label | call to malloc | -| test.c:166:24:166:29 | call to malloc | semmle.label | call to malloc | | test.c:167:13:167:15 | & ... | semmle.label | & ... | -| test.c:167:14:167:15 | s1 | semmle.label | s1 | -| test.c:167:14:167:15 | s1 | semmle.label | s1 | | test.c:168:16:168:18 | & ... | semmle.label | & ... | -| test.c:168:17:168:18 | s1 | semmle.label | s1 | -| test.c:168:17:168:18 | s1 | semmle.label | s1 | -| test.c:169:13:169:14 | ref arg s1 | semmle.label | ref arg s1 | -| test.c:169:13:169:14 | s1 | semmle.label | s1 | -| test.c:169:13:169:14 | s1 | semmle.label | s1 | | test.c:169:13:169:14 | s1 | semmle.label | s1 | | test.c:169:13:169:14 | s1 | semmle.label | s1 | -| test.c:171:15:171:16 | s1 | semmle.label | s1 | -| test.c:172:13:172:15 | & ... | semmle.label | & ... | -| test.c:172:14:172:15 | s2 | semmle.label | s2 | -| test.c:173:11:173:13 | & ... | semmle.label | & ... | -| test.c:173:12:173:13 | s2 | semmle.label | s2 | +| test.c:172:11:172:12 | s2 | semmle.label | s2 | +| test.c:173:13:173:14 | s2 | semmle.label | s2 | | test.c:174:13:174:14 | s2 | semmle.label | s2 | | test.c:174:13:174:14 | s2 | semmle.label | s2 | -| test.c:176:16:176:17 | s1 | semmle.label | s1 | -| test.c:177:13:177:15 | & ... | semmle.label | & ... | -| test.c:177:14:177:15 | s3 | semmle.label | s3 | -| test.c:178:11:178:13 | & ... | semmle.label | & ... | -| test.c:178:12:178:13 | s3 | semmle.label | s3 | +| test.c:177:11:177:12 | s3 | semmle.label | s3 | +| test.c:178:13:178:14 | s3 | semmle.label | s3 | | test.c:179:13:179:14 | s3 | semmle.label | s3 | | test.c:179:13:179:14 | s3 | semmle.label | s3 | | test.c:183:14:183:26 | call to aligned_alloc | semmle.label | call to aligned_alloc | @@ -232,8 +203,19 @@ nodes | test.c:226:12:226:13 | v1 | semmle.label | v1 | | test.c:227:11:227:12 | v1 | semmle.label | v1 | | test.c:228:13:228:14 | v1 | semmle.label | v1 | +| test.c:238:13:238:14 | & ... | semmle.label | & ... | +| test.c:240:16:240:19 | & ... | semmle.label | & ... | +| test.c:241:15:241:18 | & ... | semmle.label | & ... | +| test.c:241:15:241:18 | & ... | semmle.label | & ... | +| test.c:244:12:244:13 | ip | semmle.label | ip | +| test.c:246:9:246:12 | & ... | semmle.label | & ... | +| test.c:247:9:247:12 | & ... | semmle.label | & ... | +| test.c:252:16:252:18 | & ... | semmle.label | & ... | +| test.c:254:11:254:13 | ps1 | semmle.label | ps1 | +| test.c:255:11:255:13 | & ... | semmle.label | & ... | +| test.c:256:10:256:12 | ps1 | semmle.label | ps1 | +| test.c:257:10:257:12 | & ... | semmle.label | & ... | subpaths -| test.c:169:13:169:14 | s1 | test.c:129:22:129:22 | v | test.c:129:22:129:22 | v | test.c:169:13:169:14 | ref arg s1 | #select | test.c:8:12:8:14 | & ... | test.c:8:12:8:14 | & ... | test.c:8:12:8:14 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type short with 2-byte alignment. | test.c:8:12:8:14 | & ... | address-of expression | | test.c:9:10:9:12 | & ... | test.c:9:10:9:12 | & ... | test.c:9:10:9:12 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:9:10:9:12 | & ... | address-of expression | @@ -264,19 +246,23 @@ subpaths | test.c:107:11:107:12 | v4 | test.c:102:14:102:16 | & ... | test.c:107:11:107:12 | v4 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:102:14:102:16 | & ... | address-of expression | | test.c:108:13:108:14 | v4 | test.c:102:14:102:16 | & ... | test.c:108:13:108:14 | v4 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:102:14:102:16 | & ... | address-of expression | | test.c:130:17:130:17 | v | test.c:135:21:135:23 | & ... | test.c:130:17:130:17 | v | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:135:21:135:23 | & ... | address-of expression | -| test.c:130:17:130:17 | v | test.c:174:13:174:14 | s2 | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:174:13:174:14 | s2 | pointer base type | -| test.c:130:17:130:17 | v | test.c:179:13:179:14 | s3 | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:179:13:179:14 | s3 | pointer base type | +| test.c:130:17:130:17 | v | test.c:174:13:174:14 | s2 | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:174:13:174:14 | s2 | pointer base type short | +| test.c:130:17:130:17 | v | test.c:179:13:179:14 | s3 | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:179:13:179:14 | s3 | pointer base type short | | test.c:130:17:130:17 | v | test.c:189:14:189:26 | call to aligned_alloc | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:189:14:189:26 | call to aligned_alloc | call to aligned_alloc | | test.c:158:13:158:20 | & ... | test.c:158:13:158:20 | & ... | test.c:158:13:158:20 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:158:13:158:20 | & ... | address-of expression | | test.c:162:16:162:18 | & ... | test.c:162:16:162:18 | & ... | test.c:162:16:162:18 | & ... | Cast from pointer with 8-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:162:16:162:18 | & ... | address-of expression | | test.c:168:16:168:18 | & ... | test.c:166:24:166:29 | call to malloc | test.c:168:16:168:18 | & ... | Cast from pointer with 16-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:166:24:166:29 | call to malloc | call to malloc | | test.c:168:16:168:18 | & ... | test.c:168:16:168:18 | & ... | test.c:168:16:168:18 | & ... | Cast from pointer with 8-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:168:16:168:18 | & ... | address-of expression | +| test.c:173:13:173:14 | s2 | test.c:173:13:173:14 | s2 | test.c:173:13:173:14 | s2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:173:13:173:14 | s2 | pointer base type short | +| test.c:178:13:178:14 | s3 | test.c:178:13:178:14 | s3 | test.c:178:13:178:14 | s3 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:178:13:178:14 | s3 | pointer base type short | | test.c:186:13:186:14 | v1 | test.c:183:14:183:26 | call to aligned_alloc | test.c:186:13:186:14 | v1 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:183:14:183:26 | call to aligned_alloc | call to aligned_alloc | -| test.c:216:10:216:11 | p2 | test.c:216:10:216:11 | p2 | test.c:216:10:216:11 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:216:10:216:11 | p2 | pointer base type | -| test.c:217:11:217:12 | p2 | test.c:217:11:217:12 | p2 | test.c:217:11:217:12 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:217:11:217:12 | p2 | pointer base type | -| test.c:218:12:218:13 | p2 | test.c:218:12:218:13 | p2 | test.c:218:12:218:13 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:218:12:218:13 | p2 | pointer base type | -| test.c:219:13:219:14 | p2 | test.c:219:13:219:14 | p2 | test.c:219:13:219:14 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:219:13:219:14 | p2 | pointer base type | -| test.c:225:10:225:11 | v1 | test.c:222:8:222:9 | p2 | test.c:225:10:225:11 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type | -| test.c:226:12:226:13 | v1 | test.c:222:8:222:9 | p2 | test.c:226:12:226:13 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type | -| test.c:227:11:227:12 | v1 | test.c:222:8:222:9 | p2 | test.c:227:11:227:12 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type | -| test.c:228:13:228:14 | v1 | test.c:222:8:222:9 | p2 | test.c:228:13:228:14 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type | +| test.c:216:10:216:11 | p2 | test.c:216:10:216:11 | p2 | test.c:216:10:216:11 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:216:10:216:11 | p2 | pointer base type short | +| test.c:217:11:217:12 | p2 | test.c:217:11:217:12 | p2 | test.c:217:11:217:12 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:217:11:217:12 | p2 | pointer base type short | +| test.c:218:12:218:13 | p2 | test.c:218:12:218:13 | p2 | test.c:218:12:218:13 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:218:12:218:13 | p2 | pointer base type short | +| test.c:219:13:219:14 | p2 | test.c:219:13:219:14 | p2 | test.c:219:13:219:14 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:219:13:219:14 | p2 | pointer base type short | +| test.c:225:10:225:11 | v1 | test.c:222:8:222:9 | p2 | test.c:225:10:225:11 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | +| test.c:226:12:226:13 | v1 | test.c:222:8:222:9 | p2 | test.c:226:12:226:13 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | +| test.c:227:11:227:12 | v1 | test.c:222:8:222:9 | p2 | test.c:227:11:227:12 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | +| test.c:228:13:228:14 | v1 | test.c:222:8:222:9 | p2 | test.c:228:13:228:14 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | +| test.c:256:10:256:12 | ps1 | test.c:252:16:252:18 | & ... | test.c:256:10:256:12 | ps1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:252:16:252:18 | & ... | address-of expression | +| test.c:257:10:257:12 | & ... | test.c:257:10:257:12 | & ... | test.c:257:10:257:12 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:257:10:257:12 | & ... | address-of expression | diff --git a/c/cert/test/rules/EXP36-C/test.c b/c/cert/test/rules/EXP36-C/test.c index b32e2ab80f..0d0eef551a 100644 --- a/c/cert/test/rules/EXP36-C/test.c +++ b/c/cert/test/rules/EXP36-C/test.c @@ -169,13 +169,13 @@ void test_malloc_alignment_and_pointer_arithmetic() { cast_away(s1); // COMPLIANT short *s2 = s1 + 1; - (size_t *)&s2; // NON_COMPLIANT[FALSE_NEGATIVE] - (char *)&s2; // COMPLIANT + (char *)s2; // COMPLIANT + (size_t *)s2; // NON_COMPLIANT cast_away(s2); // NON_COMPLIANT short *s3 = &s1[1]; - (size_t *)&s3; // NON_COMPLIANT[FALSE_NEGATIVE] - (char *)&s3; // COMPLIANT + (char *)s3; // COMPLIANT + (size_t *)s3; // NON_COMPLIANT cast_away(s3); // NON_COMPLIANT } @@ -226,4 +226,33 @@ void test_standalone_pointer_cast_alignment(void *p1, short *p2) { (float *)v1; // NON_COMPLIANT (long *)v1; // NON_COMPLIANT (double *)v1; // NON_COMPLIANT +} + +void test_array_element_cast_alignment() { + char *acp[3]; + int *aip[3]; + + int i = 0; + char c = ' '; + char *cp = &c; // COMPLIANT + int *ip = &i; // COMPLIANT + + char **cpp = &acp; // COMPLIANT + int **ipp = &aip; // COMPLIANT + + acp[0] = cp; // COMPLIANT + acp[1] = ip; // NON_COMPLIANT[FALSE_NEGATIVE] + + cpp = &acp; // COMPLIANT + cpp = &ipp; // NON_COMPLIANT[FALSE_NEGATIVE] +} + +void test_pointer_dereference_barrier() { + short s1 = 0; + short *ps1 = &s1; + *ps1 = 1; + (char *)ps1; // COMPLIANT + (char *)&s1; // COMPLIANT + (int *)ps1; // NON_COMPLIANT + (int *)&s1; // NON_COMPLIANT } \ No newline at end of file From 0c2831004346e0b156ccb8ddfd9035e6b885c9f8 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Fri, 18 Nov 2022 05:02:17 +0100 Subject: [PATCH 13/36] Update EXP39-C test and implement query --- ...essVariableViaPointerOfIncompatibleType.md | 18 ++ ...essVariableViaPointerOfIncompatibleType.ql | 203 ++++++++++++++++++ ...iableViaPointerOfIncompatibleType.expected | 60 ++++++ ...VariableViaPointerOfIncompatibleType.qlref | 1 + c/cert/test/rules/EXP39-C/test.c | 9 +- 5 files changed, 288 insertions(+), 3 deletions(-) create mode 100644 c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md create mode 100644 c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql create mode 100644 c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected create mode 100644 c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.qlref diff --git a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md new file mode 100644 index 0000000000..90d2139747 --- /dev/null +++ b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md @@ -0,0 +1,18 @@ +# EXP39-C: Do not access a variable through a pointer of an incompatible type + +This query implements the CERT-C rule EXP39-C: + +> Do not access a variable through a pointer of an incompatible type + + +## CERT + +** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` ** + +## Implementation notes + +None + +## References + +* CERT-C: [EXP39-C: Do not access a variable through a pointer of an incompatible type](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql new file mode 100644 index 0000000000..acb835afff --- /dev/null +++ b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql @@ -0,0 +1,203 @@ +/** + * @id c/cert/do-not-access-variable-via-pointer-of-incompatible-type + * @name EXP39-C: Do not access a variable through a pointer of an incompatible type + * @description Modifying underlying pointer data through a pointer of an incompatible type can lead + * to unpredictable results. + * @kind path-problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/exp39-c + * correctness + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.controlflow.Dominance +import DataFlow::PathGraph + +/** + * The standard function `memset` and its assorted variants + */ +class MemsetFunction extends Function { + MemsetFunction() { + this.hasGlobalOrStdOrBslName("memset") + or + this.hasGlobalOrStdName("wmemset") + or + this.hasGlobalName(["__builtin_memset", "__builtin___memset_chk", "__builtin_memset_chk"]) + } +} + +class IndirectCastAnalysisUnconvertedCastExpr extends Expr { + IndirectCastAnalysisUnconvertedCastExpr() { this = any(Cast c).getUnconverted() } +} + +class IndirectCastAnalysisDereferenceSink extends Expr { + IndirectCastAnalysisDereferenceSink() { dereferenced(this) } +} + +class ReallocationFunction extends AllocationFunction { + ReallocationFunction() { exists(this.getReallocPtrArg()) } +} + +/** + * A data-flow state for a pointer which has not been reallocated. + */ +class IndirectCastDefaultFlowState extends DataFlow::FlowState { + IndirectCastDefaultFlowState() { this = "IndirectCastDefaultFlowState" } +} + +/** + * A data-flow state for a pointer which has been reallocated but + * has not yet been zeroed with a memset call. + */ +class IndirectCastReallocatedFlowState extends DataFlow::FlowState { + IndirectCastReallocatedFlowState() { this = "IndirectCastReallocatedFlowState" } +} + +/** + * A data-flow configuration to track the flow from cast expressions to either + * other cast expressions or to dereferences of pointers reallocated with a call + * to `realloc` but not cleared via a function call to `memset`. + */ +class IndirectCastConfiguration extends DataFlow::Configuration { + IndirectCastConfiguration() { this = "CastToIncompatibleTypeConfiguration" } + + override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { + state instanceof IndirectCastDefaultFlowState and + source.asExpr() instanceof IndirectCastAnalysisUnconvertedCastExpr + } + + override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { + sink.asExpr() instanceof IndirectCastAnalysisUnconvertedCastExpr and + state instanceof IndirectCastDefaultFlowState + or + sink.asExpr() instanceof IndirectCastAnalysisDereferenceSink and + state instanceof IndirectCastReallocatedFlowState and + // The memset call won't always have an edge to subsequent dereferences. + // + // Therefore, check that: + // 1) The memset call dominates the dereference. + // 2) The realloc call dominates the memset call. + // 3) There is no subsequent memset that also dominates the dereference. + // + // Currently, there is no relation between the pointer passed to memset + // and the pointer dereferenced. This unimplemented check might produce + // false-negatives when the memset call is unrelated to the reallocated memory. + not exists(FunctionCall memset, FunctionCall realloc, Expr ptr | + memset.getTarget() instanceof MemsetFunction and + realloc.getTarget() instanceof ReallocationFunction and + ptr = sink.asExpr() and + dominates(memset, ptr) and + not dominates(memset, + any(FunctionCall other | + other.getTarget() instanceof MemsetFunction and + other != memset and + dominates(other, ptr) + | + other + )) and + dominates(realloc, memset) + ) + } + + override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) { + state instanceof IndirectCastReallocatedFlowState and + exists(FunctionCall fc | + fc.getTarget() instanceof MemsetFunction and + fc.getArgument(0) = node.asExpr() + ) + } + + override predicate isAdditionalFlowStep( + DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2, + DataFlow::FlowState state2 + ) { + // track pointer flow through realloc calls and update state to `IndirectCastReallocatedFlowState` + state1 instanceof IndirectCastDefaultFlowState and + state2 instanceof IndirectCastReallocatedFlowState and + exists(FunctionCall fc | + fc.getTarget() instanceof ReallocationFunction and + node1.asExpr() = fc.getArgument(fc.getTarget().(ReallocationFunction).getReallocPtrArg()) and + node2.asExpr() = fc + ) + or + // track pointer flow through memset calls and reset state to `IndirectCastDefaultFlowState` + state1 instanceof IndirectCastReallocatedFlowState and + state2 instanceof IndirectCastDefaultFlowState and + exists(FunctionCall fc | + fc.getTarget() instanceof MemsetFunction and + node1.asExpr() = fc.getArgument(0) and + node2.asExpr() = fc + ) + } +} + +pragma[inline] +predicate areTypesSameExceptForConstSpecifiers(Type a, Type b) { + a.stripType() = b.stripType() and + a.getSize() = b.getSize() and + forall(Specifier s | s = a.getASpecifier() and not s.hasName("const") | + b.hasSpecifier(s.getName()) + ) +} + +pragma[inline] +Type compatibleTypes(Type type) { + not ( + type.isVolatile() and not result.isVolatile() + or + type.isConst() and not result.isConst() + ) and + ( + ( + result instanceof UnsignedCharType or + [result.stripTopLevelSpecifiers(), type.stripTopLevelSpecifiers()] instanceof VoidType + ) + or + not result instanceof UnsignedCharType and + not result instanceof VoidType and + ( + type.stripType() instanceof Struct and + type.getUnspecifiedType() = result.getUnspecifiedType() and + not type.getName() = "struct " and + not result.getName() = "struct " + or + not type.stripType() instanceof Struct and + ( + areTypesSameExceptForConstSpecifiers(type, result) + or + result.getSize() = type.getSize() and + ( + type instanceof Enum and result instanceof IntegralOrEnumType + or + not type instanceof PlainCharType and + ( + result.(IntegralType).isSigned() and type.(IntegralType).isSigned() + or + result.(IntegralType).isUnsigned() and type.(IntegralType).isUnsigned() + ) + or + result.(FloatingPointType).getDomain() = type.(FloatingPointType).getDomain() + ) + or + type instanceof Enum and result instanceof IntegralOrEnumType + ) + ) + ) +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, Cast cast, Type fromType, Type toType +where + not isExcluded(cast, Pointers3Package::doNotAccessVariableViaPointerOfIncompatibleTypeQuery()) and + cast.getFile().compiledAsC() and + any(IndirectCastConfiguration config).hasFlowPath(source, sink) and + // include only sinks which are not a compatible type to the associated source + source.getNode().asExpr() = cast.getUnconverted() and + fromType = cast.getUnconverted().getType().(PointerType).getBaseType() and + toType = sink.getNode().asExpr().getActualType().(PointerType).getBaseType() and + not toType = compatibleTypes(fromType) +select sink.getNode().asExpr().getUnconverted(), source, sink, + "Cast from " + fromType + " to " + toType + " results in an incompatible pointer base type." diff --git a/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected new file mode 100644 index 0000000000..0ba6202bf1 --- /dev/null +++ b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected @@ -0,0 +1,60 @@ +edges +| test.c:49:8:49:9 | s3 | test.c:50:8:50:9 | s1 | +| test.c:60:16:60:18 | E1A | test.c:61:16:61:17 | e1 | +| test.c:60:16:60:18 | E1A | test.c:65:10:65:12 | & ... | +| test.c:68:22:68:22 | v | test.c:68:41:68:41 | v | +| test.c:72:13:72:15 | & ... | test.c:68:22:68:22 | v | +| test.c:74:13:74:15 | & ... | test.c:68:22:68:22 | v | +| test.c:97:32:97:37 | call to malloc | test.c:98:40:98:41 | s2 | +| test.c:97:32:97:37 | call to malloc | test.c:98:40:98:41 | s2 | +| test.c:98:32:98:38 | call to realloc | test.c:99:3:99:4 | s3 | +| test.c:98:32:98:38 | call to realloc | test.c:100:10:100:11 | s3 | +| test.c:98:40:98:41 | s2 | test.c:98:32:98:38 | call to realloc | +nodes +| test.c:6:19:6:20 | & ... | semmle.label | & ... | +| test.c:11:10:11:11 | & ... | semmle.label | & ... | +| test.c:13:17:13:19 | & ... | semmle.label | & ... | +| test.c:15:17:15:19 | & ... | semmle.label | & ... | +| test.c:19:18:19:20 | & ... | semmle.label | & ... | +| test.c:20:20:20:22 | & ... | semmle.label | & ... | +| test.c:21:11:21:13 | & ... | semmle.label | & ... | +| test.c:26:17:26:19 | & ... | semmle.label | & ... | +| test.c:27:10:27:12 | & ... | semmle.label | & ... | +| test.c:28:13:28:15 | & ... | semmle.label | & ... | +| test.c:29:19:29:21 | & ... | semmle.label | & ... | +| test.c:30:16:30:18 | & ... | semmle.label | & ... | +| test.c:47:8:47:9 | s2 | semmle.label | s2 | +| test.c:49:8:49:9 | s3 | semmle.label | s3 | +| test.c:49:8:49:9 | s3 | semmle.label | s3 | +| test.c:50:8:50:9 | s1 | semmle.label | s1 | +| test.c:60:16:60:18 | E1A | semmle.label | E1A | +| test.c:60:16:60:18 | E1A | semmle.label | E1A | +| test.c:61:16:61:17 | e1 | semmle.label | e1 | +| test.c:65:10:65:12 | & ... | semmle.label | & ... | +| test.c:68:22:68:22 | v | semmle.label | v | +| test.c:68:41:68:41 | v | semmle.label | v | +| test.c:72:13:72:15 | & ... | semmle.label | & ... | +| test.c:72:13:72:15 | & ... | semmle.label | & ... | +| test.c:74:13:74:15 | & ... | semmle.label | & ... | +| test.c:74:13:74:15 | & ... | semmle.label | & ... | +| test.c:97:32:97:37 | call to malloc | semmle.label | call to malloc | +| test.c:97:32:97:37 | call to malloc | semmle.label | call to malloc | +| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | +| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | +| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | +| test.c:98:40:98:41 | s2 | semmle.label | s2 | +| test.c:98:40:98:41 | s2 | semmle.label | s2 | +| test.c:99:3:99:4 | s3 | semmle.label | s3 | +| test.c:100:10:100:11 | s3 | semmle.label | s3 | +subpaths +#select +| test.c:6:19:6:20 | & ... | test.c:6:19:6:20 | & ... | test.c:6:19:6:20 | & ... | Cast from float to int results in an incompatible pointer base type. | +| test.c:11:10:11:11 | & ... | test.c:11:10:11:11 | & ... | test.c:11:10:11:11 | & ... | Cast from short[2] to int results in an incompatible pointer base type. | +| test.c:13:17:13:19 | & ... | test.c:13:17:13:19 | & ... | test.c:13:17:13:19 | & ... | Cast from short[2] to short[4] results in an incompatible pointer base type. | +| test.c:19:18:19:20 | & ... | test.c:19:18:19:20 | & ... | test.c:19:18:19:20 | & ... | Cast from char to signed char results in an incompatible pointer base type. | +| test.c:29:19:29:21 | & ... | test.c:29:19:29:21 | & ... | test.c:29:19:29:21 | & ... | Cast from int to unsigned int results in an incompatible pointer base type. | +| test.c:47:8:47:9 | s2 | test.c:47:8:47:9 | s2 | test.c:47:8:47:9 | s2 | Cast from struct to struct results in an incompatible pointer base type. | +| test.c:49:8:49:9 | s3 | test.c:49:8:49:9 | s3 | test.c:49:8:49:9 | s3 | Cast from S1 to struct results in an incompatible pointer base type. | +| test.c:50:8:50:9 | s1 | test.c:50:8:50:9 | s1 | test.c:50:8:50:9 | s1 | Cast from struct to S1 results in an incompatible pointer base type. | +| test.c:68:41:68:41 | v | test.c:72:13:72:15 | & ... | test.c:68:41:68:41 | v | Cast from float to int results in an incompatible pointer base type. | +| test.c:99:3:99:4 | s3 | test.c:98:40:98:41 | s2 | test.c:99:3:99:4 | s3 | Cast from S2 to S3 results in an incompatible pointer base type. | diff --git a/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.qlref b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.qlref new file mode 100644 index 0000000000..41eb7db3b1 --- /dev/null +++ b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.qlref @@ -0,0 +1 @@ +rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP39-C/test.c b/c/cert/test/rules/EXP39-C/test.c index 1b67ec028e..8647163ea0 100644 --- a/c/cert/test/rules/EXP39-C/test.c +++ b/c/cert/test/rules/EXP39-C/test.c @@ -1,3 +1,6 @@ +#include +#include + void test_incompatible_arithmetic() { float f = 0.0f; int *p = (int *)&f; // NON_COMPLIANT - arithmetic types are not compatible @@ -14,8 +17,8 @@ void test_incompatible_arithmetic() { // char may be signed or unsigned, and so is not compatible with either char c1; (signed char *)&c1; // NON_COMPLIANT - (unsigned char *)&c1; // NON_COMPLIANT - (char *)&c1; // NON_COMPLIANT + (unsigned char *)&c1; // COMPLIANT - the underlying byte representation is always compatible + (char *)&c1; // COMPLIANT - same type // int is defined as signed, so is compatible with all the signed versions // (long, short etc. are similar) @@ -24,7 +27,7 @@ void test_incompatible_arithmetic() { (int *)&i1; // COMPLIANT (signed *)&i1; // COMPLIANT (unsigned int *)&i1; // NON_COMPLIANT - (const int *)&i1; // NON_COMPLIANT + (const int *)&i1; // COMPLIANT - adding a const specifier is permitted } struct { From 87d52ae702b688a8285f96cf8c9bd95d49aa28d3 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Fri, 18 Nov 2022 05:06:30 +0100 Subject: [PATCH 14/36] Update Pointers3.json --- rule_packages/c/Pointers3.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rule_packages/c/Pointers3.json b/rule_packages/c/Pointers3.json index 1e9038ebd7..41c1496672 100644 --- a/rule_packages/c/Pointers3.json +++ b/rule_packages/c/Pointers3.json @@ -48,9 +48,9 @@ "queries": [ { "description": "Modifying underlying pointer data through a pointer of an incompatible type can lead to unpredictable results.", - "kind": "problem", + "kind": "path-problem", "name": "Do not access a variable through a pointer of an incompatible type", - "precision": "very-high", + "precision": "high", "severity": "error", "short_name": "DoNotAccessVariableViaPointerOfIncompatibleType", "tags": [ From c0f97671fb9af3f7b16954251434bfd09634cb3b Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Fri, 18 Nov 2022 05:27:55 +0100 Subject: [PATCH 15/36] Update DoNotAccessVariableViaPointerOfIncompatibleType.ql --- .../DoNotAccessVariableViaPointerOfIncompatibleType.ql | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql index acb835afff..d9ef7e742c 100644 --- a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql +++ b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql @@ -4,7 +4,7 @@ * @description Modifying underlying pointer data through a pointer of an incompatible type can lead * to unpredictable results. * @kind path-problem - * @precision very-high + * @precision high * @problem.severity error * @tags external/cert/id/exp39-c * correctness @@ -191,7 +191,8 @@ Type compatibleTypes(Type type) { from DataFlow::PathNode source, DataFlow::PathNode sink, Cast cast, Type fromType, Type toType where - not isExcluded(cast, Pointers3Package::doNotAccessVariableViaPointerOfIncompatibleTypeQuery()) and + not isExcluded(sink.getNode().asExpr(), + Pointers3Package::doNotAccessVariableViaPointerOfIncompatibleTypeQuery()) and cast.getFile().compiledAsC() and any(IndirectCastConfiguration config).hasFlowPath(source, sink) and // include only sinks which are not a compatible type to the associated source From 0431c73369867089cfb202470082223d29f53726 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Mon, 16 Jan 2023 20:54:35 +0100 Subject: [PATCH 16/36] Resolve EXP32-C performance issue --- ...oNotAccessVolatileObjectWithNonVolatileReference.ql | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql index 407561f532..1fece60f44 100644 --- a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql +++ b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql @@ -51,6 +51,13 @@ class CastFromVolatileToNonVolatileBaseType extends Cast, UndefinedVolatilePoint } } +/** + * Holds if `va` has a subsequent `VariableAccess` which is dereferenced after access + */ +predicate hasSubsequentDereference(VariableAccess va) { + dereferenced(va.getASuccessor+().(VariableAccess)) +} + /** * An `AssignExpr` with an *lvalue* that is a pointer to a volatile base type and * and *rvalue* that is not also a pointer to a volatile base type. @@ -67,8 +74,7 @@ class NonVolatileObjectAssignedToVolatilePointer extends AssignExpr, UndefinedVo // considerations that this simple forward traversal of the control-flow graph does not account for. exists(VariableAccess va | va = this.getRValue().getAChild*().(VariableAccess).getTarget().getAnAccess() and - this.getASuccessor+() = va and - dereferenced(va) + hasSubsequentDereference(va) ) } From 8633a30bbbb3092bb69270f7c2e757f93dc92d1f Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Mon, 16 Jan 2023 22:11:17 +0100 Subject: [PATCH 17/36] Update DoNotAccessVolatileObjectWithNonVolatileReference.ql --- ...ccessVolatileObjectWithNonVolatileReference.ql | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql index 1fece60f44..47b94c5288 100644 --- a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql +++ b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql @@ -28,10 +28,10 @@ abstract class UndefinedVolatilePointerExpr extends Expr { /** * Gets the depth of a pointer's base type's volatile qualifier */ -int getAVolatileDepth(PointerType pt) { - pt.getBaseType().isVolatile() and result = 1 +int getAVolatileDepth(Type type) { + type.isVolatile() and result = 1 or - result = getAVolatileDepth(pt.getBaseType()) + 1 + result = getAVolatileDepth(type.(DerivedType).getBaseType()) + 1 } /** @@ -54,8 +54,9 @@ class CastFromVolatileToNonVolatileBaseType extends Cast, UndefinedVolatilePoint /** * Holds if `va` has a subsequent `VariableAccess` which is dereferenced after access */ +bindingset[va] predicate hasSubsequentDereference(VariableAccess va) { - dereferenced(va.getASuccessor+().(VariableAccess)) + dereferenced(pragma[only_bind_out](va).getASuccessor+()) } /** @@ -68,9 +69,9 @@ class NonVolatileObjectAssignedToVolatilePointer extends AssignExpr, UndefinedVo not i = getAVolatileDepth(this.getRValue().getType()) and i = getAVolatileDepth(this.getLValue().(VariableAccess).getTarget().getType()) ) and - // Checks for subsequent accesses to the underlying object via the original non-volatile - // pointer assigned to the volatile pointer. This heuristic can cause false-positives - // in certain instances which require more advanced reachability analysis, e.g. loops and scope + // Checks for subsequent accesses to the underlying object via the original non-volatile + // pointer assigned to the volatile pointer. This heuristic can cause false-positives + // in certain instances which require more advanced reachability analysis, e.g. loops and scope // considerations that this simple forward traversal of the control-flow graph does not account for. exists(VariableAccess va | va = this.getRValue().getAChild*().(VariableAccess).getTarget().getAnAccess() and From 469e42cb5eb3f707d241df17f2a96e5d4ae1c995 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Fri, 27 Jan 2023 02:42:18 +0100 Subject: [PATCH 18/36] Implement EXP43-C and add help files --- ...otAddOrSubtractAScaledIntegerToAPointer.ql | 42 +-- ...sVolatileObjectWithNonVolatileReference.md | 93 +++++- ...PointerToMoreStrictlyAlignedPointerType.md | 209 +++++++++++- ...essVariableViaPointerOfIncompatibleType.md | 299 +++++++++++++++++- ...essVariableViaPointerOfIncompatibleType.ql | 2 +- ...isedPointerToRestrictQualifiedParameter.md | 297 +++++++++++++++++ ...isedPointerToRestrictQualifiedParameter.ql | 141 +++++++++ ...trictPointerReferencesOverlappingObject.md | 297 +++++++++++++++++ ...trictPointerReferencesOverlappingObject.ql | 75 +++++ ...interToRestrictQualifiedParameter.expected | 5 + ...dPointerToRestrictQualifiedParameter.qlref | 1 + ...ointerReferencesOverlappingObject.expected | 6 + ...ctPointerReferencesOverlappingObject.qlref | 1 + c/cert/test/rules/EXP43-C/test.c | 51 +-- c/common/src/codingstandards/c/Pointers.qll | 41 +++ .../cpp/exclusions/c/Pointers3.qll | 32 +- rule_packages/c/Pointers3.json | 19 +- 17 files changed, 1532 insertions(+), 79 deletions(-) create mode 100644 c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.md create mode 100644 c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql create mode 100644 c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md create mode 100644 c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql create mode 100644 c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.expected create mode 100644 c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.qlref create mode 100644 c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected create mode 100644 c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.qlref diff --git a/c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql b/c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql index 5ad9fc7f6e..09ff575e6d 100644 --- a/c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql +++ b/c/cert/src/rules/ARR39-C/DoNotAddOrSubtractAScaledIntegerToAPointer.ql @@ -13,50 +13,10 @@ import cpp import codingstandards.c.cert +import codingstandards.c.Pointers import semmle.code.cpp.dataflow.TaintTracking import DataFlow::PathGraph -/** - * An expression which performs pointer arithmetic - */ -abstract class PointerArithmeticExpr extends Expr { - abstract Expr getPointer(); - - abstract Expr getOperand(); -} - -/** - * A pointer arithmetic binary operation expression. - */ -class SimplePointerArithmeticExpr extends PointerArithmeticExpr, PointerArithmeticOperation { - override Expr getPointer() { result = this.getLeftOperand() } - - override Expr getOperand() { result = this.getRightOperand() } -} - -/** - * A pointer arithmetic assignment expression. - */ -class AssignPointerArithmeticExpr extends PointerArithmeticExpr, AssignOperation { - AssignPointerArithmeticExpr() { - this instanceof AssignPointerAddExpr or - this instanceof AssignPointerSubExpr - } - - override Expr getPointer() { result = this.getLValue() } - - override Expr getOperand() { result = this.getRValue() } -} - -/** - * A pointer arithmetic array access expression. - */ -class ArrayPointerArithmeticExpr extends PointerArithmeticExpr, ArrayExpr { - override Expr getPointer() { result = this.getArrayBase() } - - override Expr getOperand() { result = this.getArrayOffset() } -} - /** * An expression which invokes the `offsetof` macro or `__builtin_offsetof` operation. */ diff --git a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md index 449644423b..11f8566e5c 100644 --- a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md +++ b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md @@ -5,13 +5,100 @@ This query implements the CERT-C rule EXP32-C: > Do not access a volatile object through a nonvolatile reference -## CERT -** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` ** +## Description + +An object that has volatile-qualified type may be modified in ways unknown to the [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) or have other unknown [side effects](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-sideeffect). Referencing a volatile object by using a non-volatile lvalue is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). The C Standard, 6.7.3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states + +> If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined. + + +See [undefined behavior 65](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_65). + +## Noncompliant Code Example + +In this noncompliant code example, a volatile object is accessed through a non-volatile-qualified reference, resulting in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior): + +```cpp +#include + +void func(void) { + static volatile int **ipp; + static int *ip; + static volatile int i = 0; + + printf("i = %d.\n", i); + + ipp = &ip; /* May produce a warning diagnostic */ + ipp = (int**) &ip; /* Constraint violation; may produce a warning diagnostic */ + *ipp = &i; /* Valid */ + if (*ip != 0) { /* Valid */ + /* ... */ + } +} +``` +The assignment `ipp = &ip` is not safe because it allows the valid code that follows to reference the value of the volatile object `i` through the non-volatile-qualified reference `ip`. In this example, the compiler may optimize out the entire `if` block because `*ip != 0` must be false if the object to which `ip` points is not volatile. + +**Implementation Details** + +This example compiles without warning on Microsoft Visual Studio 2013 when compiled in C mode (`/TC`) but causes errors when compiled in C++ mode (`/TP`). + +GCC 4.8.1 generates a warning but compiles successfully. + +## Compliant Solution + +In this compliant solution, `ip` is declared `volatile`: + +```cpp +#include + +void func(void) { + static volatile int **ipp; + static volatile int *ip; + static volatile int i = 0; + + printf("i = %d.\n", i); + + ipp = &ip; + *ipp = &i; + if (*ip != 0) { + /* ... */ + } + +} +``` + +## Risk Assessment + +Accessing an object with a volatile-qualified type through a reference with a non-volatile-qualified type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +
Rule Severity Likelihood Remediation Cost Priority Level
EXP32-C Low Likely Medium P6 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 pointer-qualifier-cast-volatile pointer-qualifier-cast-volatile-implicit Supported indirectly via MISRA C 2012 Rule 11.8
Axivion Bauhaus Suite 7.2.0 CertC-EXP32 Fully implemented
Clang 3.9 -Wincompatible-pointer-types-discards-qualifiers
Compass/ROSE
Coverity 2017.07 MISRA C 2012 Rule 11.8 Implemented
GCC 4.3.5 Can detect violations of this rule when the -Wcast-qual flag is used
Helix QAC 2022.4 C0312, C0562, C0563, C0673, C0674
Klocwork 2022.4 CERT.EXPR.VOLATILE.ADDR CERT.EXPR.VOLATILE.ADDR.PARAM CERT.EXPR.VOLATILE.PTRPTR
LDRA tool suite 9.7.1 344 S Partially implemented
Parasoft C/C++test 2022.2 CERT_C-EXP32-a A cast shall not remove any 'const' or 'volatile' qualification from the type of a pointer or reference
Polyspace Bug Finder CERT C: Rule EXP32-C Checks for cast to pointer that removes const or volatile qualification (rule fully covered)
PRQA QA-C 9.7 0312,562,563,673,674 Fully implemented
RuleChecker 22.04 pointer-qualifier-cast-volatile pointer-qualifier-cast-volatile-implicit Supported indirectly via MISRA C 2012 Rule 11.8
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP32-C). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
ISO/IEC TR 24772:2013 Pointer Casting and Pointer Type Changes \[HFC\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Type System \[IHN\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 11.8 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C EXP55-CPP. Do not access a cv-qualified object through a cv-unqualified type Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] 6.7.3, "Type Qualifiers"
+ ## Implementation notes -None +In limited cases, this query can raise false-positives for assignment of volatile objects and subsequent accesses of those objects via non-volatile pointers. ## References diff --git a/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md index 870ae704aa..8c56801e01 100644 --- a/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md +++ b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md @@ -5,9 +5,214 @@ This query implements the CERT-C rule EXP36-C: > Do not cast pointers into more strictly aligned pointer types -## CERT -** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` ** +## Description + +Do not convert a pointer value to a pointer type that is more strictly aligned than the referenced type. Different alignments are possible for different types of objects. If the type-checking system is overridden by an explicit cast or the pointer is converted to a void pointer (`void *`) and then to a different type, the alignment of an object may be changed. + +The C Standard, 6.3.2.3, paragraph 7 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states + +> A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. + + +See [undefined behavior 25.](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_25) + +If the misaligned pointer is dereferenced, the program may [terminate abnormally](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). On some architectures, the cast alone may cause a loss of information even if the value is not dereferenced if the types involved have differing alignment requirements. + +## Noncompliant Code Example + +In this noncompliant example, the `char` pointer `&c` is converted to the more strictly aligned `int` pointer `ip`. On some [implementations](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation), `cp` will not match `&c`. As a result, if a pointer to one object type is converted to a pointer to a different object type, the second object type must not require stricter alignment than the first. + +```cpp +#include + +void func(void) { + char c = 'x'; + int *ip = (int *)&c; /* This can lose information */ + char *cp = (char *)ip; + + /* Will fail on some conforming implementations */ + assert(cp == &c); +} + +``` + +## Compliant Solution (Intermediate Object) + +In this compliant solution, the `char` value is stored into an object of type `int` so that the pointer's value will be properly aligned: + +```cpp +#include + +void func(void) { + char c = 'x'; + int i = c; + int *ip = &i; + + assert(ip == &i); +} +``` + +## Noncompliant Code Example + +The C Standard allows any object pointer to be cast to and from `void *`. As a result, it is possible to silently convert from one pointer type to another without the compiler diagnosing the problem by storing or casting a pointer to `void *` and then storing or casting it to the final type. In this noncompliant code example, `loop_function()` is passed the `char` pointer `char_ptr` but returns an object of type `int` pointer: + +```cpp +int *loop_function(void *v_pointer) { + /* ... */ + return v_pointer; +} + +void func(char *char_ptr) { + int *int_ptr = loop_function(char_ptr); + + /* ... */ +} +``` +This example compiles without warning using GCC 4.8 on Ubuntu Linux 14.04. However, `int_pointer` can be more strictly aligned than an object of type `char *`. + +## Compliant Solution + +Because the input parameter directly influences the return value, and `loop_function()` returns an object of type `int *`, the formal parameter `v_pointer` is redeclared to accept only an object of type `int *`: + +```cpp +int *loop_function(int *v_pointer) { + /* ... */ + return v_pointer; +} + +void func(int *loop_ptr) { + int *int_ptr = loop_function(loop_ptr); + + /* ... */ +} +``` + +## Noncompliant Code Example + +Some architectures require that pointers are correctly aligned when accessing objects larger than a byte. However, it is common in system code that unaligned data (for example, the network stacks) must be copied to a properly aligned memory location, such as in this noncompliant code example: + +```cpp +#include + +struct foo_header { + int len; + /* ... */ +}; + +void func(char *data, size_t offset) { + struct foo_header *tmp; + struct foo_header header; + + tmp = (struct foo_header *)(data + offset); + memcpy(&header, tmp, sizeof(header)); + + /* ... */ +} +``` +Assigning an unaligned value to a pointer that references a type that needs to be aligned is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). An [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) may notice, for example, that `tmp` and `header` must be aligned and use an inline `memcpy()` that uses instructions that assume aligned data. + +## Compliant Solution + +This compliant solution avoids the use of the `foo_header` pointer: + +```cpp +#include + +struct foo_header { + int len; + /* ... */ +}; + +void func(char *data, size_t offset) { + struct foo_header header; + memcpy(&header, data + offset, sizeof(header)); + + /* ... */ +} +``` + +## Exceptions + +**EXP36-C-EX1:** Some hardware architectures have relaxed requirements with regard to pointer alignment. Using a pointer that is not properly aligned is correctly handled by the architecture, although there might be a performance penalty. On such an architecture, improper pointer alignment is permitted but remains an efficiency problem. + +The x86 32- and 64-bit architectures usually impose only a performance penalty for violations of this rule, but under some circumstances, noncompliant code can still exhibit undefined behavior. Consider the following program: + +```cpp +#include +#include + +#define READ_UINT16(ptr) (*(uint16_t *)(ptr)) +#define WRITE_UINT16(ptr, val) (*(uint16_t *)(ptr) = (val)) + +void compute(unsigned char *b1, unsigned char *b2, + int value, int range) { + int i; + for (i = 0; i < range; i++) { + int newval = (int)READ_UINT16(b1) + value; + WRITE_UINT16(b2, newval); + b1 += 2; + b2 += 2; + } +} + +int main() { + unsigned char buffer1[1024]; + unsigned char buffer2[1024]; + printf("Compute something\n"); + compute(buffer1 + 3, buffer2 + 1, 42, 500); + return 0; +} +``` +This code tries to read short ints (which are 16 bits long) from odd pairs in a character array, which violates this rule. On 32- and 64-bit x86 platforms, this program should run to completion without incident. However, the program aborts with a SIGSEGV due to the unaligned reads on a 64-bit platform running Debian Linux, when compiled with GCC 4.9.4 using the flags `-O3` or `-O2 -ftree-loop-vectorize -fvect-cost-model`. + +If a developer wishes to violate this rule and use undefined behavior, they must not only ensure that the hardware guarantees the behavior of the object code, but they must also ensure that their compiler, along with its optimizer, also respect these guarantees. + +**EXP36-C-EX2**: If a pointer is known to be correctly aligned to the target type, then a cast to that type is permitted. There are several cases where a pointer is known to be correctly aligned to the target type. The pointer could point to an object declared with a suitable alignment specifier. It could point to an object returned by `aligned_alloc()`, `calloc()`, `malloc()`, or `realloc()`, as per the C standard, section 7.22.3, paragraph 1 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\]. + +This compliant solution uses the alignment specifier, which is new to C11, to declare the `char` object `c` with the same alignment as that of an object of type `int`. As a result, the two pointers reference equally aligned pointer types: + +```cpp +#include +#include + +void func(void) { + /* Align c to the alignment of an int */ + alignas(int) char c = 'x'; + int *ip = (int *)&c; + char *cp = (char *)ip; + /* Both cp and &c point to equally aligned objects */ + assert(cp == &c); +} +``` + +## Risk Assessment + +Accessing a pointer or an object that is not properly aligned can cause a program to crash or give erroneous information, or it can cause slow pointer accesses (if the architecture allows misaligned accesses). + +
Rule Severity Likelihood Remediation Cost Priority Level
EXP36-C Low Probable Medium P4 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 pointer-cast-alignment Fully checked
Axivion Bauhaus Suite 7.2.0 CertC-EXP36
CodeSonar 7.2p0 LANG.CAST.PC.OBJ Cast: Object Pointers
Compass/ROSE Can detect violations of this rule. However, it does not flag explicit casts to void \* and then back to another pointer type
Coverity 2017.07 MISRA C 2004 Rule 11.4 MISRA C 2012 Rule 11.1 MISRA C 2012 Rule 11.2 MISRA C 2012 Rule 11.5 MISRA C 2012 Rule 11.7 Implemented
ECLAIR 1.2 CC2.EXP36 Fully implemented
EDG
GCC 4.3.5 Can detect some violations of this rule when the -Wcast-align flag is used
Helix QAC 2022.4 C0326, C3305 C++3033, C++3038
Klocwork 2022.4 MISRA.CAST.OBJ_PTR_TO_OBJ_PTR.2012
LDRA tool suite 9.7.1 94 S, 606 S Partially implemented
Parasoft C/C++test 2022.2 CERT_C-EXP36-a A cast should not be performed between a pointer to object type and a different pointer to object type
PC-lint Plus 1.4 2445 Partially supported: reports casts directly from a pointer to a less strictly aligned type to a pointer to a more strictly aligned type
Polyspace Bug Finder R2022b CERT C: Rule EXP36-C Checks for source buffer misaligned with destination buffer (rule fully covered)
PRQA QA-C 9.7 0326, 3305 Fully implemented
PRQA QA-C++ 4.4 3033, 3038
PVS-Studio 7.22 V548 , V641 , V1032
RuleChecker 22.04 pointer-cast-alignment Fully checked
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP36-C). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
CERT C VOID EXP56-CPP. Do not cast pointers into more strictly aligned pointer types Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Pointer Casting and Pointer Type Changes \[HFC\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Converting pointer values to more strictly aligned pointer types \[alignconv\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 11.1 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 11.2 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 11.5 (advisory) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 11.7 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ Bryant 2003 \]
\[ ISO/IEC 9899:2011 \] 6.3.2.3, "Pointers"
\[ Walfridsson 2003 \] Aliasing, Pointer Casts and GCC 3.3
+ ## Implementation notes diff --git a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md index 90d2139747..8d8b132c4d 100644 --- a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md +++ b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md @@ -5,9 +5,304 @@ This query implements the CERT-C rule EXP39-C: > Do not access a variable through a pointer of an incompatible type -## CERT -** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` ** +## Description + +Modifying a variable through a pointer of an incompatible type (other than `unsigned char`) can lead to unpredictable results. Subclause 6.2.7 of the C Standard states that two types may be distinct yet compatible and addresses precisely when two distinct types are compatible. + +This problem is often caused by a violation of aliasing rules. The C Standard, 6.5, paragraph 7 \[ [ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011) \], specifies those circumstances in which an object may or may not be aliased. + +> An object shall have its stored value accessed only by an lvalue expression that has one of the following types: + + +* a type compatible with the effective type of the object, +* a qualified version of a type compatible with the effective type of the object, +* a type that is the signed or unsigned type corresponding to the effective type of the object, +* a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object, +* an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or +* a character type. +Accessing an object by means of any other [lvalue](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-lvalue) expression (other than `unsigned char`) is [undefined behavior 37](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_37). + +## Noncompliant Code Example + +In this noncompliant example, an object of type `float` is incremented through an `int *`. The programmer can use the unit in the last place to get the next representable value for a floating-point type. However, accessing an object through a pointer of an incompatible type is undefined behavior. + +```cpp +#include + +void f(void) { + if (sizeof(int) == sizeof(float)) { + float f = 0.0f; + int *ip = (int *)&f; + (*ip)++; + printf("float is %f\n", f); + } +} + +``` + +## Compliant Solution + +In this compliant solution, the standard C function `nextafterf()` is used to round toward the highest representable floating-point value: + +```cpp +#include +#include +#include + +void f(void) { + float f = 0.0f; + f = nextafterf(f, FLT_MAX); + printf("float is %f\n", f); +} + +``` + +## Noncompliant Code Example + +In this noncompliant code example, an array of two values of type `short` is treated as an integer and assigned an integer value. The resulting values are indeterminate. + +```cpp +#include + +void func(void) { + short a[2]; + a[0]=0x1111; + a[1]=0x1111; + + *(int *)a = 0x22222222; + + printf("%x %x\n", a[0], a[1]); +} +``` +When translating this code, an implementation can assume that no access through an integer pointer can change the array `a`, consisting of shorts. Consequently, `printf()` may be called with the original values of `a[0]` and `a[1]`. + +**Implementation Details** + +Recent versions of GCC turn on the option `-fstrict-aliasing,` which allows alias-based optimizations, by default with `-O2`. Some architectures then print "1111 1111" as a result. Without optimization, the executable generates the *expected* output "2222 2222." + +To disable optimizations based on alias analysis for faulty legacy code, the option `-fno-strict-aliasing` can be used as a workaround. The option `-Wstrict-aliasing,` which is included in `-Wall,` warns about some, but not all, violations of aliasing rules when `-fstrict-aliasing` is active. + +When GCC 3.4.6 compiles this code with optimization, the assignment through the aliased pointer is effectively eliminated. + +## Compliant Solution + +This compliant solution uses a `union` type that includes a type compatible with the effective type of the object: + +```cpp +#include + +void func(void) { + union { + short a[2]; + int i; + } u; + + u.a[0]=0x1111; + u.a[1]=0x1111; + u.i = 0x22222222; + + printf("%x %x\n", u.a[0], u.a[1]); + + /* ... */ +} +``` +The C standard states: + +> If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called “type punning”). This might be a trap representation. + + +The call to `printf()` typically outputs "2222 2222". However, there is no guarantee that this will be true; the object representations of `a` and `i` are unspecified and need not be compatible in this way, despite this operation being commonly accepted as an implementation extension. (See [unspecified behavior 11](https://wiki.sei.cmu.edu/confluence/display/c/DD.+Unspecified+Behavior#DD.UnspecifiedBehavior-unspecifiedbehavior11).) + +## Noncompliant Code Example + +In this noncompliant code example, a `gadget` object is allocated, then `realloc()` is called to create a `widget` object using the memory from the `gadget` object. Although reusing memory to change types is acceptable, accessing the memory copied from the original object is undefined behavior. + +```cpp +#include + +struct gadget { + int i; + double d; + char *p; +}; + +struct widget { + char *q; + int j; + double e; +}; + +void func(void) { + struct gadget *gp; + struct widget *wp; + + gp = (struct gadget *)malloc(sizeof(struct gadget)); + if (!gp) { + /* Handle error */ + } + /* ... Initialize gadget ... */ + wp = (struct widget *)realloc(gp, sizeof(struct widget)); + if (!wp) { + free(gp); + /* Handle error */ + } + if (wp->j == 12) { + /* ... */ + } + /* ... */ + free(wp); +} +``` + +## Compliant Solution + +This compliant solution reuses the memory from the `gadget` object but reinitializes the memory to a consistent state before reading from it: + +```cpp +#include +#include + +struct gadget { + int i; + double d; + char *p; +}; + +struct widget { + char *q; + int j; + double e; +}; + +void func(void) { + struct gadget *gp; + struct widget *wp; + + gp = (struct gadget *)malloc(sizeof (struct gadget)); + if (!gp) { + /* Handle error */ + } + /* ... */ + wp = (struct widget *)realloc(gp, sizeof(struct widget)); + if (!wp) { + free(gp); + /* Handle error */ + } + memset(wp, 0, sizeof(struct widget)); + /* ... Initialize widget ... */ + + if (wp->j == 12) { + /* ... */ + } + /* ... */ + free(wp); +} +``` + +## Noncompliant Code Example + +According to the C Standard, 6.7.6.2 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], using two or more incompatible arrays in an expression is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See also [undefined behavior 76](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_76).) + +For two array types to be compatible, both should have compatible underlying element types, and both size specifiers should have the same constant value. If either of these properties is violated, the resulting behavior is undefined. + +In this noncompliant code example, the two arrays `a` and `b` fail to satisfy the equal size specifier criterion for array compatibility. Because `a` and `b` are not equal, writing to what is believed to be a valid member of `a` might exceed its defined memory boundary, resulting in an arbitrary memory overwrite. + +```cpp +enum { ROWS = 10, COLS = 15 }; + +void func(void) { + int a[ROWS][COLS]; + int (*b)[ROWS] = a; +} +``` +Most compilers will produce a warning diagnostic if the two array types used in an expression are incompatible. + +## Compliant Solution + +In this compliant solution, `b` is declared to point to an array with the same number of elements as `a`, satisfying the size specifier criterion for array compatibility: + +```cpp +enum { ROWS = 10, COLS = 15 }; + +void func(void) { + int a[ROWS][COLS]; + int (*b)[COLS] = a; +} +``` + +## Risk Assessment + +Optimizing for performance can lead to aliasing errors that can be quite difficult to detect. Furthermore, as in the preceding example, unexpected results can lead to buffer overflow attacks, bypassing security checks, or unexpected execution. + +
Recommendation Severity Likelihood Remediation Cost Priority Level
EXP39-C Medium Unlikely High P2 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Helix QAC 2022.4 C0310, C0751, C3305 C++3017, C++3030, C++3033
Klocwork 2022.4 MISRA.CAST.FUNC_PTR.2012 MISRA.CAST.INCOMPLETE_PTR_TO_ANY.2012 MISRA.CAST.OBJ_PTR_TO_NON_INT.2012 MISRA.CAST.OBJ_PTR_TO_OBJ_PTR.2012
LDRA tool suite 9.7.1 94 S, 554 S Partially implemented
Parasoft C/C++test 2022.2 CERT_C-EXP39-a CERT_C-EXP39-b CERT_C-EXP39-c CERT_C-EXP39-d CERT_C-EXP39-e CERT_C-EXP39-f There shall be no implicit conversions from integral to floating type A cast should not be performed between a pointer to object type and a different pointer to object type Avoid accessing arrays and pointers out of bounds Avoid buffer overflow from tainted data due to defining incorrect format limits Avoid buffer read overflow from tainted data Avoid buffer write overflow from tainted data
Polyspace Bug Finder R2022b CERT C: Rule EXP39-C Checks for cast to pointer pointing to object of different type (rule partially covered)
PRQA QA-C 9.7 0310, 0751, 3305 Partially implemented
PRQA QA-C++ 4.4 3017, 3030, 3033
PVS-Studio 7.22 V580
+ + +## Related Vulnerabilities + +Search for vulnerabilities resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP39-C). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
ISO/IEC TS 17961 Accessing an object through a pointer to an incompatible type \[ptrcomp\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-119 , Improper Restriction of Operations within the Bounds of a Memory Buffer 2017-05-18: CERT: Partial overlap
CWE 2.11 CWE-125 , Out-of-bounds Read 2017-05-18: CERT: Partial overlap
CWE 2.11 CWE-704 2017-06-14: CERT: Rule subset of CWE
+ + +## CERT-CWE Mapping Notes + +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes + +**CWE-119 and EXP39-C** + +Independent( ARR30-C, ARR38-C, ARR32-C, INT30-C, INT31-C, EXP39-C, EXP33-C, FIO37-C) STR31-C = Subset( Union( ARR30-C, ARR38-C)) STR32-C = Subset( ARR38-C) + +Intersection( EXP39-C, CWE-119) = + +* Reading memory assigned to one type, but being accessed through a pointer to a larger type. +EXP39-C – CWE-119 = +* Writing to memory assigned to one type, but accessed through a pointer to a larger type +* Reading memory assigned to one type, but being accessed through a pointer to a smaller (or equal-sized) type +CWE-119 – EXP39-C = +* Reading beyond a buffer using a means other than accessing a variable through an incompatible pointer. +**CWE-123 and EXP39-C** + +Intersection( CWE-123, EXP39-C) = Ø + +EXP39-C allows overflowing a (small) buffer, but not arbitrary memory writes. (Possibly an arbitrary-memory write exploit could be devised using a “perfect storm” of incompatible types, but this would be uncommon in practice.) + +**CWE-125 and EXP39-C** + +Independent( ARR30-C, ARR38-C, EXP39-C, INT30-C) STR31-C = Subset( Union( ARR30-C, ARR38-C)) STR32-C = Subset( ARR38-C) + +Intersection( EXP39-C, CWE-125) = + +* Reading memory assigned to one type, but being accessed through a pointer to a larger type. +ESP39-C – CWE-125 = +* Reading memory assigned to one type, but being accessed through a pointer to a smaller (or equal-sized) type +CWE-125 – EXP39-C = +* Reading beyond a buffer using a means other than accessing a variable through an incompatible pointer. +**CWE-188 and EXP39-C** + +Intersection( CWE-188, EXP39-C) = Ø + +CWE-188 appears to be about making assumptions about the layout of memory between distinct variables (that are not part of a larger struct or array). Such assumptions typically involve pointer arithmetic (which violates ARR30-C). EXP39-C involves only one object in memory being (incorrectly) interpreted as if it were another object. EG a float being treated as an int (usually via pointers and typecasting) + +**CWE-704 and EXP39-C** + +CWE-704 = Union( EXP39-C, list) where list = + +* Incorrect (?) typecast that is not incompatible + +## Bibliography + +
\[ Acton 2006 \] " Understanding Strict Aliasing "
GCC Known Bugs "C Bugs, Aliasing Issues while Casting to Incompatible Types"
\[ ISO/IEC 9899:2011 \] 6.5, "Expressions" 6.7.6.2, "Array Declarators"
\[ Walfridsson 2003 \] Aliasing, Pointer Casts and GCC 3.3
+ ## Implementation notes diff --git a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql index d9ef7e742c..760603de6b 100644 --- a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql +++ b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql @@ -13,7 +13,7 @@ import cpp import codingstandards.c.cert -import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.ir.dataflow.DataFlow import semmle.code.cpp.controlflow.Dominance import DataFlow::PathGraph diff --git a/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.md b/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.md new file mode 100644 index 0000000000..827aad74c0 --- /dev/null +++ b/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.md @@ -0,0 +1,297 @@ +# EXP43-C: Do not pass aliased pointers to restrict-qualified parameters + +This query implements the CERT-C rule EXP43-C: + +> Avoid undefined behavior when using restrict-qualified pointers + + + +## Description + +An object that is accessed through a `restrict`-qualified pointer has a special association with that pointer. This association requires that all accesses to that object use, directly or indirectly, the value of that particular pointer. The intended use of the restrict qualifier is to promote optimization, and deleting all instances of the qualifier from a program does not change its meaning (that is, observable behavior). In the absence of this qualifier, other pointers can alias this object. Caching the value in an object designated through a `restrict`-qualified pointer is safe at the beginning of the block in which the pointer is declared because no preexisting aliases may also be used to reference that object. The cached value must be restored to the object by the end of the block, where preexisting aliases again become available. New aliases may be formed within the block, but these must all depend on the value of the `restrict`-qualified pointer so that they can be identified and adjusted to refer to the cached value. For a `restrict`-qualified pointer at file scope, the block is the body of each function in the file \[[Walls 2006](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography)\]. Developers should be aware that C++ does not support the `restrict` qualifier, but some C++ compiler implementations support an equivalent qualifier as an extension. + +The C Standard \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\] identifies the following [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior): + +> A restrict-qualified pointer is assigned a value based on another restricted pointer whose associated block neither began execution before the block associated with this pointer, nor ended before the assignment (6.7.3.1). + + +This is an oversimplification, however, and it is important to review the formal definition of *restrict* in subclause 6.7.3.1 of the C Standard to properly understand undefined behaviors associated with the use of `restrict`-qualified pointers. + +## Overlapping Objects + +The `restrict` qualifier requires that the pointers do not reference overlapping objects. If the objects referenced by arguments to functions overlap (meaning the objects share some common memory addresses), the behavior is undefined. + +**Noncompliant Code Example** + +This code example is noncompliant because an assignment is made between two `restrict`-qualified pointers in the same scope: + +```cpp +int *restrict a; +int *restrict b; + +extern int c[]; + +int main(void) { + c[0] = 17; + c[1] = 18; + a = &c[0]; + b = &c[1]; + a = b; /* Undefined behavior */ + /* ... */ +} +``` +Note that [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) occurs only when `a` is assigned to `b`. It is valid for `a` and `b` to point into the same array object, provided the range of elements accessed through one of the pointers does not overlap with the range of elements accessed through the other pointer. + +**Compliant Solution** + +One way to eliminate the [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) is simply to remove the `restrict-`qualification from the affected pointers: + +```cpp +int *a; +int *b; + +extern int c[]; + +int main(void) { + c[0] = 17; + c[1] = 18; + a = &c[0]; + b = &c[1]; + a = b; /* Defined behavior */ + /* ... */ +} +``` + +## restrict-Qualified Function Parameters + +When calling functions that have `restrict`-qualified function parameters, it is important that the pointer arguments do not reference overlapping objects if one or more of the pointers are used to modify memory. Consequently, it is important to understand the semantics of the function being called. + +**Noncompliant Code Example** + +In this noncompliant code example, the function `f()` accepts three parameters. The function copies `n` integers from the `int` array referenced by the `restrict`-qualified pointer `p` to the `int` array referenced by the `restrict`-qualified pointer `q`. Because the destination array is modified during each execution of the function (for which `n` is nonzero), if the array is accessed through one of the pointer parameters, it cannot also be accessed through the other. Declaring these function parameters as `restrict`-qualified pointers allows aggressive optimization by the compiler but can also result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) if these pointers refer to overlapping objects. + +```cpp +#include +void f(size_t n, int *restrict p, const int *restrict q) { + while (n-- > 0) { + *p++ = *q++; + } +} + +void g(void) { + extern int d[100]; + /* ... */ + f(50, d + 1, d); /* Undefined behavior */ +} +``` +The function `g()` declares an array `d` consisting of 100 `int` values and then invokes `f()` to copy memory from one area of the array to another. This call has undefined behavior because each of `d[1]` through `d[49]` is accessed through both `p` and `q`. + +**Compliant Solution** + +In this compliant solution, the function `f()` is unchanged but the programmer has ensured that none of the calls to `f()` result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). The call to `f()` in `g()` is valid because the storage allocated to `d` is effectively divided into two disjoint objects. + +```cpp +#include +void f(size_t n, int *restrict p, const int *restrict q) { + while (n-- > 0) { + *p++ = *q++; + } +} + +void g(void) { + extern int d[100]; + /* ... */ + f(50, d + 50, d); /* Defined behavior */ +} +``` +**Noncompliant Code Example** + +In this noncompliant code example, the function `add()` adds the integer array referenced by the `restrict`-qualified pointers lhs to the integer array referenced by the `restrict`-qualified pointer `rhs` and stores the result in the `restrict`-qualified pointer referenced by `res`. The function `f()` declares an array `a` consisting of 100 `int` values and then invokes `add()` to copy memory from one area of the array to another. The call `add(100, a, a, a)` has [undefined behavior ](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior)because the object modified by `res` is accessed by lhs and `rhs`. + +```cpp +#include + +void add(size_t n, int *restrict res, const int *restrict lhs, + const int *restrict rhs) { + for (size_t i = 0; i < n; ++i) { + res[i] = lhs[i] + rhs[i]; + } +} + +void f(void) { + int a[100]; + add(100, a, a, a); /* Undefined behavior */ +} +``` +**Compliant Solution** + +In this compliant solution, an unmodified object is aliased through two restricted pointers. Because `a` and `b` are disjoint arrays, a call of the form `add(100, a, b, b)` has defined behavior, because array `b` is not modified within function `add`. + +```cpp +#include +void add(size_t n, int *restrict res, const int *restrict lhs, + const int *restrict rhs) { + for (size_t i = 0; i < n; ++i) { + res[i] = lhs[i] + rhs[i]; + } +} + +void f(void) { + int a[100]; + int b[100]; + add(100, a, b, b); /* Defined behavior */ +} +``` + +## Invoking Library Functions with restrict-Qualified Pointers + +Ensure that `restrict`-qualified source and destination pointers do not reference overlapping objects when invoking library functions. For example, the following table lists C standard library functions that copy memory from a source object referenced by a `restrict`-qualified pointer to a destination object that is also referenced by a `restrict`-qualified pointer: + +
Standard C Annex K
strcpy() strcpy_s()
strncpy() strncpy_s()
strcat() strcat_s()
strncat() strncat_s()
memcpy() memcpy_s()
strtok_s()
+If the objects referenced by arguments to functions overlap (meaning the objects share some common memory addresses), the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See also [undefined behavior 68](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_68).) The result of the functions is unknown, and data may be corrupted. As a result, these functions must never be passed pointers to overlapping objects. If data must be copied between objects that share common memory addresses, a copy function guaranteed to work on overlapping memory, such as `memmove()`, should be used. + + +**Noncompliant Code Example** + +In this noncompliant code example, the values of objects referenced by `ptr1` and `ptr2` become unpredictable after the call to `memcpy()` because their memory areas overlap: + +```cpp +#include + +void func(void) { + char c_str[]= "test string"; + char *ptr1 = c_str; + char *ptr2; + + ptr2 = ptr1 + 3; + /* Undefined behavior because of overlapping objects */ + memcpy(ptr2, ptr1, 6); + /* ... */ +} +``` +**Compliant Solution** + +In this compliant solution, the call to `memcpy()` is replaced with a call to `memmove()`. The `memmove()` function performs the same operation as `memcpy()` when the memory regions do not overlap. When the memory regions do overlap, the *n* characters from the object pointed to by the source (`ptr1`) are first copied into a temporary array of *n* characters that does not overlap the objects pointed to by the destination (`ptr2`) or the source. The *n* characters from the temporary array are then copied into the object pointed to by the destination. + +```cpp +#include + +void func(void) { + char c_str[]= "test string"; + char *ptr1 = c_str; + char *ptr2; + + ptr2 = ptr1 + 3; + memmove(ptr2, ptr1, 6); /* Replace call to memcpy() */ + /* ... */ +} +``` +Similar solutions using `memmove()` can replace the string functions as long as care is taken regarding the byte size of the characters and proper null-termination of the copied string. + +## Calling Functions with restrict-Qualified Pointer to a const-Qualified Type + +Ensure that functions that accept a `restrict`-qualified pointer to a `const`-qualified type do not modify the object referenced by that pointer. Formatted input and output standard library functions frequently fit this description. The following table lists of some of the common functions for which the format argument is a `restrict`-qualified pointer to a `const`-qualified type. + +
Standard C Annex K
printf() printf_s()
scanf() scanf_s()
sprintf() sprintf_s()
snprintf() snprintf_s()
+For formatted output functions such as `printf()`, it is unlikely that a programmer would modify the format string. However, an attacker may attempt to do so if a program violates [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) and passes tainted values as part of the format string. + + +**Noncompliant Code Example** + +In this noncompliant code example, the programmer is attempting to overwrite the format string with a string value read in from `stdin` such as `"%d%f 1 3.3"` and use the resulting modified string of `"%s%d%f"` to input the subsequent values of `1` and `3.3`: + +```cpp +#include + +void func(void) { + int i; + float x; + char format[100] = "%s"; + /* Undefined behavior */ + int n = scanf(format, format + 2, &i, &x); + /* ... */ +} +``` +**Compliant Solution** + +The intended results are achieved by this compliant solution: + +```cpp +#include + +void func(void) { + int i; + float x; + int n = scanf("%d%f", &i, &x); /* Defined behavior */ + /* ... */ +} +``` + +## Outer-to-Inner Assignments between Restricted Pointers + +The assignment between `restrict`-qualified pointers declared in an inner nested block from an outer block has defined behavior. + +**Noncompliant Code Example** + +The assignment of `restrict`-qualified pointers to other `restrict`-qualified pointers within the same block has [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior): + +```cpp +void func(void) { + int *restrict p1; + int *restrict q1; + + int *restrict p2 = p1; /* Undefined behavior */ + int *restrict q2 = q1; /* Undefined behavior */ + } +``` +**Compliant Solution** + +The intended results can be achieved using an inner nested block, as shown in this compliant solution: + +```cpp +void func(void) { + int *restrict p1; + int *restrict q1; + { /* Added inner block */ + int *restrict p2 = p1; /* Valid, well-defined behavior */ + int *restrict q2 = q1; /* Valid, well-defined behavior */ + } +} +``` + +## Risk Assessment + +The incorrect use of `restrict`-qualified pointers can result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) that might be [exploited](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) to cause data integrity violations. + +
Rule Severity Likelihood Remediation Cost Priority Level
EXP43-C Medium Probable High P4 L3
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP43-C). + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 restrict Supported indirectly via MISRA C 2012 Rule 8.14.
CodeSonar 7.2p0 LANG.TYPE.RESTRICT Restrict qualifier used
Coverity 2017.07 MISRA C 2012 Rule 8.14 Partially implemented
GCC 8.1 -Wrestrict Fully implemented
Helix QAC 2022.4 C1057
Klocwork 2022.4 MISRA.TYPE.RESTRICT.QUAL.2012
LDRA tool suite 9.7.1 480 S, 489 S, 613 S Enhanced enforcement
Parasoft C/C++test 2022.2 CERT_C-EXP43-a The restrict type qualifier shall not be used
PC-lint Plus 1.4 586 Assistance provided: reports use of the restrict keyword
Polyspace Bug Finder R2022b CERT C: Rule EXP43-C Checks for copy of overlapping memory (rule partially covered)
PRQA QA-C 9.7 1057
RuleChecker 22.04 restrict Supported indirectly via MISRA C 2012 Rule 8.14.
SonarQube C/C++ Plugin 3.11 S1836 Implements MISRA C:2012 Rule 8.14 to flag uses of restrict
+ + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard FIO30-C. Exclude user input from format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Passing Parameters and Return Values \[CSJ\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Passing pointers into the same object as arguments to different restrict-qualified parameters \[restrict\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 8.14 (required) 1 Prior to 2018-01-12: CERT: Unspecified Relationship
+1. MISRA Rule 8.14 prohibits the use of the restrict keyword except in C standard library functions. + + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] 6.7.3.1, "Formal Definition of restrict "
\[ Walls 2006 \]
+ + +## Implementation notes + +None + +## References + +* CERT-C: [EXP43-C: Avoid undefined behavior when using restrict-qualified pointers](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql b/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql new file mode 100644 index 0000000000..60cbfa7756 --- /dev/null +++ b/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql @@ -0,0 +1,141 @@ +/** + * @id c/cert/do-not-pass-alised-pointer-to-restrict-qualified-parameter + * @name EXP43-C: Do not pass aliased pointers to restrict-qualified parameters + * @description Passing an aliased pointer to a restrict-qualified parameter is undefined behavior. + * @kind problem + * @precision medium + * @problem.severity error + * @tags external/cert/id/exp43-c + * correctness + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert +import codingstandards.c.Pointers +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.pointsto.PointsTo +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** + * A type that is a pointer or array type. + */ +class PointerOrArrayType extends DerivedType { + PointerOrArrayType() { + this.stripTopLevelSpecifiers() instanceof PointerType or + this.stripTopLevelSpecifiers() instanceof ArrayType + } +} + +/** + * A function that has a parameter with a restrict-qualified pointer type. + */ +class FunctionWithRestrictParameters extends Function { + Parameter restrictPtrParam; + + FunctionWithRestrictParameters() { + restrictPtrParam = this.getAParameter() and + restrictPtrParam.getUnspecifiedType() instanceof PointerOrArrayType and + restrictPtrParam.getType().hasSpecifier("restrict") + } + + Parameter getARestrictPtrParam() { result = restrictPtrParam } +} + +/** + * A call to a function that has a parameter with a restrict-qualified pointer type. + */ +class CallToFunctionWithRestrictParameters extends FunctionCall { + CallToFunctionWithRestrictParameters() { + this.getTarget() instanceof FunctionWithRestrictParameters + } + + Expr getARestrictPtrArg() { + result = + this.getArgument(this.getTarget() + .(FunctionWithRestrictParameters) + .getARestrictPtrParam() + .getIndex()) + } + + Expr getAPtrArg() { + result = this.getAnArgument() and + pointerValue(result) + } + + Expr getAPossibleSizeArg() { + exists(Parameter param | + param = this.getTarget().(FunctionWithRestrictParameters).getAParameter() and + param.getUnderlyingType() instanceof IntegralType and + // exclude __builtin_object_size + not result.(FunctionCall).getTarget() instanceof BuiltInFunction and + result = this.getArgument(param.getIndex()) + ) + } +} + +/** + * A `PointsToExpr` that is an argument of a pointer-type in a `CallToFunctionWithRestrictParameters` + */ +class ArgPointsToExpr extends PointsToExpr { + override predicate interesting() { + any(CallToFunctionWithRestrictParameters call).getAnArgument() = this and + pointerValue(this) + } +} + +int getStatedValue(Expr e) { + // `upperBound(e)` defaults to `exprMaxVal(e)` when `e` isn't analyzable. So to get a meaningful + // result in this case we pick the minimum value obtainable from dataflow and range analysis. + result = + upperBound(e) + .minimum(min(Expr source | DataFlow::localExprFlow(source, e) | source.getValue().toInt())) +} + +int getPointerArithmeticOperandStatedValue(ArgPointsToExpr expr) { + result = getStatedValue(expr.(PointerArithmeticExpr).getOperand()) + or + // edge-case: &(array[index]) expressions + result = getStatedValue(expr.(AddressOfExpr).getOperand().(PointerArithmeticExpr).getOperand()) + or + // fall-back if `expr` is not a pointer arithmetic expression + not expr instanceof PointerArithmeticExpr and + not expr.(AddressOfExpr).getOperand() instanceof PointerArithmeticExpr and + result = 0 +} + +from + CallToFunctionWithRestrictParameters call, ArgPointsToExpr arg1, ArgPointsToExpr arg2, + int argOffset1, int argOffset2 +where + not isExcluded(call, Pointers3Package::doNotPassAlisedPointerToRestrictQualifiedParameterQuery()) and + arg1 = call.getARestrictPtrArg() and + arg2 = call.getAPtrArg() and + // two arguments that point to the same object + arg1 != arg2 and + arg1.pointsTo() = arg2.pointsTo() and + arg1.confidence() = 1.0 and + arg2.confidence() = 1.0 and + // get the offset of the pointer arithmetic operand (or '0' if there is none) + argOffset1 = getPointerArithmeticOperandStatedValue(arg1) and + argOffset2 = getPointerArithmeticOperandStatedValue(arg2) and + ( + // case 1: the pointer args are the same. + // (definite aliasing) + argOffset1 = argOffset2 + or + // case 2: the pointer args are different, a size arg exists, + // and the size arg is greater than the difference between the offsets. + // (potential aliasing) + exists(Expr sizeArg | + sizeArg = call.getAPossibleSizeArg() and + getStatedValue(sizeArg) > (argOffset1 - argOffset2).abs() + ) + or + // case 3: the pointer args are different, and a size arg does not exist + // (potential aliasing) + not exists(call.getAPossibleSizeArg()) + ) +select call, + "Call to '" + call.getTarget().getName() + + "' passes an aliased pointer to a restrict-qualified parameter." diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md new file mode 100644 index 0000000000..3fef1b9e33 --- /dev/null +++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md @@ -0,0 +1,297 @@ +# EXP43-C: Do not assign the value of a restrict-qualified pointer to another restrict-qualified pointer. + +This query implements the CERT-C rule EXP43-C: + +> Avoid undefined behavior when using restrict-qualified pointers + + + +## Description + +An object that is accessed through a `restrict`-qualified pointer has a special association with that pointer. This association requires that all accesses to that object use, directly or indirectly, the value of that particular pointer. The intended use of the restrict qualifier is to promote optimization, and deleting all instances of the qualifier from a program does not change its meaning (that is, observable behavior). In the absence of this qualifier, other pointers can alias this object. Caching the value in an object designated through a `restrict`-qualified pointer is safe at the beginning of the block in which the pointer is declared because no preexisting aliases may also be used to reference that object. The cached value must be restored to the object by the end of the block, where preexisting aliases again become available. New aliases may be formed within the block, but these must all depend on the value of the `restrict`-qualified pointer so that they can be identified and adjusted to refer to the cached value. For a `restrict`-qualified pointer at file scope, the block is the body of each function in the file \[[Walls 2006](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography)\]. Developers should be aware that C++ does not support the `restrict` qualifier, but some C++ compiler implementations support an equivalent qualifier as an extension. + +The C Standard \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\] identifies the following [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior): + +> A restrict-qualified pointer is assigned a value based on another restricted pointer whose associated block neither began execution before the block associated with this pointer, nor ended before the assignment (6.7.3.1). + + +This is an oversimplification, however, and it is important to review the formal definition of *restrict* in subclause 6.7.3.1 of the C Standard to properly understand undefined behaviors associated with the use of `restrict`-qualified pointers. + +## Overlapping Objects + +The `restrict` qualifier requires that the pointers do not reference overlapping objects. If the objects referenced by arguments to functions overlap (meaning the objects share some common memory addresses), the behavior is undefined. + +**Noncompliant Code Example** + +This code example is noncompliant because an assignment is made between two `restrict`-qualified pointers in the same scope: + +```cpp +int *restrict a; +int *restrict b; + +extern int c[]; + +int main(void) { + c[0] = 17; + c[1] = 18; + a = &c[0]; + b = &c[1]; + a = b; /* Undefined behavior */ + /* ... */ +} +``` +Note that [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) occurs only when `a` is assigned to `b`. It is valid for `a` and `b` to point into the same array object, provided the range of elements accessed through one of the pointers does not overlap with the range of elements accessed through the other pointer. + +**Compliant Solution** + +One way to eliminate the [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) is simply to remove the `restrict-`qualification from the affected pointers: + +```cpp +int *a; +int *b; + +extern int c[]; + +int main(void) { + c[0] = 17; + c[1] = 18; + a = &c[0]; + b = &c[1]; + a = b; /* Defined behavior */ + /* ... */ +} +``` + +## restrict-Qualified Function Parameters + +When calling functions that have `restrict`-qualified function parameters, it is important that the pointer arguments do not reference overlapping objects if one or more of the pointers are used to modify memory. Consequently, it is important to understand the semantics of the function being called. + +**Noncompliant Code Example** + +In this noncompliant code example, the function `f()` accepts three parameters. The function copies `n` integers from the `int` array referenced by the `restrict`-qualified pointer `p` to the `int` array referenced by the `restrict`-qualified pointer `q`. Because the destination array is modified during each execution of the function (for which `n` is nonzero), if the array is accessed through one of the pointer parameters, it cannot also be accessed through the other. Declaring these function parameters as `restrict`-qualified pointers allows aggressive optimization by the compiler but can also result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) if these pointers refer to overlapping objects. + +```cpp +#include +void f(size_t n, int *restrict p, const int *restrict q) { + while (n-- > 0) { + *p++ = *q++; + } +} + +void g(void) { + extern int d[100]; + /* ... */ + f(50, d + 1, d); /* Undefined behavior */ +} +``` +The function `g()` declares an array `d` consisting of 100 `int` values and then invokes `f()` to copy memory from one area of the array to another. This call has undefined behavior because each of `d[1]` through `d[49]` is accessed through both `p` and `q`. + +**Compliant Solution** + +In this compliant solution, the function `f()` is unchanged but the programmer has ensured that none of the calls to `f()` result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). The call to `f()` in `g()` is valid because the storage allocated to `d` is effectively divided into two disjoint objects. + +```cpp +#include +void f(size_t n, int *restrict p, const int *restrict q) { + while (n-- > 0) { + *p++ = *q++; + } +} + +void g(void) { + extern int d[100]; + /* ... */ + f(50, d + 50, d); /* Defined behavior */ +} +``` +**Noncompliant Code Example** + +In this noncompliant code example, the function `add()` adds the integer array referenced by the `restrict`-qualified pointers lhs to the integer array referenced by the `restrict`-qualified pointer `rhs` and stores the result in the `restrict`-qualified pointer referenced by `res`. The function `f()` declares an array `a` consisting of 100 `int` values and then invokes `add()` to copy memory from one area of the array to another. The call `add(100, a, a, a)` has [undefined behavior ](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior)because the object modified by `res` is accessed by lhs and `rhs`. + +```cpp +#include + +void add(size_t n, int *restrict res, const int *restrict lhs, + const int *restrict rhs) { + for (size_t i = 0; i < n; ++i) { + res[i] = lhs[i] + rhs[i]; + } +} + +void f(void) { + int a[100]; + add(100, a, a, a); /* Undefined behavior */ +} +``` +**Compliant Solution** + +In this compliant solution, an unmodified object is aliased through two restricted pointers. Because `a` and `b` are disjoint arrays, a call of the form `add(100, a, b, b)` has defined behavior, because array `b` is not modified within function `add`. + +```cpp +#include +void add(size_t n, int *restrict res, const int *restrict lhs, + const int *restrict rhs) { + for (size_t i = 0; i < n; ++i) { + res[i] = lhs[i] + rhs[i]; + } +} + +void f(void) { + int a[100]; + int b[100]; + add(100, a, b, b); /* Defined behavior */ +} +``` + +## Invoking Library Functions with restrict-Qualified Pointers + +Ensure that `restrict`-qualified source and destination pointers do not reference overlapping objects when invoking library functions. For example, the following table lists C standard library functions that copy memory from a source object referenced by a `restrict`-qualified pointer to a destination object that is also referenced by a `restrict`-qualified pointer: + +
Standard C Annex K
strcpy() strcpy_s()
strncpy() strncpy_s()
strcat() strcat_s()
strncat() strncat_s()
memcpy() memcpy_s()
strtok_s()
+If the objects referenced by arguments to functions overlap (meaning the objects share some common memory addresses), the behavior is [undefined](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See also [undefined behavior 68](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_68).) The result of the functions is unknown, and data may be corrupted. As a result, these functions must never be passed pointers to overlapping objects. If data must be copied between objects that share common memory addresses, a copy function guaranteed to work on overlapping memory, such as `memmove()`, should be used. + + +**Noncompliant Code Example** + +In this noncompliant code example, the values of objects referenced by `ptr1` and `ptr2` become unpredictable after the call to `memcpy()` because their memory areas overlap: + +```cpp +#include + +void func(void) { + char c_str[]= "test string"; + char *ptr1 = c_str; + char *ptr2; + + ptr2 = ptr1 + 3; + /* Undefined behavior because of overlapping objects */ + memcpy(ptr2, ptr1, 6); + /* ... */ +} +``` +**Compliant Solution** + +In this compliant solution, the call to `memcpy()` is replaced with a call to `memmove()`. The `memmove()` function performs the same operation as `memcpy()` when the memory regions do not overlap. When the memory regions do overlap, the *n* characters from the object pointed to by the source (`ptr1`) are first copied into a temporary array of *n* characters that does not overlap the objects pointed to by the destination (`ptr2`) or the source. The *n* characters from the temporary array are then copied into the object pointed to by the destination. + +```cpp +#include + +void func(void) { + char c_str[]= "test string"; + char *ptr1 = c_str; + char *ptr2; + + ptr2 = ptr1 + 3; + memmove(ptr2, ptr1, 6); /* Replace call to memcpy() */ + /* ... */ +} +``` +Similar solutions using `memmove()` can replace the string functions as long as care is taken regarding the byte size of the characters and proper null-termination of the copied string. + +## Calling Functions with restrict-Qualified Pointer to a const-Qualified Type + +Ensure that functions that accept a `restrict`-qualified pointer to a `const`-qualified type do not modify the object referenced by that pointer. Formatted input and output standard library functions frequently fit this description. The following table lists of some of the common functions for which the format argument is a `restrict`-qualified pointer to a `const`-qualified type. + +
Standard C Annex K
printf() printf_s()
scanf() scanf_s()
sprintf() sprintf_s()
snprintf() snprintf_s()
+For formatted output functions such as `printf()`, it is unlikely that a programmer would modify the format string. However, an attacker may attempt to do so if a program violates [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) and passes tainted values as part of the format string. + + +**Noncompliant Code Example** + +In this noncompliant code example, the programmer is attempting to overwrite the format string with a string value read in from `stdin` such as `"%d%f 1 3.3"` and use the resulting modified string of `"%s%d%f"` to input the subsequent values of `1` and `3.3`: + +```cpp +#include + +void func(void) { + int i; + float x; + char format[100] = "%s"; + /* Undefined behavior */ + int n = scanf(format, format + 2, &i, &x); + /* ... */ +} +``` +**Compliant Solution** + +The intended results are achieved by this compliant solution: + +```cpp +#include + +void func(void) { + int i; + float x; + int n = scanf("%d%f", &i, &x); /* Defined behavior */ + /* ... */ +} +``` + +## Outer-to-Inner Assignments between Restricted Pointers + +The assignment between `restrict`-qualified pointers declared in an inner nested block from an outer block has defined behavior. + +**Noncompliant Code Example** + +The assignment of `restrict`-qualified pointers to other `restrict`-qualified pointers within the same block has [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior): + +```cpp +void func(void) { + int *restrict p1; + int *restrict q1; + + int *restrict p2 = p1; /* Undefined behavior */ + int *restrict q2 = q1; /* Undefined behavior */ + } +``` +**Compliant Solution** + +The intended results can be achieved using an inner nested block, as shown in this compliant solution: + +```cpp +void func(void) { + int *restrict p1; + int *restrict q1; + { /* Added inner block */ + int *restrict p2 = p1; /* Valid, well-defined behavior */ + int *restrict q2 = q1; /* Valid, well-defined behavior */ + } +} +``` + +## Risk Assessment + +The incorrect use of `restrict`-qualified pointers can result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) that might be [exploited](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) to cause data integrity violations. + +
Rule Severity Likelihood Remediation Cost Priority Level
EXP43-C Medium Probable High P4 L3
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP43-C). + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 restrict Supported indirectly via MISRA C 2012 Rule 8.14.
CodeSonar 7.2p0 LANG.TYPE.RESTRICT Restrict qualifier used
Coverity 2017.07 MISRA C 2012 Rule 8.14 Partially implemented
GCC 8.1 -Wrestrict Fully implemented
Helix QAC 2022.4 C1057
Klocwork 2022.4 MISRA.TYPE.RESTRICT.QUAL.2012
LDRA tool suite 9.7.1 480 S, 489 S, 613 S Enhanced enforcement
Parasoft C/C++test 2022.2 CERT_C-EXP43-a The restrict type qualifier shall not be used
PC-lint Plus 1.4 586 Assistance provided: reports use of the restrict keyword
Polyspace Bug Finder R2022b CERT C: Rule EXP43-C Checks for copy of overlapping memory (rule partially covered)
PRQA QA-C 9.7 1057
RuleChecker 22.04 restrict Supported indirectly via MISRA C 2012 Rule 8.14.
SonarQube C/C++ Plugin 3.11 S1836 Implements MISRA C:2012 Rule 8.14 to flag uses of restrict
+ + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard FIO30-C. Exclude user input from format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Passing Parameters and Return Values \[CSJ\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Passing pointers into the same object as arguments to different restrict-qualified parameters \[restrict\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 8.14 (required) 1 Prior to 2018-01-12: CERT: Unspecified Relationship
+1. MISRA Rule 8.14 prohibits the use of the restrict keyword except in C standard library functions. + + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] 6.7.3.1, "Formal Definition of restrict "
\[ Walls 2006 \]
+ + +## Implementation notes + +None + +## References + +* CERT-C: [EXP43-C: Avoid undefined behavior when using restrict-qualified pointers](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql new file mode 100644 index 0000000000..37d2575bfe --- /dev/null +++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql @@ -0,0 +1,75 @@ +/** + * @id c/cert/restrict-pointer-references-overlapping-object + * @name EXP43-C: Do not assign the value of a restrict-qualified pointer to another restrict-qualified pointer. + * @description Restrict qualified pointers referencing overlapping objects is undefined behavior. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/exp43-c + * correctness + * external/cert/obligation/rule + */ + +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.controlflow.Dominance +import codingstandards.c.cert + +/** + * An `Expr` that is an assignment or initialization to a restrict-qualified pointer-type variable. + */ +class AssignmentOrInitializationToRestrictPtrValueExpr extends Expr { + Variable v; + + AssignmentOrInitializationToRestrictPtrValueExpr() { + this = v.getAnAssignedValue() and + v.getType().hasSpecifier("restrict") + } + + Variable getVariable() { result = v } +} + +/** + * Returns the target variable of a `VariableAccess`. + * If the access is a field access, then the target is the `Variable` of the qualifier. + */ +Variable getAddressOfExprTargetBase(AddressOfExpr expr) { + result = expr.getOperand().(ValueFieldAccess).getQualifier().(VariableAccess).getTarget() + or + result = expr.getOperand().(VariableAccess).getTarget() +} + +from + AssignmentOrInitializationToRestrictPtrValueExpr source, + AssignmentOrInitializationToRestrictPtrValueExpr expr, + AssignmentOrInitializationToRestrictPtrValueExpr pre_expr +where + not isExcluded(expr, Pointers3Package::restrictPointerReferencesOverlappingObjectQuery()) and + ( + // If the same expressions flows to two unique `AssignmentOrInitializationToRestrictPtrValueExpr` + // in the same block, then the two variables point to the same (overlapping) object + expr.getEnclosingBlock() = pre_expr.getEnclosingBlock() and + strictlyDominates(pre_expr, expr) and + ( + dominates(source, pre_expr) and + DataFlow::localExprFlow(source, expr) and + DataFlow::localExprFlow(source, pre_expr) + or + // Expressions referring to the address of the same variable can also result in aliasing + getAddressOfExprTargetBase(expr) = getAddressOfExprTargetBase(pre_expr) and + source = + any(AddressOfExpr ao | getAddressOfExprTargetBase(ao) = getAddressOfExprTargetBase(expr)) + ) and + // But only if there is no intermediate assignment that could change the value of one of the variables + not exists(AssignmentOrInitializationToRestrictPtrValueExpr mid | + strictlyDominates(mid, expr) and + strictlyDominates(pre_expr, mid) and + not DataFlow::localExprFlow(source, mid) + ) + or + // Two restrict-qualified pointers in the same scope assigned to each other + expr.getVariable().getType().hasSpecifier("restrict") and + expr.(VariableAccess).getTarget().getType().hasSpecifier("restrict") and + expr.(VariableAccess).getTarget().getParentScope() = expr.getVariable().getParentScope() + ) +select expr, "Restrict qualified pointers referencing overlapping objects is undefined behavior." diff --git a/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.expected b/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.expected new file mode 100644 index 0000000000..61aaf6b4ce --- /dev/null +++ b/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.expected @@ -0,0 +1,5 @@ +| test.c:53:3:53:6 | call to copy | Call to 'copy' passes an aliased pointer to a restrict-qualified parameter. | +| test.c:58:3:58:6 | call to copy | Call to 'copy' passes an aliased pointer to a restrict-qualified parameter. | +| test.c:64:3:64:8 | call to strcpy | Call to 'strcpy' passes an aliased pointer to a restrict-qualified parameter. | +| test.c:71:3:71:8 | call to memcpy | Call to 'memcpy' passes an aliased pointer to a restrict-qualified parameter. | +| test.c:84:3:84:7 | call to scanf | Call to 'scanf' passes an aliased pointer to a restrict-qualified parameter. | diff --git a/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.qlref b/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.qlref new file mode 100644 index 0000000000..937021c550 --- /dev/null +++ b/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.qlref @@ -0,0 +1 @@ +rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected new file mode 100644 index 0000000000..0254ab7c02 --- /dev/null +++ b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected @@ -0,0 +1,6 @@ +| test.c:16:22:16:23 | i2 | Restrict qualified pointers referencing overlapping objects is undefined behavior. | +| test.c:17:8:17:9 | g2 | Restrict qualified pointers referencing overlapping objects is undefined behavior. | +| test.c:18:8:18:9 | i2 | Restrict qualified pointers referencing overlapping objects is undefined behavior. | +| test.c:26:10:26:11 | g1 | Restrict qualified pointers referencing overlapping objects is undefined behavior. | +| test.c:33:22:33:26 | & ... | Restrict qualified pointers referencing overlapping objects is undefined behavior. | +| test.c:40:10:40:14 | & ... | Restrict qualified pointers referencing overlapping objects is undefined behavior. | diff --git a/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.qlref b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.qlref new file mode 100644 index 0000000000..81043b56c2 --- /dev/null +++ b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.qlref @@ -0,0 +1 @@ +rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP43-C/test.c b/c/cert/test/rules/EXP43-C/test.c index 8527f7ce84..31f179a64c 100644 --- a/c/cert/test/rules/EXP43-C/test.c +++ b/c/cert/test/rules/EXP43-C/test.c @@ -1,15 +1,44 @@ #include +#include #include int *restrict g1; int *restrict g2; +struct s1 { + int x, y, z; +}; +struct s1 v1; + void test_global_local() { int *restrict i1 = g1; // COMPLIANT int *restrict i2 = g2; // COMPLIANT int *restrict i3 = i2; // NON_COMPLIANT g1 = g2; // NON_COMPLIANT i1 = i2; // NON_COMPLIANT + { + int *restrict i4; + int *restrict i5; + int *restrict i6; + i4 = g1; // COMPLIANT + i4 = (void *)0; // COMPLIANT + i5 = g1; // COMPLIANT + i4 = g1; // NON_COMPLIANT + i6 = g2; // COMPLIANT + } +} + +void test_structs() { + struct s1 *restrict p1 = &v1; + int *restrict px = &v1.x; // NON_COMPLIANT + { + int *restrict py; + int *restrict pz; + py = &v1.y; // COMPLIANT + py = (int *)0; + pz = &v1.z; // COMPLIANT + py = &v1.y; // NON_COMPLIANT + } } void copy(int *restrict p1, int *restrict p2, size_t s) { @@ -25,8 +54,8 @@ void test_restrict_params() { copy(&i1, &i2, 1); // COMPLIANT int x[10]; - copy(x[0], x[1], 1); // COMPLIANT - non overlapping - copy(x[0], x[1], 2); // NON_COMPLIANT - overlapping + copy(&x[0], &x[1], 1); // COMPLIANT - non overlapping + copy(&x[0], &x[1], 2); // NON_COMPLIANT - overlapping } void test_strcpy() { @@ -36,13 +65,6 @@ void test_strcpy() { strcpy(&s2, &s1); // COMPLIANT } -void test_strcpy_s() { - char s1[] = "my test string"; - char s2[] = "my other string"; - strcpy_s(&s1, &s1 + 3); // NON_COMPLIANT - strcpy_s(&s2, sizeof(s2), &s1); // COMPLIANT -} - void test_memcpy() { char s1[] = "my test string"; char s2[] = "my other string"; @@ -50,23 +72,16 @@ void test_memcpy() { memcpy(&s2, &s1 + 3, 5); // COMPLIANT } -void test_memcpy_s() { - char s1[] = "my test string"; - char s2[] = "my other string"; - memcpy_s(&s1, sizeof(s1), &s1 + 3, 5); // NON_COMPLIANT - memcpy_s(&s2, sizeof(s2), &s1 + 3, 5); // COMPLIANT -} - void test_memmove() { char s1[] = "my test string"; char s2[] = "my other string"; - memmove(&s1, &s1 + 3, 5); // COMPLIANT + memmove(&s1, &s1 + 3, 5); // COMPLIANT - memmove is allowed to overlap memmove(&s2, &s1 + 3, 5); // COMPLIANT } void test_scanf() { char s1[200] = "%10s"; - scanf(&s2, &s2 + 4); // NON_COMPLIANT + scanf(&s1, &s1 + 4); // NON_COMPLIANT } // TODO also consider the following: diff --git a/c/common/src/codingstandards/c/Pointers.qll b/c/common/src/codingstandards/c/Pointers.qll index 87ade425e1..4b4e46a138 100644 --- a/c/common/src/codingstandards/c/Pointers.qll +++ b/c/common/src/codingstandards/c/Pointers.qll @@ -5,6 +5,47 @@ import cpp import codingstandards.cpp.Type +/** + * An expression which performs pointer arithmetic + */ +abstract class PointerArithmeticExpr extends Expr { + abstract Expr getPointer(); + + abstract Expr getOperand(); +} + +/** + * A pointer arithmetic binary operation expression. + */ +class SimplePointerArithmeticExpr extends PointerArithmeticExpr, PointerArithmeticOperation { + override Expr getPointer() { result = this.getLeftOperand() } + + override Expr getOperand() { result = this.getRightOperand() } +} + +/** + * A pointer arithmetic assignment expression. + */ +class AssignPointerArithmeticExpr extends PointerArithmeticExpr, AssignOperation { + AssignPointerArithmeticExpr() { + this instanceof AssignPointerAddExpr or + this instanceof AssignPointerSubExpr + } + + override Expr getPointer() { result = this.getLValue() } + + override Expr getOperand() { result = this.getRValue() } +} + +/** + * A pointer arithmetic array access expression. + */ +class ArrayPointerArithmeticExpr extends PointerArithmeticExpr, ArrayExpr { + override Expr getPointer() { result = this.getArrayBase() } + + override Expr getOperand() { result = this.getArrayOffset() } +} + /** * A null pointer constant, which is either in the form `NULL` or `(void *)0`. */ diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll index 9c4741f620..0ca6ae2b80 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll @@ -7,7 +7,8 @@ newtype Pointers3Query = TDoNotAccessVolatileObjectWithNonVolatileReferenceQuery() or TDoNotCastPointerToMoreStrictlyAlignedPointerTypeQuery() or TDoNotAccessVariableViaPointerOfIncompatibleTypeQuery() or - TUndefinedBehaviorWithRestrictQualifiedPointersQuery() + TDoNotPassAlisedPointerToRestrictQualifiedParameterQuery() or + TRestrictPointerReferencesOverlappingObjectQuery() predicate isPointers3QueryMetadata(Query query, string queryId, string ruleId) { query = @@ -35,11 +36,19 @@ predicate isPointers3QueryMetadata(Query query, string queryId, string ruleId) { ruleId = "EXP39-C" or query = - // `Query` instance for the `undefinedBehaviorWithRestrictQualifiedPointers` query - Pointers3Package::undefinedBehaviorWithRestrictQualifiedPointersQuery() and + // `Query` instance for the `doNotPassAlisedPointerToRestrictQualifiedParameter` query + Pointers3Package::doNotPassAlisedPointerToRestrictQualifiedParameterQuery() and queryId = - // `@id` for the `undefinedBehaviorWithRestrictQualifiedPointers` query - "c/cert/undefined-behavior-with-restrict-qualified-pointers" and + // `@id` for the `doNotPassAlisedPointerToRestrictQualifiedParameter` query + "c/cert/do-not-pass-alised-pointer-to-restrict-qualified-parameter" and + ruleId = "EXP43-C" + or + query = + // `Query` instance for the `restrictPointerReferencesOverlappingObject` query + Pointers3Package::restrictPointerReferencesOverlappingObjectQuery() and + queryId = + // `@id` for the `restrictPointerReferencesOverlappingObject` query + "c/cert/restrict-pointer-references-overlapping-object" and ruleId = "EXP43-C" } @@ -65,10 +74,17 @@ module Pointers3Package { TQueryC(TPointers3PackageQuery(TDoNotAccessVariableViaPointerOfIncompatibleTypeQuery())) } - Query undefinedBehaviorWithRestrictQualifiedPointersQuery() { + Query doNotPassAlisedPointerToRestrictQualifiedParameterQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotPassAlisedPointerToRestrictQualifiedParameter` query + TQueryC(TPointers3PackageQuery(TDoNotPassAlisedPointerToRestrictQualifiedParameterQuery())) + } + + Query restrictPointerReferencesOverlappingObjectQuery() { //autogenerate `Query` type result = - // `Query` type for `undefinedBehaviorWithRestrictQualifiedPointers` query - TQueryC(TPointers3PackageQuery(TUndefinedBehaviorWithRestrictQualifiedPointersQuery())) + // `Query` type for `restrictPointerReferencesOverlappingObject` query + TQueryC(TPointers3PackageQuery(TRestrictPointerReferencesOverlappingObjectQuery())) } } diff --git a/rule_packages/c/Pointers3.json b/rule_packages/c/Pointers3.json index 41c1496672..b05faf289c 100644 --- a/rule_packages/c/Pointers3.json +++ b/rule_packages/c/Pointers3.json @@ -66,12 +66,23 @@ }, "queries": [ { - "description": "", + "description": "Passing an aliased pointer to a restrict-qualified parameter is undefined behavior.", "kind": "problem", - "name": "Avoid undefined behavior when using restrict-qualified pointers", - "precision": "very-high", + "name": "Do not pass aliased pointers to restrict-qualified parameters", + "precision": "medium", "severity": "error", - "short_name": "UndefinedBehaviorWithRestrictQualifiedPointers", + "short_name": "DoNotPassAlisedPointerToRestrictQualifiedParameter", + "tags": [ + "correctness" + ] + }, + { + "description": "Restrict qualified pointers referencing overlapping objects is undefined behavior.", + "kind": "problem", + "name": "Do not assign the value of a restrict-qualified pointer to another restrict-qualified pointer.", + "precision": "high", + "severity": "error", + "short_name": "RestrictPointerReferencesOverlappingObject", "tags": [ "correctness" ] From 72e9fd66061ad263906a2b7a6e7f64cba10687ca Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Fri, 27 Jan 2023 02:46:23 +0100 Subject: [PATCH 19/36] Update RuleMetadata.qll --- .../src/codingstandards/cpp/exclusions/c/RuleMetadata.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index e97b5722a9..91732455ea 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -118,7 +118,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isMiscQueryMetadata(query, queryId, ruleId, category) or isPointers1QueryMetadata(query, queryId, ruleId, category) or isPointers2QueryMetadata(query, queryId, ruleId, category) or - isPointers3QueryMetadata(query, queryId, ruleId) or + isPointers3QueryMetadata(query, queryId, ruleId, category) or isPreprocessor1QueryMetadata(query, queryId, ruleId, category) or isPreprocessor2QueryMetadata(query, queryId, ruleId, category) or isPreprocessor3QueryMetadata(query, queryId, ruleId, category) or From 0e9df1ea4097081d822a9cb32caef3d557e52317 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Fri, 27 Jan 2023 02:47:37 +0100 Subject: [PATCH 20/36] Update test.c --- c/cert/test/rules/EXP39-C/test.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/c/cert/test/rules/EXP39-C/test.c b/c/cert/test/rules/EXP39-C/test.c index 8647163ea0..5ae4bbe35b 100644 --- a/c/cert/test/rules/EXP39-C/test.c +++ b/c/cert/test/rules/EXP39-C/test.c @@ -17,7 +17,8 @@ void test_incompatible_arithmetic() { // char may be signed or unsigned, and so is not compatible with either char c1; (signed char *)&c1; // NON_COMPLIANT - (unsigned char *)&c1; // COMPLIANT - the underlying byte representation is always compatible + (unsigned char *)&c1; // COMPLIANT - the underlying byte representation is + // always compatible (char *)&c1; // COMPLIANT - same type // int is defined as signed, so is compatible with all the signed versions @@ -32,13 +33,13 @@ void test_incompatible_arithmetic() { struct { int a; -} * s1; +} *s1; struct { int a; -} * s2; +} *s2; struct S1 { int a; -} * s3; +} *s3; struct S1 *s4; // TODO test across files From 7bb56655e7be64276660e0c0be7ffcb7c81357c3 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Fri, 27 Jan 2023 02:51:35 +0100 Subject: [PATCH 21/36] Regenerate Pointers3 package files --- ...estrictPointerReferencesOverlappingObject.md | 2 +- ...estrictPointerReferencesOverlappingObject.ql | 2 +- .../cpp/exclusions/c/Pointers3.qll | 17 +++++++++++------ rule_packages/c/Pointers3.json | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md index 3fef1b9e33..f5ab3886a0 100644 --- a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md +++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md @@ -1,4 +1,4 @@ -# EXP43-C: Do not assign the value of a restrict-qualified pointer to another restrict-qualified pointer. +# EXP43-C: Do not assign the value of a restrict-qualified pointer to another restrict-qualified pointer This query implements the CERT-C rule EXP43-C: diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql index 37d2575bfe..20f724f7ef 100644 --- a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql +++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql @@ -1,6 +1,6 @@ /** * @id c/cert/restrict-pointer-references-overlapping-object - * @name EXP43-C: Do not assign the value of a restrict-qualified pointer to another restrict-qualified pointer. + * @name EXP43-C: Do not assign the value of a restrict-qualified pointer to another restrict-qualified pointer * @description Restrict qualified pointers referencing overlapping objects is undefined behavior. * @kind problem * @precision high diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll index 0ca6ae2b80..8cfc140f99 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll @@ -10,14 +10,15 @@ newtype Pointers3Query = TDoNotPassAlisedPointerToRestrictQualifiedParameterQuery() or TRestrictPointerReferencesOverlappingObjectQuery() -predicate isPointers3QueryMetadata(Query query, string queryId, string ruleId) { +predicate isPointers3QueryMetadata(Query query, string queryId, string ruleId, string category) { query = // `Query` instance for the `doNotAccessVolatileObjectWithNonVolatileReference` query Pointers3Package::doNotAccessVolatileObjectWithNonVolatileReferenceQuery() and queryId = // `@id` for the `doNotAccessVolatileObjectWithNonVolatileReference` query "c/cert/do-not-access-volatile-object-with-non-volatile-reference" and - ruleId = "EXP32-C" + ruleId = "EXP32-C" and + category = "rule" or query = // `Query` instance for the `doNotCastPointerToMoreStrictlyAlignedPointerType` query @@ -25,7 +26,8 @@ predicate isPointers3QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doNotCastPointerToMoreStrictlyAlignedPointerType` query "c/cert/do-not-cast-pointer-to-more-strictly-aligned-pointer-type" and - ruleId = "EXP36-C" + ruleId = "EXP36-C" and + category = "rule" or query = // `Query` instance for the `doNotAccessVariableViaPointerOfIncompatibleType` query @@ -33,7 +35,8 @@ predicate isPointers3QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doNotAccessVariableViaPointerOfIncompatibleType` query "c/cert/do-not-access-variable-via-pointer-of-incompatible-type" and - ruleId = "EXP39-C" + ruleId = "EXP39-C" and + category = "rule" or query = // `Query` instance for the `doNotPassAlisedPointerToRestrictQualifiedParameter` query @@ -41,7 +44,8 @@ predicate isPointers3QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `doNotPassAlisedPointerToRestrictQualifiedParameter` query "c/cert/do-not-pass-alised-pointer-to-restrict-qualified-parameter" and - ruleId = "EXP43-C" + ruleId = "EXP43-C" and + category = "rule" or query = // `Query` instance for the `restrictPointerReferencesOverlappingObject` query @@ -49,7 +53,8 @@ predicate isPointers3QueryMetadata(Query query, string queryId, string ruleId) { queryId = // `@id` for the `restrictPointerReferencesOverlappingObject` query "c/cert/restrict-pointer-references-overlapping-object" and - ruleId = "EXP43-C" + ruleId = "EXP43-C" and + category = "rule" } module Pointers3Package { diff --git a/rule_packages/c/Pointers3.json b/rule_packages/c/Pointers3.json index b05faf289c..c8188b38aa 100644 --- a/rule_packages/c/Pointers3.json +++ b/rule_packages/c/Pointers3.json @@ -79,7 +79,7 @@ { "description": "Restrict qualified pointers referencing overlapping objects is undefined behavior.", "kind": "problem", - "name": "Do not assign the value of a restrict-qualified pointer to another restrict-qualified pointer.", + "name": "Do not assign the value of a restrict-qualified pointer to another restrict-qualified pointer", "precision": "high", "severity": "error", "short_name": "RestrictPointerReferencesOverlappingObject", From 6d25cd6c1bc1b7dbe58519b58f7e9c60a906b529 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Fri, 27 Jan 2023 03:05:20 +0100 Subject: [PATCH 22/36] Update test.c --- c/cert/test/rules/EXP39-C/test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/c/cert/test/rules/EXP39-C/test.c b/c/cert/test/rules/EXP39-C/test.c index 5ae4bbe35b..60a0a428c7 100644 --- a/c/cert/test/rules/EXP39-C/test.c +++ b/c/cert/test/rules/EXP39-C/test.c @@ -33,13 +33,13 @@ void test_incompatible_arithmetic() { struct { int a; -} *s1; +} * s1; struct { int a; -} *s2; +} * s2; struct S1 { int a; -} *s3; +} * s3; struct S1 *s4; // TODO test across files From 23961bc3756f8b7688a4d4c76250032f7460a545 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Fri, 27 Jan 2023 03:11:26 +0100 Subject: [PATCH 23/36] Correct whitespace in rule help files (regen package) --- .../EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md | 1 - .../EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md | 1 - .../EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md | 1 - .../DoNotPassAlisedPointerToRestrictQualifiedParameter.md | 1 - .../rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md | 1 - 5 files changed, 5 deletions(-) diff --git a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md index 11f8566e5c..aa2dc7036a 100644 --- a/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md +++ b/c/cert/src/rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.md @@ -5,7 +5,6 @@ This query implements the CERT-C rule EXP32-C: > Do not access a volatile object through a nonvolatile reference - ## Description An object that has volatile-qualified type may be modified in ways unknown to the [implementation](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-implementation) or have other unknown [side effects](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-sideeffect). Referencing a volatile object by using a non-volatile lvalue is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). The C Standard, 6.7.3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states diff --git a/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md index 8c56801e01..4a682f4afe 100644 --- a/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md +++ b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.md @@ -5,7 +5,6 @@ This query implements the CERT-C rule EXP36-C: > Do not cast pointers into more strictly aligned pointer types - ## Description Do not convert a pointer value to a pointer type that is more strictly aligned than the referenced type. Different alignments are possible for different types of objects. If the type-checking system is overridden by an explicit cast or the pointer is converted to a void pointer (`void *`) and then to a different type, the alignment of an object may be changed. diff --git a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md index 8d8b132c4d..cd01c4282e 100644 --- a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md +++ b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.md @@ -5,7 +5,6 @@ This query implements the CERT-C rule EXP39-C: > Do not access a variable through a pointer of an incompatible type - ## Description Modifying a variable through a pointer of an incompatible type (other than `unsigned char`) can lead to unpredictable results. Subclause 6.2.7 of the C Standard states that two types may be distinct yet compatible and addresses precisely when two distinct types are compatible. diff --git a/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.md b/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.md index 827aad74c0..855f7ce963 100644 --- a/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.md +++ b/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.md @@ -5,7 +5,6 @@ This query implements the CERT-C rule EXP43-C: > Avoid undefined behavior when using restrict-qualified pointers - ## Description An object that is accessed through a `restrict`-qualified pointer has a special association with that pointer. This association requires that all accesses to that object use, directly or indirectly, the value of that particular pointer. The intended use of the restrict qualifier is to promote optimization, and deleting all instances of the qualifier from a program does not change its meaning (that is, observable behavior). In the absence of this qualifier, other pointers can alias this object. Caching the value in an object designated through a `restrict`-qualified pointer is safe at the beginning of the block in which the pointer is declared because no preexisting aliases may also be used to reference that object. The cached value must be restored to the object by the end of the block, where preexisting aliases again become available. New aliases may be formed within the block, but these must all depend on the value of the `restrict`-qualified pointer so that they can be identified and adjusted to refer to the cached value. For a `restrict`-qualified pointer at file scope, the block is the body of each function in the file \[[Walls 2006](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography)\]. Developers should be aware that C++ does not support the `restrict` qualifier, but some C++ compiler implementations support an equivalent qualifier as an extension. diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md index f5ab3886a0..ce02712b72 100644 --- a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md +++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.md @@ -5,7 +5,6 @@ This query implements the CERT-C rule EXP43-C: > Avoid undefined behavior when using restrict-qualified pointers - ## Description An object that is accessed through a `restrict`-qualified pointer has a special association with that pointer. This association requires that all accesses to that object use, directly or indirectly, the value of that particular pointer. The intended use of the restrict qualifier is to promote optimization, and deleting all instances of the qualifier from a program does not change its meaning (that is, observable behavior). In the absence of this qualifier, other pointers can alias this object. Caching the value in an object designated through a `restrict`-qualified pointer is safe at the beginning of the block in which the pointer is declared because no preexisting aliases may also be used to reference that object. The cached value must be restored to the object by the end of the block, where preexisting aliases again become available. New aliases may be formed within the block, but these must all depend on the value of the `restrict`-qualified pointer so that they can be identified and adjusted to refer to the cached value. For a `restrict`-qualified pointer at file scope, the block is the body of each function in the file \[[Walls 2006](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography)\]. Developers should be aware that C++ does not support the `restrict` qualifier, but some C++ compiler implementations support an equivalent qualifier as an extension. From e2e6590865a39417ea58ff0a85fa70fca7ef744b Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Fri, 27 Jan 2023 03:26:11 +0100 Subject: [PATCH 24/36] Update EXP43-C message and expected output --- .../RestrictPointerReferencesOverlappingObject.ql | 3 ++- ...strictPointerReferencesOverlappingObject.expected | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql index 20f724f7ef..851fbcebaa 100644 --- a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql +++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql @@ -72,4 +72,5 @@ where expr.(VariableAccess).getTarget().getType().hasSpecifier("restrict") and expr.(VariableAccess).getTarget().getParentScope() = expr.getVariable().getParentScope() ) -select expr, "Restrict qualified pointers referencing overlapping objects is undefined behavior." +select expr, "Assignment to restrict-qualified pointer $@ results in pointer aliasing.", + expr.getVariable(), expr.getVariable().getName() diff --git a/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected index 0254ab7c02..34fe741bd5 100644 --- a/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected +++ b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected @@ -1,6 +1,6 @@ -| test.c:16:22:16:23 | i2 | Restrict qualified pointers referencing overlapping objects is undefined behavior. | -| test.c:17:8:17:9 | g2 | Restrict qualified pointers referencing overlapping objects is undefined behavior. | -| test.c:18:8:18:9 | i2 | Restrict qualified pointers referencing overlapping objects is undefined behavior. | -| test.c:26:10:26:11 | g1 | Restrict qualified pointers referencing overlapping objects is undefined behavior. | -| test.c:33:22:33:26 | & ... | Restrict qualified pointers referencing overlapping objects is undefined behavior. | -| test.c:40:10:40:14 | & ... | Restrict qualified pointers referencing overlapping objects is undefined behavior. | +| test.c:16:22:16:23 | i2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:16:17:16:18 | i3 | i3 | +| test.c:17:8:17:9 | g2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:5:15:5:16 | g1 | g1 | +| test.c:18:8:18:9 | i2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:14:17:14:18 | i1 | i1 | +| test.c:26:10:26:11 | g1 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:20:19:20:20 | i4 | i4 | +| test.c:33:22:33:26 | & ... | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:33:17:33:18 | px | px | +| test.c:40:10:40:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:35:19:35:20 | py | py | From 5f094084ca4b677f48f6fe8e72343b3f2577a68f Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Fri, 27 Jan 2023 14:11:50 +0100 Subject: [PATCH 25/36] Revert accidental change to EXP39-C and update .expected results --- ...essVariableViaPointerOfIncompatibleType.ql | 2 +- ...iableViaPointerOfIncompatibleType.expected | 92 +++++++++---------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql index 760603de6b..d9ef7e742c 100644 --- a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql +++ b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql @@ -13,7 +13,7 @@ import cpp import codingstandards.c.cert -import semmle.code.cpp.ir.dataflow.DataFlow +import semmle.code.cpp.dataflow.DataFlow import semmle.code.cpp.controlflow.Dominance import DataFlow::PathGraph diff --git a/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected index 0ba6202bf1..da5437e3bb 100644 --- a/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected +++ b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected @@ -1,15 +1,15 @@ edges -| test.c:49:8:49:9 | s3 | test.c:50:8:50:9 | s1 | -| test.c:60:16:60:18 | E1A | test.c:61:16:61:17 | e1 | -| test.c:60:16:60:18 | E1A | test.c:65:10:65:12 | & ... | -| test.c:68:22:68:22 | v | test.c:68:41:68:41 | v | -| test.c:72:13:72:15 | & ... | test.c:68:22:68:22 | v | -| test.c:74:13:74:15 | & ... | test.c:68:22:68:22 | v | -| test.c:97:32:97:37 | call to malloc | test.c:98:40:98:41 | s2 | -| test.c:97:32:97:37 | call to malloc | test.c:98:40:98:41 | s2 | -| test.c:98:32:98:38 | call to realloc | test.c:99:3:99:4 | s3 | -| test.c:98:32:98:38 | call to realloc | test.c:100:10:100:11 | s3 | -| test.c:98:40:98:41 | s2 | test.c:98:32:98:38 | call to realloc | +| test.c:50:8:50:9 | s3 | test.c:51:8:51:9 | s1 | +| test.c:61:16:61:18 | E1A | test.c:62:16:62:17 | e1 | +| test.c:61:16:61:18 | E1A | test.c:66:10:66:12 | & ... | +| test.c:69:22:69:22 | v | test.c:69:41:69:41 | v | +| test.c:73:13:73:15 | & ... | test.c:69:22:69:22 | v | +| test.c:75:13:75:15 | & ... | test.c:69:22:69:22 | v | +| test.c:98:32:98:37 | call to malloc | test.c:99:40:99:41 | s2 | +| test.c:98:32:98:37 | call to malloc | test.c:99:40:99:41 | s2 | +| test.c:99:32:99:38 | call to realloc | test.c:100:3:100:4 | s3 | +| test.c:99:32:99:38 | call to realloc | test.c:101:10:101:11 | s3 | +| test.c:99:40:99:41 | s2 | test.c:99:32:99:38 | call to realloc | nodes | test.c:6:19:6:20 | & ... | semmle.label | & ... | | test.c:11:10:11:11 | & ... | semmle.label | & ... | @@ -17,44 +17,44 @@ nodes | test.c:15:17:15:19 | & ... | semmle.label | & ... | | test.c:19:18:19:20 | & ... | semmle.label | & ... | | test.c:20:20:20:22 | & ... | semmle.label | & ... | -| test.c:21:11:21:13 | & ... | semmle.label | & ... | -| test.c:26:17:26:19 | & ... | semmle.label | & ... | -| test.c:27:10:27:12 | & ... | semmle.label | & ... | -| test.c:28:13:28:15 | & ... | semmle.label | & ... | -| test.c:29:19:29:21 | & ... | semmle.label | & ... | -| test.c:30:16:30:18 | & ... | semmle.label | & ... | -| test.c:47:8:47:9 | s2 | semmle.label | s2 | -| test.c:49:8:49:9 | s3 | semmle.label | s3 | -| test.c:49:8:49:9 | s3 | semmle.label | s3 | -| test.c:50:8:50:9 | s1 | semmle.label | s1 | -| test.c:60:16:60:18 | E1A | semmle.label | E1A | -| test.c:60:16:60:18 | E1A | semmle.label | E1A | -| test.c:61:16:61:17 | e1 | semmle.label | e1 | -| test.c:65:10:65:12 | & ... | semmle.label | & ... | -| test.c:68:22:68:22 | v | semmle.label | v | -| test.c:68:41:68:41 | v | semmle.label | v | -| test.c:72:13:72:15 | & ... | semmle.label | & ... | -| test.c:72:13:72:15 | & ... | semmle.label | & ... | -| test.c:74:13:74:15 | & ... | semmle.label | & ... | -| test.c:74:13:74:15 | & ... | semmle.label | & ... | -| test.c:97:32:97:37 | call to malloc | semmle.label | call to malloc | -| test.c:97:32:97:37 | call to malloc | semmle.label | call to malloc | -| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | -| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | -| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | -| test.c:98:40:98:41 | s2 | semmle.label | s2 | -| test.c:98:40:98:41 | s2 | semmle.label | s2 | -| test.c:99:3:99:4 | s3 | semmle.label | s3 | -| test.c:100:10:100:11 | s3 | semmle.label | s3 | +| test.c:22:11:22:13 | & ... | semmle.label | & ... | +| test.c:27:17:27:19 | & ... | semmle.label | & ... | +| test.c:28:10:28:12 | & ... | semmle.label | & ... | +| test.c:29:13:29:15 | & ... | semmle.label | & ... | +| test.c:30:19:30:21 | & ... | semmle.label | & ... | +| test.c:31:16:31:18 | & ... | semmle.label | & ... | +| test.c:48:8:48:9 | s2 | semmle.label | s2 | +| test.c:50:8:50:9 | s3 | semmle.label | s3 | +| test.c:50:8:50:9 | s3 | semmle.label | s3 | +| test.c:51:8:51:9 | s1 | semmle.label | s1 | +| test.c:61:16:61:18 | E1A | semmle.label | E1A | +| test.c:61:16:61:18 | E1A | semmle.label | E1A | +| test.c:62:16:62:17 | e1 | semmle.label | e1 | +| test.c:66:10:66:12 | & ... | semmle.label | & ... | +| test.c:69:22:69:22 | v | semmle.label | v | +| test.c:69:41:69:41 | v | semmle.label | v | +| test.c:73:13:73:15 | & ... | semmle.label | & ... | +| test.c:73:13:73:15 | & ... | semmle.label | & ... | +| test.c:75:13:75:15 | & ... | semmle.label | & ... | +| test.c:75:13:75:15 | & ... | semmle.label | & ... | +| test.c:98:32:98:37 | call to malloc | semmle.label | call to malloc | +| test.c:98:32:98:37 | call to malloc | semmle.label | call to malloc | +| test.c:99:32:99:38 | call to realloc | semmle.label | call to realloc | +| test.c:99:32:99:38 | call to realloc | semmle.label | call to realloc | +| test.c:99:32:99:38 | call to realloc | semmle.label | call to realloc | +| test.c:99:40:99:41 | s2 | semmle.label | s2 | +| test.c:99:40:99:41 | s2 | semmle.label | s2 | +| test.c:100:3:100:4 | s3 | semmle.label | s3 | +| test.c:101:10:101:11 | s3 | semmle.label | s3 | subpaths #select | test.c:6:19:6:20 | & ... | test.c:6:19:6:20 | & ... | test.c:6:19:6:20 | & ... | Cast from float to int results in an incompatible pointer base type. | | test.c:11:10:11:11 | & ... | test.c:11:10:11:11 | & ... | test.c:11:10:11:11 | & ... | Cast from short[2] to int results in an incompatible pointer base type. | | test.c:13:17:13:19 | & ... | test.c:13:17:13:19 | & ... | test.c:13:17:13:19 | & ... | Cast from short[2] to short[4] results in an incompatible pointer base type. | | test.c:19:18:19:20 | & ... | test.c:19:18:19:20 | & ... | test.c:19:18:19:20 | & ... | Cast from char to signed char results in an incompatible pointer base type. | -| test.c:29:19:29:21 | & ... | test.c:29:19:29:21 | & ... | test.c:29:19:29:21 | & ... | Cast from int to unsigned int results in an incompatible pointer base type. | -| test.c:47:8:47:9 | s2 | test.c:47:8:47:9 | s2 | test.c:47:8:47:9 | s2 | Cast from struct to struct results in an incompatible pointer base type. | -| test.c:49:8:49:9 | s3 | test.c:49:8:49:9 | s3 | test.c:49:8:49:9 | s3 | Cast from S1 to struct results in an incompatible pointer base type. | -| test.c:50:8:50:9 | s1 | test.c:50:8:50:9 | s1 | test.c:50:8:50:9 | s1 | Cast from struct to S1 results in an incompatible pointer base type. | -| test.c:68:41:68:41 | v | test.c:72:13:72:15 | & ... | test.c:68:41:68:41 | v | Cast from float to int results in an incompatible pointer base type. | -| test.c:99:3:99:4 | s3 | test.c:98:40:98:41 | s2 | test.c:99:3:99:4 | s3 | Cast from S2 to S3 results in an incompatible pointer base type. | +| test.c:30:19:30:21 | & ... | test.c:30:19:30:21 | & ... | test.c:30:19:30:21 | & ... | Cast from int to unsigned int results in an incompatible pointer base type. | +| test.c:48:8:48:9 | s2 | test.c:48:8:48:9 | s2 | test.c:48:8:48:9 | s2 | Cast from struct to struct results in an incompatible pointer base type. | +| test.c:50:8:50:9 | s3 | test.c:50:8:50:9 | s3 | test.c:50:8:50:9 | s3 | Cast from S1 to struct results in an incompatible pointer base type. | +| test.c:51:8:51:9 | s1 | test.c:51:8:51:9 | s1 | test.c:51:8:51:9 | s1 | Cast from struct to S1 results in an incompatible pointer base type. | +| test.c:69:41:69:41 | v | test.c:73:13:73:15 | & ... | test.c:69:41:69:41 | v | Cast from float to int results in an incompatible pointer base type. | +| test.c:100:3:100:4 | s3 | test.c:99:40:99:41 | s2 | test.c:100:3:100:4 | s3 | Cast from S2 to S3 results in an incompatible pointer base type. | From 68b3f39e2ae9fe3044eb37442d5be6e77c92b7d8 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Sat, 28 Jan 2023 01:20:49 +0100 Subject: [PATCH 26/36] EXP43-C: add test-case and update expected results --- ...lisedPointerToRestrictQualifiedParameter.expected | 10 +++++----- ...strictPointerReferencesOverlappingObject.expected | 12 ++++++------ c/cert/test/rules/EXP43-C/test.c | 6 ++++++ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.expected b/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.expected index 61aaf6b4ce..3ad9bc225b 100644 --- a/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.expected +++ b/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.expected @@ -1,5 +1,5 @@ -| test.c:53:3:53:6 | call to copy | Call to 'copy' passes an aliased pointer to a restrict-qualified parameter. | -| test.c:58:3:58:6 | call to copy | Call to 'copy' passes an aliased pointer to a restrict-qualified parameter. | -| test.c:64:3:64:8 | call to strcpy | Call to 'strcpy' passes an aliased pointer to a restrict-qualified parameter. | -| test.c:71:3:71:8 | call to memcpy | Call to 'memcpy' passes an aliased pointer to a restrict-qualified parameter. | -| test.c:84:3:84:7 | call to scanf | Call to 'scanf' passes an aliased pointer to a restrict-qualified parameter. | +| test.c:59:3:59:6 | call to copy | Call to 'copy' passes an aliased pointer to a restrict-qualified parameter. | +| test.c:64:3:64:6 | call to copy | Call to 'copy' passes an aliased pointer to a restrict-qualified parameter. | +| test.c:70:3:70:8 | call to strcpy | Call to 'strcpy' passes an aliased pointer to a restrict-qualified parameter. | +| test.c:77:3:77:8 | call to memcpy | Call to 'memcpy' passes an aliased pointer to a restrict-qualified parameter. | +| test.c:90:3:90:7 | call to scanf | Call to 'scanf' passes an aliased pointer to a restrict-qualified parameter. | diff --git a/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected index 34fe741bd5..66aa8ff233 100644 --- a/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected +++ b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected @@ -1,6 +1,6 @@ -| test.c:16:22:16:23 | i2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:16:17:16:18 | i3 | i3 | -| test.c:17:8:17:9 | g2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:5:15:5:16 | g1 | g1 | -| test.c:18:8:18:9 | i2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:14:17:14:18 | i1 | i1 | -| test.c:26:10:26:11 | g1 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:20:19:20:20 | i4 | i4 | -| test.c:33:22:33:26 | & ... | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:33:17:33:18 | px | px | -| test.c:40:10:40:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:35:19:35:20 | py | py | +| test.c:18:22:18:23 | i2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:18:17:18:18 | i3 | i3 | +| test.c:19:8:19:9 | g2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:5:15:5:16 | g1 | g1 | +| test.c:20:8:20:9 | i2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:16:17:16:18 | i1 | i1 | +| test.c:28:10:28:11 | g1 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:22:19:22:20 | i4 | i4 | +| test.c:39:22:39:26 | & ... | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:39:17:39:18 | px | px | +| test.c:46:10:46:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:41:19:41:20 | py | py | diff --git a/c/cert/test/rules/EXP43-C/test.c b/c/cert/test/rules/EXP43-C/test.c index 31f179a64c..468aa354d2 100644 --- a/c/cert/test/rules/EXP43-C/test.c +++ b/c/cert/test/rules/EXP43-C/test.c @@ -4,6 +4,8 @@ int *restrict g1; int *restrict g2; +int *restrict g1_1; +int *g2_1; struct s1 { int x, y, z; @@ -28,6 +30,10 @@ void test_global_local() { } } +void test_global_local_1() { + g1_1 = g2_1; // COMPLIANT +} + void test_structs() { struct s1 *restrict p1 = &v1; int *restrict px = &v1.x; // NON_COMPLIANT From 934811dcee8abbe0da03058309db7eecf7691e9b Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Sat, 28 Jan 2023 01:22:17 +0100 Subject: [PATCH 27/36] EXP43-C: remove redundant check --- .../rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql | 1 - 1 file changed, 1 deletion(-) diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql index 851fbcebaa..e382814ba1 100644 --- a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql +++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql @@ -68,7 +68,6 @@ where ) or // Two restrict-qualified pointers in the same scope assigned to each other - expr.getVariable().getType().hasSpecifier("restrict") and expr.(VariableAccess).getTarget().getType().hasSpecifier("restrict") and expr.(VariableAccess).getTarget().getParentScope() = expr.getVariable().getParentScope() ) From de6cd014a0acf4f107e9767af4189025cb40634a Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Sat, 28 Jan 2023 01:22:37 +0100 Subject: [PATCH 28/36] Move `PointerOrArrayType` into `Pointers.qll` to deduplicate usage --- ...NotPassAlisedPointerToRestrictQualifiedParameter.ql | 10 ---------- c/common/src/codingstandards/c/Pointers.qll | 10 ++++++++++ .../PointerShouldPointToConstTypeWhenPossible.ql | 8 +------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql b/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql index 60cbfa7756..08c6cb638f 100644 --- a/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql +++ b/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql @@ -17,16 +17,6 @@ import semmle.code.cpp.dataflow.DataFlow import semmle.code.cpp.pointsto.PointsTo import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis -/** - * A type that is a pointer or array type. - */ -class PointerOrArrayType extends DerivedType { - PointerOrArrayType() { - this.stripTopLevelSpecifiers() instanceof PointerType or - this.stripTopLevelSpecifiers() instanceof ArrayType - } -} - /** * A function that has a parameter with a restrict-qualified pointer type. */ diff --git a/c/common/src/codingstandards/c/Pointers.qll b/c/common/src/codingstandards/c/Pointers.qll index 4b4e46a138..6658ec9e81 100644 --- a/c/common/src/codingstandards/c/Pointers.qll +++ b/c/common/src/codingstandards/c/Pointers.qll @@ -5,6 +5,16 @@ import cpp import codingstandards.cpp.Type +/** + * A type that is a pointer or array type. + */ +class PointerOrArrayType extends DerivedType { + PointerOrArrayType() { + this.stripTopLevelSpecifiers() instanceof PointerType or + this.stripTopLevelSpecifiers() instanceof ArrayType + } +} + /** * An expression which performs pointer arithmetic */ diff --git a/c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql b/c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql index f04721883b..5e63e74e2c 100644 --- a/c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql +++ b/c/misra/src/rules/RULE-8-13/PointerShouldPointToConstTypeWhenPossible.ql @@ -15,15 +15,9 @@ import cpp import codingstandards.c.misra +import codingstandards.c.Pointers import codingstandards.cpp.SideEffect -class PointerOrArrayType extends DerivedType { - PointerOrArrayType() { - this.stripTopLevelSpecifiers() instanceof PointerType or - this.stripTopLevelSpecifiers() instanceof ArrayType - } -} - from Variable ptr, PointerOrArrayType type where not isExcluded(ptr, Pointers1Package::pointerShouldPointToConstTypeWhenPossibleQuery()) and From a390e548a8fe6906d349970340a8d9021dcb0439 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Mon, 6 Feb 2023 12:43:57 +0100 Subject: [PATCH 29/36] Update EXP43-C query and test Replace local with global data-flow. Modify the query implementation to detect violations of aliasing at block rather than statement scope as defined in the standard. --- ...trictPointerReferencesOverlappingObject.ql | 37 ++++++++++++------- ...ointerReferencesOverlappingObject.expected | 2 + c/cert/test/rules/EXP43-C/test.c | 4 +- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql index e382814ba1..8974b835b6 100644 --- a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql +++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql @@ -39,8 +39,26 @@ Variable getAddressOfExprTargetBase(AddressOfExpr expr) { result = expr.getOperand().(VariableAccess).getTarget() } +/** + * A data-flow configuration for tracking flow from an assignment or initialization to + * an assignment to an `AssignmentOrInitializationToRestrictPtrValueExpr`. + */ +class AssignedValueToRestrictPtrValueConfiguration extends DataFlow::Configuration { + AssignedValueToRestrictPtrValueConfiguration() { + this = "AssignmentOrInitializationToRestrictPtrValueConfiguration" + } + + override predicate isSource(DataFlow::Node source) { + exists(Variable v | source.asExpr() = v.getAnAssignedValue()) + } + + override predicate isSink(DataFlow::Node sink) { + sink.asExpr() instanceof AssignmentOrInitializationToRestrictPtrValueExpr + } +} + from - AssignmentOrInitializationToRestrictPtrValueExpr source, + AssignedValueToRestrictPtrValueConfiguration config, DataFlow::Node sourceValue, AssignmentOrInitializationToRestrictPtrValueExpr expr, AssignmentOrInitializationToRestrictPtrValueExpr pre_expr where @@ -49,23 +67,14 @@ where // If the same expressions flows to two unique `AssignmentOrInitializationToRestrictPtrValueExpr` // in the same block, then the two variables point to the same (overlapping) object expr.getEnclosingBlock() = pre_expr.getEnclosingBlock() and - strictlyDominates(pre_expr, expr) and ( - dominates(source, pre_expr) and - DataFlow::localExprFlow(source, expr) and - DataFlow::localExprFlow(source, pre_expr) + config.hasFlow(sourceValue, DataFlow::exprNode(pre_expr)) and + config.hasFlow(sourceValue, DataFlow::exprNode(expr)) or // Expressions referring to the address of the same variable can also result in aliasing - getAddressOfExprTargetBase(expr) = getAddressOfExprTargetBase(pre_expr) and - source = - any(AddressOfExpr ao | getAddressOfExprTargetBase(ao) = getAddressOfExprTargetBase(expr)) + getAddressOfExprTargetBase(expr) = getAddressOfExprTargetBase(pre_expr) ) and - // But only if there is no intermediate assignment that could change the value of one of the variables - not exists(AssignmentOrInitializationToRestrictPtrValueExpr mid | - strictlyDominates(mid, expr) and - strictlyDominates(pre_expr, mid) and - not DataFlow::localExprFlow(source, mid) - ) + strictlyDominates(pragma[only_bind_out](pre_expr), pragma[only_bind_out](expr)) or // Two restrict-qualified pointers in the same scope assigned to each other expr.(VariableAccess).getTarget().getType().hasSpecifier("restrict") and diff --git a/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected index 66aa8ff233..cd389fcde3 100644 --- a/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected +++ b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected @@ -1,6 +1,8 @@ | test.c:18:22:18:23 | i2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:18:17:18:18 | i3 | i3 | | test.c:19:8:19:9 | g2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:5:15:5:16 | g1 | g1 | | test.c:20:8:20:9 | i2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:16:17:16:18 | i1 | i1 | +| test.c:27:10:27:11 | g1 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:23:19:23:20 | i5 | i5 | | test.c:28:10:28:11 | g1 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:22:19:22:20 | i4 | i4 | | test.c:39:22:39:26 | & ... | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:39:17:39:18 | px | px | +| test.c:45:10:45:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:42:19:42:20 | pz | pz | | test.c:46:10:46:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:41:19:41:20 | py | py | diff --git a/c/cert/test/rules/EXP43-C/test.c b/c/cert/test/rules/EXP43-C/test.c index 468aa354d2..705c2a171a 100644 --- a/c/cert/test/rules/EXP43-C/test.c +++ b/c/cert/test/rules/EXP43-C/test.c @@ -24,7 +24,7 @@ void test_global_local() { int *restrict i6; i4 = g1; // COMPLIANT i4 = (void *)0; // COMPLIANT - i5 = g1; // COMPLIANT + i5 = g1; // NON_COMPLIANT - block rather than statement scope matters i4 = g1; // NON_COMPLIANT i6 = g2; // COMPLIANT } @@ -42,7 +42,7 @@ void test_structs() { int *restrict pz; py = &v1.y; // COMPLIANT py = (int *)0; - pz = &v1.z; // COMPLIANT + pz = &v1.z; // NON_COMPLIANT - block rather than statement scope matters py = &v1.y; // NON_COMPLIANT } } From c2391975dcbb7bd7970a5482e62a705336fdb068 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Mon, 6 Feb 2023 17:18:37 +0100 Subject: [PATCH 30/36] EXP43-C: swap PointsTo with data-flow and fix typo --- ...AliasedPointerToRestrictQualifiedParam.md} | 0 ...AliasedPointerToRestrictQualifiedParam.ql} | 47 +++++++++++++------ ...trictPointerReferencesOverlappingObject.ql | 11 +---- ...dPointerToRestrictQualifiedParam.expected} | 0 ...iasedPointerToRestrictQualifiedParam.qlref | 1 + ...dPointerToRestrictQualifiedParameter.qlref | 1 - c/common/src/codingstandards/c/Variable.qll | 13 +++++ .../cpp/exclusions/c/Pointers3.qll | 16 +++---- rule_packages/c/Pointers3.json | 2 +- 9 files changed, 57 insertions(+), 34 deletions(-) rename c/cert/src/rules/EXP43-C/{DoNotPassAlisedPointerToRestrictQualifiedParameter.md => DoNotPassAliasedPointerToRestrictQualifiedParam.md} (100%) rename c/cert/src/rules/EXP43-C/{DoNotPassAlisedPointerToRestrictQualifiedParameter.ql => DoNotPassAliasedPointerToRestrictQualifiedParam.ql} (69%) rename c/cert/test/rules/EXP43-C/{DoNotPassAlisedPointerToRestrictQualifiedParameter.expected => DoNotPassAliasedPointerToRestrictQualifiedParam.expected} (100%) create mode 100644 c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.qlref delete mode 100644 c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.qlref diff --git a/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.md b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.md similarity index 100% rename from c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.md rename to c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.md diff --git a/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql similarity index 69% rename from c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql rename to c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql index 08c6cb638f..36bff0b06a 100644 --- a/c/cert/src/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql +++ b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql @@ -1,5 +1,5 @@ /** - * @id c/cert/do-not-pass-alised-pointer-to-restrict-qualified-parameter + * @id c/cert/do-not-pass-aliased-pointer-to-restrict-qualified-param * @name EXP43-C: Do not pass aliased pointers to restrict-qualified parameters * @description Passing an aliased pointer to a restrict-qualified parameter is undefined behavior. * @kind problem @@ -13,7 +13,8 @@ import cpp import codingstandards.c.cert import codingstandards.c.Pointers -import semmle.code.cpp.dataflow.DataFlow +import codingstandards.c.Variable +import semmle.code.cpp.ir.dataflow.DataFlow import semmle.code.cpp.pointsto.PointsTo import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis @@ -67,10 +68,9 @@ class CallToFunctionWithRestrictParameters extends FunctionCall { /** * A `PointsToExpr` that is an argument of a pointer-type in a `CallToFunctionWithRestrictParameters` */ -class ArgPointsToExpr extends PointsToExpr { - override predicate interesting() { - any(CallToFunctionWithRestrictParameters call).getAnArgument() = this and - pointerValue(this) +class CallToFunctionWithRestrictParametersArgExpr extends Expr { + CallToFunctionWithRestrictParametersArgExpr() { + this = any(CallToFunctionWithRestrictParameters call).getAPtrArg() } } @@ -82,7 +82,7 @@ int getStatedValue(Expr e) { .minimum(min(Expr source | DataFlow::localExprFlow(source, e) | source.getValue().toInt())) } -int getPointerArithmeticOperandStatedValue(ArgPointsToExpr expr) { +int getPointerArithmeticOperandStatedValue(CallToFunctionWithRestrictParametersArgExpr expr) { result = getStatedValue(expr.(PointerArithmeticExpr).getOperand()) or // edge-case: &(array[index]) expressions @@ -94,18 +94,37 @@ int getPointerArithmeticOperandStatedValue(ArgPointsToExpr expr) { result = 0 } +class PointerValueToRestrictArgConfig extends DataFlow::Configuration { + PointerValueToRestrictArgConfig() { this = "PointerValueToRestrictArgConfig" } + + override predicate isSource(DataFlow::Node source) { pointerValue(source.asExpr()) } + + override predicate isSink(DataFlow::Node sink) { + exists(CallToFunctionWithRestrictParameters call | + sink.asExpr() = call.getAPtrArg().getAChild*() + ) + } +} + from - CallToFunctionWithRestrictParameters call, ArgPointsToExpr arg1, ArgPointsToExpr arg2, - int argOffset1, int argOffset2 + CallToFunctionWithRestrictParameters call, CallToFunctionWithRestrictParametersArgExpr arg1, + CallToFunctionWithRestrictParametersArgExpr arg2, int argOffset1, int argOffset2 where - not isExcluded(call, Pointers3Package::doNotPassAlisedPointerToRestrictQualifiedParameterQuery()) and + not isExcluded(call, Pointers3Package::doNotPassAliasedPointerToRestrictQualifiedParamQuery()) and arg1 = call.getARestrictPtrArg() and arg2 = call.getAPtrArg() and - // two arguments that point to the same object arg1 != arg2 and - arg1.pointsTo() = arg2.pointsTo() and - arg1.confidence() = 1.0 and - arg2.confidence() = 1.0 and + exists(PointerValueToRestrictArgConfig config, Expr source1, Expr source2 | + config.hasFlow(DataFlow::exprNode(source1), DataFlow::exprNode(arg1.getAChild*())) and + ( + // one pointer value flows to both args + config.hasFlow(DataFlow::exprNode(source1), DataFlow::exprNode(arg2.getAChild*())) + or + // there are two separate values that flow from an AddressOfExpr of the same target + getAddressOfExprTargetBase(source1) = getAddressOfExprTargetBase(source2) and + config.hasFlow(DataFlow::exprNode(source2), DataFlow::exprNode(arg2.getAChild*())) + ) + ) and // get the offset of the pointer arithmetic operand (or '0' if there is none) argOffset1 = getPointerArithmeticOperandStatedValue(arg1) and argOffset2 = getPointerArithmeticOperandStatedValue(arg2) and diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql index 8974b835b6..212d0b06de 100644 --- a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql +++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql @@ -14,6 +14,7 @@ import cpp import semmle.code.cpp.dataflow.DataFlow import semmle.code.cpp.controlflow.Dominance import codingstandards.c.cert +import codingstandards.c.Variable /** * An `Expr` that is an assignment or initialization to a restrict-qualified pointer-type variable. @@ -29,16 +30,6 @@ class AssignmentOrInitializationToRestrictPtrValueExpr extends Expr { Variable getVariable() { result = v } } -/** - * Returns the target variable of a `VariableAccess`. - * If the access is a field access, then the target is the `Variable` of the qualifier. - */ -Variable getAddressOfExprTargetBase(AddressOfExpr expr) { - result = expr.getOperand().(ValueFieldAccess).getQualifier().(VariableAccess).getTarget() - or - result = expr.getOperand().(VariableAccess).getTarget() -} - /** * A data-flow configuration for tracking flow from an assignment or initialization to * an assignment to an `AssignmentOrInitializationToRestrictPtrValueExpr`. diff --git a/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.expected b/c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.expected similarity index 100% rename from c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.expected rename to c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.expected diff --git a/c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.qlref b/c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.qlref new file mode 100644 index 0000000000..6121235f17 --- /dev/null +++ b/c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.qlref @@ -0,0 +1 @@ +rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.qlref b/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.qlref deleted file mode 100644 index 937021c550..0000000000 --- a/c/cert/test/rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/EXP43-C/DoNotPassAlisedPointerToRestrictQualifiedParameter.ql \ No newline at end of file diff --git a/c/common/src/codingstandards/c/Variable.qll b/c/common/src/codingstandards/c/Variable.qll index 5f4492fdd6..40ec32aec5 100644 --- a/c/common/src/codingstandards/c/Variable.qll +++ b/c/common/src/codingstandards/c/Variable.qll @@ -38,3 +38,16 @@ class FlexibleArrayMemberCandidate extends MemberVariable { ) } } + +/** + * Returns the target variable of a `VariableAccess`. + * If the access is a field access, then the target is the `Variable` of the qualifier. + * If the access is an array access, then the target is the array base. + */ +Variable getAddressOfExprTargetBase(AddressOfExpr expr) { + result = expr.getOperand().(ValueFieldAccess).getQualifier().(VariableAccess).getTarget() + or + result = expr.getOperand().(VariableAccess).getTarget() + or + result = expr.getOperand().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll index 8cfc140f99..26a8c43446 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Pointers3.qll @@ -7,7 +7,7 @@ newtype Pointers3Query = TDoNotAccessVolatileObjectWithNonVolatileReferenceQuery() or TDoNotCastPointerToMoreStrictlyAlignedPointerTypeQuery() or TDoNotAccessVariableViaPointerOfIncompatibleTypeQuery() or - TDoNotPassAlisedPointerToRestrictQualifiedParameterQuery() or + TDoNotPassAliasedPointerToRestrictQualifiedParamQuery() or TRestrictPointerReferencesOverlappingObjectQuery() predicate isPointers3QueryMetadata(Query query, string queryId, string ruleId, string category) { @@ -39,11 +39,11 @@ predicate isPointers3QueryMetadata(Query query, string queryId, string ruleId, s category = "rule" or query = - // `Query` instance for the `doNotPassAlisedPointerToRestrictQualifiedParameter` query - Pointers3Package::doNotPassAlisedPointerToRestrictQualifiedParameterQuery() and + // `Query` instance for the `doNotPassAliasedPointerToRestrictQualifiedParam` query + Pointers3Package::doNotPassAliasedPointerToRestrictQualifiedParamQuery() and queryId = - // `@id` for the `doNotPassAlisedPointerToRestrictQualifiedParameter` query - "c/cert/do-not-pass-alised-pointer-to-restrict-qualified-parameter" and + // `@id` for the `doNotPassAliasedPointerToRestrictQualifiedParam` query + "c/cert/do-not-pass-aliased-pointer-to-restrict-qualified-param" and ruleId = "EXP43-C" and category = "rule" or @@ -79,11 +79,11 @@ module Pointers3Package { TQueryC(TPointers3PackageQuery(TDoNotAccessVariableViaPointerOfIncompatibleTypeQuery())) } - Query doNotPassAlisedPointerToRestrictQualifiedParameterQuery() { + Query doNotPassAliasedPointerToRestrictQualifiedParamQuery() { //autogenerate `Query` type result = - // `Query` type for `doNotPassAlisedPointerToRestrictQualifiedParameter` query - TQueryC(TPointers3PackageQuery(TDoNotPassAlisedPointerToRestrictQualifiedParameterQuery())) + // `Query` type for `doNotPassAliasedPointerToRestrictQualifiedParam` query + TQueryC(TPointers3PackageQuery(TDoNotPassAliasedPointerToRestrictQualifiedParamQuery())) } Query restrictPointerReferencesOverlappingObjectQuery() { diff --git a/rule_packages/c/Pointers3.json b/rule_packages/c/Pointers3.json index c8188b38aa..a694300cd5 100644 --- a/rule_packages/c/Pointers3.json +++ b/rule_packages/c/Pointers3.json @@ -71,7 +71,7 @@ "name": "Do not pass aliased pointers to restrict-qualified parameters", "precision": "medium", "severity": "error", - "short_name": "DoNotPassAlisedPointerToRestrictQualifiedParameter", + "short_name": "DoNotPassAliasedPointerToRestrictQualifiedParam", "tags": [ "correctness" ] From 2dd045c746774e6f78144604080f2b7a822ab8be Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Wed, 15 Feb 2023 15:25:40 +0100 Subject: [PATCH 31/36] EXP43-C: Revert IR dataflow to AST data-flow --- .../EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql index 36bff0b06a..070b56d6f2 100644 --- a/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql +++ b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql @@ -14,7 +14,7 @@ import cpp import codingstandards.c.cert import codingstandards.c.Pointers import codingstandards.c.Variable -import semmle.code.cpp.ir.dataflow.DataFlow +import semmle.code.cpp.dataflow.DataFlow import semmle.code.cpp.pointsto.PointsTo import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis From 563e08409243b11b169172c375db9b5389049a8f Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Wed, 15 Feb 2023 16:08:23 +0100 Subject: [PATCH 32/36] EXP43-C: Replace `!=` with `not =` Co-authored-by: Luke Cartey <5377966+lcartey@users.noreply.github.com> --- .../EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql index 070b56d6f2..400c8b871d 100644 --- a/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql +++ b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql @@ -113,7 +113,7 @@ where not isExcluded(call, Pointers3Package::doNotPassAliasedPointerToRestrictQualifiedParamQuery()) and arg1 = call.getARestrictPtrArg() and arg2 = call.getAPtrArg() and - arg1 != arg2 and + not arg1 = arg2 and exists(PointerValueToRestrictArgConfig config, Expr source1, Expr source2 | config.hasFlow(DataFlow::exprNode(source1), DataFlow::exprNode(arg1.getAChild*())) and ( From 3a3a6a5ca963c062fe53ab3eb9ec44b73097a9ee Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Wed, 15 Feb 2023 16:10:33 +0100 Subject: [PATCH 33/36] EXP36-C: Output cast instead of sink node --- ...PointerToMoreStrictlyAlignedPointerType.ql | 2 +- ...rToMoreStrictlyAlignedPointerType.expected | 101 +++++++++--------- c/cert/test/rules/EXP36-C/test.c | 6 +- 3 files changed, 54 insertions(+), 55 deletions(-) diff --git a/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql index 32579dd250..7dd8489415 100644 --- a/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql +++ b/c/cert/src/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.ql @@ -181,7 +181,7 @@ where alignmentFrom = expr.getAlignment() and // flag cases where the cast's target type has stricter alignment requirements than the source alignmentFrom < alignmentTo -select sink, source, sink, +select cast, source, sink, "Cast from pointer with " + alignmentFrom + "-byte alignment (defined by $@) to pointer with base type " + toBaseType.getUnderlyingType() + " with " + alignmentTo + "-byte alignment.", expr.getUnconverted(), expr.getKind() diff --git a/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected b/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected index b70d88fe3f..a1c9a14fa2 100644 --- a/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected +++ b/c/cert/test/rules/EXP36-C/DoNotCastPointerToMoreStrictlyAlignedPointerType.expected @@ -39,7 +39,7 @@ edges | test.c:135:21:135:23 | & ... | test.c:129:22:129:22 | v | | test.c:138:21:138:23 | & ... | test.c:129:22:129:22 | v | | test.c:166:24:166:29 | call to malloc | test.c:167:13:167:15 | & ... | -| test.c:166:24:166:29 | call to malloc | test.c:168:16:168:18 | & ... | +| test.c:166:24:166:29 | call to malloc | test.c:168:16:168:17 | s1 | | test.c:166:24:166:29 | call to malloc | test.c:169:13:169:14 | s1 | | test.c:166:24:166:29 | call to malloc | test.c:169:13:169:14 | s1 | | test.c:169:13:169:14 | s1 | test.c:129:22:129:22 | v | @@ -171,7 +171,7 @@ nodes | test.c:166:24:166:29 | call to malloc | semmle.label | call to malloc | | test.c:166:24:166:29 | call to malloc | semmle.label | call to malloc | | test.c:167:13:167:15 | & ... | semmle.label | & ... | -| test.c:168:16:168:18 | & ... | semmle.label | & ... | +| test.c:168:16:168:17 | s1 | semmle.label | s1 | | test.c:169:13:169:14 | s1 | semmle.label | s1 | | test.c:169:13:169:14 | s1 | semmle.label | s1 | | test.c:172:11:172:12 | s2 | semmle.label | s2 | @@ -217,52 +217,51 @@ nodes | test.c:257:10:257:12 | & ... | semmle.label | & ... | subpaths #select -| test.c:8:12:8:14 | & ... | test.c:8:12:8:14 | & ... | test.c:8:12:8:14 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type short with 2-byte alignment. | test.c:8:12:8:14 | & ... | address-of expression | -| test.c:9:10:9:12 | & ... | test.c:9:10:9:12 | & ... | test.c:9:10:9:12 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:9:10:9:12 | & ... | address-of expression | -| test.c:10:11:10:13 | & ... | test.c:10:11:10:13 | & ... | test.c:10:11:10:13 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:10:11:10:13 | & ... | address-of expression | -| test.c:11:12:11:14 | & ... | test.c:11:12:11:14 | & ... | test.c:11:12:11:14 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:11:12:11:14 | & ... | address-of expression | -| test.c:12:13:12:15 | & ... | test.c:12:13:12:15 | & ... | test.c:12:13:12:15 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:12:13:12:15 | & ... | address-of expression | -| test.c:17:10:17:12 | & ... | test.c:17:10:17:12 | & ... | test.c:17:10:17:12 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:17:10:17:12 | & ... | address-of expression | -| test.c:18:11:18:13 | & ... | test.c:18:11:18:13 | & ... | test.c:18:11:18:13 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:18:11:18:13 | & ... | address-of expression | -| test.c:19:12:19:14 | & ... | test.c:19:12:19:14 | & ... | test.c:19:12:19:14 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:19:12:19:14 | & ... | address-of expression | -| test.c:20:13:20:15 | & ... | test.c:20:13:20:15 | & ... | test.c:20:13:20:15 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:20:13:20:15 | & ... | address-of expression | -| test.c:27:11:27:13 | & ... | test.c:27:11:27:13 | & ... | test.c:27:11:27:13 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:27:11:27:13 | & ... | address-of expression | -| test.c:28:13:28:15 | & ... | test.c:28:13:28:15 | & ... | test.c:28:13:28:15 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:28:13:28:15 | & ... | address-of expression | -| test.c:35:11:35:13 | & ... | test.c:35:11:35:13 | & ... | test.c:35:11:35:13 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:35:11:35:13 | & ... | address-of expression | -| test.c:36:13:36:15 | & ... | test.c:36:13:36:15 | & ... | test.c:36:13:36:15 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:36:13:36:15 | & ... | address-of expression | -| test.c:61:11:61:13 | & ... | test.c:61:11:61:13 | & ... | test.c:61:11:61:13 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:61:11:61:13 | & ... | address-of expression | -| test.c:62:13:62:15 | & ... | test.c:62:13:62:15 | & ... | test.c:62:13:62:15 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:62:13:62:15 | & ... | address-of expression | -| test.c:77:12:77:13 | v1 | test.c:75:14:75:16 | & ... | test.c:77:12:77:13 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type short with 2-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | -| test.c:78:10:78:11 | v1 | test.c:75:14:75:16 | & ... | test.c:78:10:78:11 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | -| test.c:79:12:79:13 | v1 | test.c:75:14:75:16 | & ... | test.c:79:12:79:13 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | -| test.c:80:11:80:12 | v1 | test.c:75:14:75:16 | & ... | test.c:80:11:80:12 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | -| test.c:81:13:81:14 | v1 | test.c:75:14:75:16 | & ... | test.c:81:13:81:14 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | -| test.c:87:10:87:11 | v2 | test.c:84:14:84:16 | & ... | test.c:87:10:87:11 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | -| test.c:88:12:88:13 | v2 | test.c:84:14:84:16 | & ... | test.c:88:12:88:13 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | -| test.c:89:11:89:12 | v2 | test.c:84:14:84:16 | & ... | test.c:89:11:89:12 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | -| test.c:90:13:90:14 | v2 | test.c:84:14:84:16 | & ... | test.c:90:13:90:14 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | -| test.c:98:11:98:12 | v3 | test.c:93:14:93:16 | & ... | test.c:98:11:98:12 | v3 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:93:14:93:16 | & ... | address-of expression | -| test.c:99:13:99:14 | v3 | test.c:93:14:93:16 | & ... | test.c:99:13:99:14 | v3 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:93:14:93:16 | & ... | address-of expression | -| test.c:107:11:107:12 | v4 | test.c:102:14:102:16 | & ... | test.c:107:11:107:12 | v4 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:102:14:102:16 | & ... | address-of expression | -| test.c:108:13:108:14 | v4 | test.c:102:14:102:16 | & ... | test.c:108:13:108:14 | v4 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:102:14:102:16 | & ... | address-of expression | -| test.c:130:17:130:17 | v | test.c:135:21:135:23 | & ... | test.c:130:17:130:17 | v | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:135:21:135:23 | & ... | address-of expression | -| test.c:130:17:130:17 | v | test.c:174:13:174:14 | s2 | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:174:13:174:14 | s2 | pointer base type short | -| test.c:130:17:130:17 | v | test.c:179:13:179:14 | s3 | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:179:13:179:14 | s3 | pointer base type short | -| test.c:130:17:130:17 | v | test.c:189:14:189:26 | call to aligned_alloc | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:189:14:189:26 | call to aligned_alloc | call to aligned_alloc | -| test.c:158:13:158:20 | & ... | test.c:158:13:158:20 | & ... | test.c:158:13:158:20 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:158:13:158:20 | & ... | address-of expression | -| test.c:162:16:162:18 | & ... | test.c:162:16:162:18 | & ... | test.c:162:16:162:18 | & ... | Cast from pointer with 8-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:162:16:162:18 | & ... | address-of expression | -| test.c:168:16:168:18 | & ... | test.c:166:24:166:29 | call to malloc | test.c:168:16:168:18 | & ... | Cast from pointer with 16-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:166:24:166:29 | call to malloc | call to malloc | -| test.c:168:16:168:18 | & ... | test.c:168:16:168:18 | & ... | test.c:168:16:168:18 | & ... | Cast from pointer with 8-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:168:16:168:18 | & ... | address-of expression | -| test.c:173:13:173:14 | s2 | test.c:173:13:173:14 | s2 | test.c:173:13:173:14 | s2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:173:13:173:14 | s2 | pointer base type short | -| test.c:178:13:178:14 | s3 | test.c:178:13:178:14 | s3 | test.c:178:13:178:14 | s3 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:178:13:178:14 | s3 | pointer base type short | -| test.c:186:13:186:14 | v1 | test.c:183:14:183:26 | call to aligned_alloc | test.c:186:13:186:14 | v1 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:183:14:183:26 | call to aligned_alloc | call to aligned_alloc | -| test.c:216:10:216:11 | p2 | test.c:216:10:216:11 | p2 | test.c:216:10:216:11 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:216:10:216:11 | p2 | pointer base type short | -| test.c:217:11:217:12 | p2 | test.c:217:11:217:12 | p2 | test.c:217:11:217:12 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:217:11:217:12 | p2 | pointer base type short | -| test.c:218:12:218:13 | p2 | test.c:218:12:218:13 | p2 | test.c:218:12:218:13 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:218:12:218:13 | p2 | pointer base type short | -| test.c:219:13:219:14 | p2 | test.c:219:13:219:14 | p2 | test.c:219:13:219:14 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:219:13:219:14 | p2 | pointer base type short | -| test.c:225:10:225:11 | v1 | test.c:222:8:222:9 | p2 | test.c:225:10:225:11 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | -| test.c:226:12:226:13 | v1 | test.c:222:8:222:9 | p2 | test.c:226:12:226:13 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | -| test.c:227:11:227:12 | v1 | test.c:222:8:222:9 | p2 | test.c:227:11:227:12 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | -| test.c:228:13:228:14 | v1 | test.c:222:8:222:9 | p2 | test.c:228:13:228:14 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | -| test.c:256:10:256:12 | ps1 | test.c:252:16:252:18 | & ... | test.c:256:10:256:12 | ps1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:252:16:252:18 | & ... | address-of expression | -| test.c:257:10:257:12 | & ... | test.c:257:10:257:12 | & ... | test.c:257:10:257:12 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:257:10:257:12 | & ... | address-of expression | +| test.c:8:3:8:14 | (short *)... | test.c:8:12:8:14 | & ... | test.c:8:12:8:14 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type short with 2-byte alignment. | test.c:8:12:8:14 | & ... | address-of expression | +| test.c:9:3:9:12 | (int *)... | test.c:9:10:9:12 | & ... | test.c:9:10:9:12 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:9:10:9:12 | & ... | address-of expression | +| test.c:10:3:10:13 | (long *)... | test.c:10:11:10:13 | & ... | test.c:10:11:10:13 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:10:11:10:13 | & ... | address-of expression | +| test.c:11:3:11:14 | (float *)... | test.c:11:12:11:14 | & ... | test.c:11:12:11:14 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:11:12:11:14 | & ... | address-of expression | +| test.c:12:3:12:15 | (double *)... | test.c:12:13:12:15 | & ... | test.c:12:13:12:15 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:12:13:12:15 | & ... | address-of expression | +| test.c:17:3:17:12 | (int *)... | test.c:17:10:17:12 | & ... | test.c:17:10:17:12 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:17:10:17:12 | & ... | address-of expression | +| test.c:18:3:18:13 | (long *)... | test.c:18:11:18:13 | & ... | test.c:18:11:18:13 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:18:11:18:13 | & ... | address-of expression | +| test.c:19:3:19:14 | (float *)... | test.c:19:12:19:14 | & ... | test.c:19:12:19:14 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:19:12:19:14 | & ... | address-of expression | +| test.c:20:3:20:15 | (double *)... | test.c:20:13:20:15 | & ... | test.c:20:13:20:15 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:20:13:20:15 | & ... | address-of expression | +| test.c:27:3:27:13 | (long *)... | test.c:27:11:27:13 | & ... | test.c:27:11:27:13 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:27:11:27:13 | & ... | address-of expression | +| test.c:28:3:28:15 | (double *)... | test.c:28:13:28:15 | & ... | test.c:28:13:28:15 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:28:13:28:15 | & ... | address-of expression | +| test.c:35:3:35:13 | (long *)... | test.c:35:11:35:13 | & ... | test.c:35:11:35:13 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:35:11:35:13 | & ... | address-of expression | +| test.c:36:3:36:15 | (double *)... | test.c:36:13:36:15 | & ... | test.c:36:13:36:15 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:36:13:36:15 | & ... | address-of expression | +| test.c:61:3:61:13 | (long *)... | test.c:61:11:61:13 | & ... | test.c:61:11:61:13 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:61:11:61:13 | & ... | address-of expression | +| test.c:62:3:62:15 | (double *)... | test.c:62:13:62:15 | & ... | test.c:62:13:62:15 | & ... | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:62:13:62:15 | & ... | address-of expression | +| test.c:77:3:77:13 | (short *)... | test.c:75:14:75:16 | & ... | test.c:77:12:77:13 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type short with 2-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:78:3:78:11 | (int *)... | test.c:75:14:75:16 | & ... | test.c:78:10:78:11 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:79:3:79:13 | (float *)... | test.c:75:14:75:16 | & ... | test.c:79:12:79:13 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:80:3:80:12 | (long *)... | test.c:75:14:75:16 | & ... | test.c:80:11:80:12 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:81:3:81:14 | (double *)... | test.c:75:14:75:16 | & ... | test.c:81:13:81:14 | v1 | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:75:14:75:16 | & ... | address-of expression | +| test.c:87:3:87:11 | (int *)... | test.c:84:14:84:16 | & ... | test.c:87:10:87:11 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | +| test.c:88:3:88:13 | (float *)... | test.c:84:14:84:16 | & ... | test.c:88:12:88:13 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | +| test.c:89:3:89:12 | (long *)... | test.c:84:14:84:16 | & ... | test.c:89:11:89:12 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | +| test.c:90:3:90:14 | (double *)... | test.c:84:14:84:16 | & ... | test.c:90:13:90:14 | v2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:84:14:84:16 | & ... | address-of expression | +| test.c:98:3:98:12 | (long *)... | test.c:93:14:93:16 | & ... | test.c:98:11:98:12 | v3 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:93:14:93:16 | & ... | address-of expression | +| test.c:99:3:99:14 | (double *)... | test.c:93:14:93:16 | & ... | test.c:99:13:99:14 | v3 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:93:14:93:16 | & ... | address-of expression | +| test.c:107:3:107:12 | (long *)... | test.c:102:14:102:16 | & ... | test.c:107:11:107:12 | v4 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:102:14:102:16 | & ... | address-of expression | +| test.c:108:3:108:14 | (double *)... | test.c:102:14:102:16 | & ... | test.c:108:13:108:14 | v4 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:102:14:102:16 | & ... | address-of expression | +| test.c:130:10:130:17 | (int *)... | test.c:135:21:135:23 | & ... | test.c:130:17:130:17 | v | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:135:21:135:23 | & ... | address-of expression | +| test.c:130:10:130:17 | (int *)... | test.c:174:13:174:14 | s2 | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:174:13:174:14 | s2 | pointer base type short | +| test.c:130:10:130:17 | (int *)... | test.c:179:13:179:14 | s3 | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:179:13:179:14 | s3 | pointer base type short | +| test.c:130:10:130:17 | (int *)... | test.c:189:14:189:26 | call to aligned_alloc | test.c:130:17:130:17 | v | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:189:14:189:26 | call to aligned_alloc | call to aligned_alloc | +| test.c:158:3:158:20 | (size_t *)... | test.c:158:13:158:20 | & ... | test.c:158:13:158:20 | & ... | Cast from pointer with 1-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:158:13:158:20 | & ... | address-of expression | +| test.c:162:3:162:18 | (S3 *)... | test.c:162:16:162:18 | & ... | test.c:162:16:162:18 | & ... | Cast from pointer with 8-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:162:16:162:18 | & ... | address-of expression | +| test.c:168:3:168:17 | (S3 *)... | test.c:166:24:166:29 | call to malloc | test.c:168:16:168:17 | s1 | Cast from pointer with 16-byte alignment (defined by $@) to pointer with base type S3 with 64-byte alignment. | test.c:166:24:166:29 | call to malloc | call to malloc | +| test.c:173:3:173:14 | (size_t *)... | test.c:173:13:173:14 | s2 | test.c:173:13:173:14 | s2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:173:13:173:14 | s2 | pointer base type short | +| test.c:178:3:178:14 | (size_t *)... | test.c:178:13:178:14 | s3 | test.c:178:13:178:14 | s3 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:178:13:178:14 | s3 | pointer base type short | +| test.c:186:3:186:14 | (size_t *)... | test.c:183:14:183:26 | call to aligned_alloc | test.c:186:13:186:14 | v1 | Cast from pointer with 4-byte alignment (defined by $@) to pointer with base type unsigned long with 8-byte alignment. | test.c:183:14:183:26 | call to aligned_alloc | call to aligned_alloc | +| test.c:216:3:216:11 | (int *)... | test.c:216:10:216:11 | p2 | test.c:216:10:216:11 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:216:10:216:11 | p2 | pointer base type short | +| test.c:217:3:217:12 | (long *)... | test.c:217:11:217:12 | p2 | test.c:217:11:217:12 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:217:11:217:12 | p2 | pointer base type short | +| test.c:218:3:218:13 | (float *)... | test.c:218:12:218:13 | p2 | test.c:218:12:218:13 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:218:12:218:13 | p2 | pointer base type short | +| test.c:219:3:219:14 | (double *)... | test.c:219:13:219:14 | p2 | test.c:219:13:219:14 | p2 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:219:13:219:14 | p2 | pointer base type short | +| test.c:225:3:225:11 | (int *)... | test.c:222:8:222:9 | p2 | test.c:225:10:225:11 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | +| test.c:226:3:226:13 | (float *)... | test.c:222:8:222:9 | p2 | test.c:226:12:226:13 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type float with 4-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | +| test.c:227:3:227:12 | (long *)... | test.c:222:8:222:9 | p2 | test.c:227:11:227:12 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type long with 8-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | +| test.c:228:3:228:14 | (double *)... | test.c:222:8:222:9 | p2 | test.c:228:13:228:14 | v1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type double with 8-byte alignment. | test.c:222:8:222:9 | p2 | pointer base type short | +| test.c:256:3:256:12 | (int *)... | test.c:252:16:252:18 | & ... | test.c:256:10:256:12 | ps1 | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:252:16:252:18 | & ... | address-of expression | +| test.c:257:3:257:12 | (int *)... | test.c:257:10:257:12 | & ... | test.c:257:10:257:12 | & ... | Cast from pointer with 2-byte alignment (defined by $@) to pointer with base type int with 4-byte alignment. | test.c:257:10:257:12 | & ... | address-of expression | diff --git a/c/cert/test/rules/EXP36-C/test.c b/c/cert/test/rules/EXP36-C/test.c index 0d0eef551a..587bc6a183 100644 --- a/c/cert/test/rules/EXP36-C/test.c +++ b/c/cert/test/rules/EXP36-C/test.c @@ -164,9 +164,9 @@ void test_struct_alignment() { void test_malloc_alignment_and_pointer_arithmetic() { short *s1 = (short *)malloc(64); - (size_t *)&s1; // COMPLIANT - (struct S3 *)&s1; // NON_COMPLIANT - over-aligned struct - cast_away(s1); // COMPLIANT + (size_t *)&s1; // COMPLIANT + (struct S3 *)s1; // NON_COMPLIANT - over-aligned struct + cast_away(s1); // COMPLIANT short *s2 = s1 + 1; (char *)s2; // COMPLIANT From 009fc65836aba97a3ab5a2ccb1c3cb45da23cdba Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Wed, 15 Feb 2023 17:07:53 +0100 Subject: [PATCH 34/36] EXP39-C: Remove redundant "TODO" comment from test-case --- ...essVariableViaPointerOfIncompatibleType.ql | 1 + ...iableViaPointerOfIncompatibleType.expected | 78 +++++++++---------- c/cert/test/rules/EXP39-C/test.c | 1 - 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql index d9ef7e742c..88b464f769 100644 --- a/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql +++ b/c/cert/src/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.ql @@ -153,6 +153,7 @@ Type compatibleTypes(Type type) { ) and ( ( + // all types are compatible with void and explicitly-unsigned char types result instanceof UnsignedCharType or [result.stripTopLevelSpecifiers(), type.stripTopLevelSpecifiers()] instanceof VoidType ) diff --git a/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected index da5437e3bb..4ca3d89b25 100644 --- a/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected +++ b/c/cert/test/rules/EXP39-C/DoNotAccessVariableViaPointerOfIncompatibleType.expected @@ -1,15 +1,15 @@ edges -| test.c:50:8:50:9 | s3 | test.c:51:8:51:9 | s1 | -| test.c:61:16:61:18 | E1A | test.c:62:16:62:17 | e1 | -| test.c:61:16:61:18 | E1A | test.c:66:10:66:12 | & ... | -| test.c:69:22:69:22 | v | test.c:69:41:69:41 | v | -| test.c:73:13:73:15 | & ... | test.c:69:22:69:22 | v | -| test.c:75:13:75:15 | & ... | test.c:69:22:69:22 | v | -| test.c:98:32:98:37 | call to malloc | test.c:99:40:99:41 | s2 | -| test.c:98:32:98:37 | call to malloc | test.c:99:40:99:41 | s2 | -| test.c:99:32:99:38 | call to realloc | test.c:100:3:100:4 | s3 | -| test.c:99:32:99:38 | call to realloc | test.c:101:10:101:11 | s3 | -| test.c:99:40:99:41 | s2 | test.c:99:32:99:38 | call to realloc | +| test.c:49:8:49:9 | s3 | test.c:50:8:50:9 | s1 | +| test.c:60:16:60:18 | E1A | test.c:61:16:61:17 | e1 | +| test.c:60:16:60:18 | E1A | test.c:65:10:65:12 | & ... | +| test.c:68:22:68:22 | v | test.c:68:41:68:41 | v | +| test.c:72:13:72:15 | & ... | test.c:68:22:68:22 | v | +| test.c:74:13:74:15 | & ... | test.c:68:22:68:22 | v | +| test.c:97:32:97:37 | call to malloc | test.c:98:40:98:41 | s2 | +| test.c:97:32:97:37 | call to malloc | test.c:98:40:98:41 | s2 | +| test.c:98:32:98:38 | call to realloc | test.c:99:3:99:4 | s3 | +| test.c:98:32:98:38 | call to realloc | test.c:100:10:100:11 | s3 | +| test.c:98:40:98:41 | s2 | test.c:98:32:98:38 | call to realloc | nodes | test.c:6:19:6:20 | & ... | semmle.label | & ... | | test.c:11:10:11:11 | & ... | semmle.label | & ... | @@ -23,29 +23,29 @@ nodes | test.c:29:13:29:15 | & ... | semmle.label | & ... | | test.c:30:19:30:21 | & ... | semmle.label | & ... | | test.c:31:16:31:18 | & ... | semmle.label | & ... | -| test.c:48:8:48:9 | s2 | semmle.label | s2 | -| test.c:50:8:50:9 | s3 | semmle.label | s3 | -| test.c:50:8:50:9 | s3 | semmle.label | s3 | -| test.c:51:8:51:9 | s1 | semmle.label | s1 | -| test.c:61:16:61:18 | E1A | semmle.label | E1A | -| test.c:61:16:61:18 | E1A | semmle.label | E1A | -| test.c:62:16:62:17 | e1 | semmle.label | e1 | -| test.c:66:10:66:12 | & ... | semmle.label | & ... | -| test.c:69:22:69:22 | v | semmle.label | v | -| test.c:69:41:69:41 | v | semmle.label | v | -| test.c:73:13:73:15 | & ... | semmle.label | & ... | -| test.c:73:13:73:15 | & ... | semmle.label | & ... | -| test.c:75:13:75:15 | & ... | semmle.label | & ... | -| test.c:75:13:75:15 | & ... | semmle.label | & ... | -| test.c:98:32:98:37 | call to malloc | semmle.label | call to malloc | -| test.c:98:32:98:37 | call to malloc | semmle.label | call to malloc | -| test.c:99:32:99:38 | call to realloc | semmle.label | call to realloc | -| test.c:99:32:99:38 | call to realloc | semmle.label | call to realloc | -| test.c:99:32:99:38 | call to realloc | semmle.label | call to realloc | -| test.c:99:40:99:41 | s2 | semmle.label | s2 | -| test.c:99:40:99:41 | s2 | semmle.label | s2 | -| test.c:100:3:100:4 | s3 | semmle.label | s3 | -| test.c:101:10:101:11 | s3 | semmle.label | s3 | +| test.c:47:8:47:9 | s2 | semmle.label | s2 | +| test.c:49:8:49:9 | s3 | semmle.label | s3 | +| test.c:49:8:49:9 | s3 | semmle.label | s3 | +| test.c:50:8:50:9 | s1 | semmle.label | s1 | +| test.c:60:16:60:18 | E1A | semmle.label | E1A | +| test.c:60:16:60:18 | E1A | semmle.label | E1A | +| test.c:61:16:61:17 | e1 | semmle.label | e1 | +| test.c:65:10:65:12 | & ... | semmle.label | & ... | +| test.c:68:22:68:22 | v | semmle.label | v | +| test.c:68:41:68:41 | v | semmle.label | v | +| test.c:72:13:72:15 | & ... | semmle.label | & ... | +| test.c:72:13:72:15 | & ... | semmle.label | & ... | +| test.c:74:13:74:15 | & ... | semmle.label | & ... | +| test.c:74:13:74:15 | & ... | semmle.label | & ... | +| test.c:97:32:97:37 | call to malloc | semmle.label | call to malloc | +| test.c:97:32:97:37 | call to malloc | semmle.label | call to malloc | +| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | +| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | +| test.c:98:32:98:38 | call to realloc | semmle.label | call to realloc | +| test.c:98:40:98:41 | s2 | semmle.label | s2 | +| test.c:98:40:98:41 | s2 | semmle.label | s2 | +| test.c:99:3:99:4 | s3 | semmle.label | s3 | +| test.c:100:10:100:11 | s3 | semmle.label | s3 | subpaths #select | test.c:6:19:6:20 | & ... | test.c:6:19:6:20 | & ... | test.c:6:19:6:20 | & ... | Cast from float to int results in an incompatible pointer base type. | @@ -53,8 +53,8 @@ subpaths | test.c:13:17:13:19 | & ... | test.c:13:17:13:19 | & ... | test.c:13:17:13:19 | & ... | Cast from short[2] to short[4] results in an incompatible pointer base type. | | test.c:19:18:19:20 | & ... | test.c:19:18:19:20 | & ... | test.c:19:18:19:20 | & ... | Cast from char to signed char results in an incompatible pointer base type. | | test.c:30:19:30:21 | & ... | test.c:30:19:30:21 | & ... | test.c:30:19:30:21 | & ... | Cast from int to unsigned int results in an incompatible pointer base type. | -| test.c:48:8:48:9 | s2 | test.c:48:8:48:9 | s2 | test.c:48:8:48:9 | s2 | Cast from struct to struct results in an incompatible pointer base type. | -| test.c:50:8:50:9 | s3 | test.c:50:8:50:9 | s3 | test.c:50:8:50:9 | s3 | Cast from S1 to struct results in an incompatible pointer base type. | -| test.c:51:8:51:9 | s1 | test.c:51:8:51:9 | s1 | test.c:51:8:51:9 | s1 | Cast from struct to S1 results in an incompatible pointer base type. | -| test.c:69:41:69:41 | v | test.c:73:13:73:15 | & ... | test.c:69:41:69:41 | v | Cast from float to int results in an incompatible pointer base type. | -| test.c:100:3:100:4 | s3 | test.c:99:40:99:41 | s2 | test.c:100:3:100:4 | s3 | Cast from S2 to S3 results in an incompatible pointer base type. | +| test.c:47:8:47:9 | s2 | test.c:47:8:47:9 | s2 | test.c:47:8:47:9 | s2 | Cast from struct to struct results in an incompatible pointer base type. | +| test.c:49:8:49:9 | s3 | test.c:49:8:49:9 | s3 | test.c:49:8:49:9 | s3 | Cast from S1 to struct results in an incompatible pointer base type. | +| test.c:50:8:50:9 | s1 | test.c:50:8:50:9 | s1 | test.c:50:8:50:9 | s1 | Cast from struct to S1 results in an incompatible pointer base type. | +| test.c:68:41:68:41 | v | test.c:72:13:72:15 | & ... | test.c:68:41:68:41 | v | Cast from float to int results in an incompatible pointer base type. | +| test.c:99:3:99:4 | s3 | test.c:98:40:98:41 | s2 | test.c:99:3:99:4 | s3 | Cast from S2 to S3 results in an incompatible pointer base type. | diff --git a/c/cert/test/rules/EXP39-C/test.c b/c/cert/test/rules/EXP39-C/test.c index 60a0a428c7..75d1eed462 100644 --- a/c/cert/test/rules/EXP39-C/test.c +++ b/c/cert/test/rules/EXP39-C/test.c @@ -42,7 +42,6 @@ struct S1 { } * s3; struct S1 *s4; -// TODO test across files void test_incompatible_structs() { // s1 and s2 do not have tags, and are therefore not compatible s1 = s2; // NON_COMPLIANT From 6a554acde75048e840dbe48546ccc1641ea3bee9 Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Wed, 15 Feb 2023 22:10:16 +0100 Subject: [PATCH 35/36] EXP43-C: Refine data-flow and add context to output --- ...sAliasedPointerToRestrictQualifiedParam.ql | 42 ++++++++++----- ...trictPointerReferencesOverlappingObject.ql | 52 ++++++++++++------- ...edPointerToRestrictQualifiedParam.expected | 11 ++-- ...ointerReferencesOverlappingObject.expected | 17 +++--- c/cert/test/rules/EXP43-C/test.c | 7 ++- c/common/src/codingstandards/c/Variable.qll | 1 + 6 files changed, 85 insertions(+), 45 deletions(-) diff --git a/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql index 070b56d6f2..04f88897d3 100644 --- a/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql +++ b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql @@ -49,8 +49,8 @@ class CallToFunctionWithRestrictParameters extends FunctionCall { .getIndex()) } - Expr getAPtrArg() { - result = this.getAnArgument() and + Expr getAPtrArg(int index) { + result = this.getArgument(index) and pointerValue(result) } @@ -69,9 +69,13 @@ class CallToFunctionWithRestrictParameters extends FunctionCall { * A `PointsToExpr` that is an argument of a pointer-type in a `CallToFunctionWithRestrictParameters` */ class CallToFunctionWithRestrictParametersArgExpr extends Expr { + int paramIndex; + CallToFunctionWithRestrictParametersArgExpr() { - this = any(CallToFunctionWithRestrictParameters call).getAPtrArg() + this = any(CallToFunctionWithRestrictParameters call).getAPtrArg(paramIndex) } + + int getParamIndex() { result = paramIndex } } int getStatedValue(Expr e) { @@ -101,28 +105,41 @@ class PointerValueToRestrictArgConfig extends DataFlow::Configuration { override predicate isSink(DataFlow::Node sink) { exists(CallToFunctionWithRestrictParameters call | - sink.asExpr() = call.getAPtrArg().getAChild*() + sink.asExpr() = call.getAPtrArg(_).getAChild*() ) } + + override predicate isBarrierIn(DataFlow::Node node) { + exists(AddressOfExpr a | node.asExpr() = a.getOperand().getAChild*()) + } } from CallToFunctionWithRestrictParameters call, CallToFunctionWithRestrictParametersArgExpr arg1, - CallToFunctionWithRestrictParametersArgExpr arg2, int argOffset1, int argOffset2 + CallToFunctionWithRestrictParametersArgExpr arg2, int argOffset1, int argOffset2, Expr source1, + Expr source2, string sourceMessage1, string sourceMessage2 where not isExcluded(call, Pointers3Package::doNotPassAliasedPointerToRestrictQualifiedParamQuery()) and arg1 = call.getARestrictPtrArg() and - arg2 = call.getAPtrArg() and - arg1 != arg2 and - exists(PointerValueToRestrictArgConfig config, Expr source1, Expr source2 | + arg2 = call.getAPtrArg(_) and + // enforce ordering to remove permutations if multiple restrict-qualified args exist + (not arg2 = call.getARestrictPtrArg() or arg2.getParamIndex() > arg1.getParamIndex()) and + // check if two pointers address the same object + exists(PointerValueToRestrictArgConfig config | config.hasFlow(DataFlow::exprNode(source1), DataFlow::exprNode(arg1.getAChild*())) and ( // one pointer value flows to both args - config.hasFlow(DataFlow::exprNode(source1), DataFlow::exprNode(arg2.getAChild*())) + config.hasFlow(DataFlow::exprNode(source1), DataFlow::exprNode(arg2.getAChild*())) and + sourceMessage1 = "$@" and + sourceMessage2 = "source" and + source1 = source2 or // there are two separate values that flow from an AddressOfExpr of the same target getAddressOfExprTargetBase(source1) = getAddressOfExprTargetBase(source2) and - config.hasFlow(DataFlow::exprNode(source2), DataFlow::exprNode(arg2.getAChild*())) + config.hasFlow(DataFlow::exprNode(source2), DataFlow::exprNode(arg2.getAChild*())) and + sourceMessage1 = "a pair of address-of expressions ($@, $@)" and + sourceMessage2 = "addressof1" and + not source1 = source2 ) ) and // get the offset of the pointer arithmetic operand (or '0' if there is none) @@ -146,5 +163,6 @@ where not exists(call.getAPossibleSizeArg()) ) select call, - "Call to '" + call.getTarget().getName() + - "' passes an aliased pointer to a restrict-qualified parameter." + "Call to '" + call.getTarget().getName() + "' passes an $@ to a $@ (pointer value derived from " + + sourceMessage1 + ".", arg2, "aliased pointer", arg1, "restrict-qualified parameter", source1, + sourceMessage2, source2, "addressof2" diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql index 212d0b06de..6d5ba288af 100644 --- a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql +++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql @@ -28,6 +28,11 @@ class AssignmentOrInitializationToRestrictPtrValueExpr extends Expr { } Variable getVariable() { result = v } + + predicate isTargetRestrictQualifiedAndInSameScope() { + this.(VariableAccess).getTarget().getType().hasSpecifier("restrict") and + this.(VariableAccess).getTarget().getParentScope() = this.getVariable().getParentScope() + } } /** @@ -46,30 +51,41 @@ class AssignedValueToRestrictPtrValueConfiguration extends DataFlow::Configurati override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof AssignmentOrInitializationToRestrictPtrValueExpr } + + override predicate isBarrierIn(DataFlow::Node node) { + isSource(node) + } } from - AssignedValueToRestrictPtrValueConfiguration config, DataFlow::Node sourceValue, - AssignmentOrInitializationToRestrictPtrValueExpr expr, - AssignmentOrInitializationToRestrictPtrValueExpr pre_expr + AssignedValueToRestrictPtrValueConfiguration config, + AssignmentOrInitializationToRestrictPtrValueExpr expr, DataFlow::Node sourceValue, + string sourceMessage where not isExcluded(expr, Pointers3Package::restrictPointerReferencesOverlappingObjectQuery()) and ( + // Two restrict-qualified pointers in the same scope assigned to each other + expr.isTargetRestrictQualifiedAndInSameScope() and + sourceValue.asExpr() = expr and + sourceMessage = "the object pointed to by " + expr.(VariableAccess).getTarget().getName() + or // If the same expressions flows to two unique `AssignmentOrInitializationToRestrictPtrValueExpr` // in the same block, then the two variables point to the same (overlapping) object - expr.getEnclosingBlock() = pre_expr.getEnclosingBlock() and - ( - config.hasFlow(sourceValue, DataFlow::exprNode(pre_expr)) and - config.hasFlow(sourceValue, DataFlow::exprNode(expr)) - or - // Expressions referring to the address of the same variable can also result in aliasing - getAddressOfExprTargetBase(expr) = getAddressOfExprTargetBase(pre_expr) - ) and - strictlyDominates(pragma[only_bind_out](pre_expr), pragma[only_bind_out](expr)) - or - // Two restrict-qualified pointers in the same scope assigned to each other - expr.(VariableAccess).getTarget().getType().hasSpecifier("restrict") and - expr.(VariableAccess).getTarget().getParentScope() = expr.getVariable().getParentScope() + not expr.isTargetRestrictQualifiedAndInSameScope() and + exists(AssignmentOrInitializationToRestrictPtrValueExpr pre_expr | + expr.getEnclosingBlock() = pre_expr.getEnclosingBlock() and + ( + config.hasFlow(sourceValue, DataFlow::exprNode(pre_expr)) and + config.hasFlow(sourceValue, DataFlow::exprNode(expr)) and + sourceMessage = "the same source value" + or + // Expressions referring to the address of the same variable can also result in aliasing + getAddressOfExprTargetBase(expr) = getAddressOfExprTargetBase(pre_expr) and + sourceValue.asExpr() = pre_expr and + sourceMessage = getAddressOfExprTargetBase(expr).getName() + " via address-of" + ) and + strictlyDominates(pragma[only_bind_out](pre_expr), pragma[only_bind_out](expr)) + ) ) -select expr, "Assignment to restrict-qualified pointer $@ results in pointer aliasing.", - expr.getVariable(), expr.getVariable().getName() +select expr, "Assignment to restrict-qualified pointer $@ results in pointers aliasing $@.", + expr.getVariable(), expr.getVariable().getName(), sourceValue, sourceMessage diff --git a/c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.expected b/c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.expected index 3ad9bc225b..4d4c20a39c 100644 --- a/c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.expected +++ b/c/cert/test/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.expected @@ -1,5 +1,6 @@ -| test.c:59:3:59:6 | call to copy | Call to 'copy' passes an aliased pointer to a restrict-qualified parameter. | -| test.c:64:3:64:6 | call to copy | Call to 'copy' passes an aliased pointer to a restrict-qualified parameter. | -| test.c:70:3:70:8 | call to strcpy | Call to 'strcpy' passes an aliased pointer to a restrict-qualified parameter. | -| test.c:77:3:77:8 | call to memcpy | Call to 'memcpy' passes an aliased pointer to a restrict-qualified parameter. | -| test.c:90:3:90:7 | call to scanf | Call to 'scanf' passes an aliased pointer to a restrict-qualified parameter. | +| test.c:59:3:59:6 | call to copy | Call to 'copy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.c:59:13:59:15 | & ... | aliased pointer | test.c:59:8:59:10 | & ... | restrict-qualified parameter | test.c:59:8:59:10 | & ... | addressof1 | test.c:59:13:59:15 | & ... | addressof2 | +| test.c:65:3:65:6 | call to copy | Call to 'copy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.c:65:15:65:19 | & ... | aliased pointer | test.c:65:8:65:12 | & ... | restrict-qualified parameter | test.c:65:8:65:12 | & ... | addressof1 | test.c:65:15:65:19 | & ... | addressof2 | +| test.c:67:3:67:6 | call to copy | Call to 'copy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.c:67:15:67:16 | px | aliased pointer | test.c:67:8:67:12 | & ... | restrict-qualified parameter | test.c:67:8:67:12 | & ... | addressof1 | test.c:63:13:63:17 | & ... | addressof2 | +| test.c:73:3:73:8 | call to strcpy | Call to 'strcpy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.c:73:15:73:21 | ... + ... | aliased pointer | test.c:73:10:73:12 | & ... | restrict-qualified parameter | test.c:73:10:73:12 | & ... | addressof1 | test.c:73:15:73:17 | & ... | addressof2 | +| test.c:80:3:80:8 | call to memcpy | Call to 'memcpy' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.c:80:15:80:21 | ... + ... | aliased pointer | test.c:80:10:80:12 | & ... | restrict-qualified parameter | test.c:80:10:80:12 | & ... | addressof1 | test.c:80:15:80:17 | & ... | addressof2 | +| test.c:93:3:93:7 | call to scanf | Call to 'scanf' passes an $@ to a $@ (pointer value derived from a pair of address-of expressions ($@, $@). | test.c:93:14:93:20 | ... + ... | aliased pointer | test.c:93:9:93:11 | & ... | restrict-qualified parameter | test.c:93:9:93:11 | & ... | addressof1 | test.c:93:14:93:16 | & ... | addressof2 | diff --git a/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected index cd389fcde3..3746991c09 100644 --- a/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected +++ b/c/cert/test/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.expected @@ -1,8 +1,9 @@ -| test.c:18:22:18:23 | i2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:18:17:18:18 | i3 | i3 | -| test.c:19:8:19:9 | g2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:5:15:5:16 | g1 | g1 | -| test.c:20:8:20:9 | i2 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:16:17:16:18 | i1 | i1 | -| test.c:27:10:27:11 | g1 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:23:19:23:20 | i5 | i5 | -| test.c:28:10:28:11 | g1 | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:22:19:22:20 | i4 | i4 | -| test.c:39:22:39:26 | & ... | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:39:17:39:18 | px | px | -| test.c:45:10:45:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:42:19:42:20 | pz | pz | -| test.c:46:10:46:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointer aliasing. | test.c:41:19:41:20 | py | py | +| test.c:18:22:18:23 | i2 | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:18:17:18:18 | i3 | i3 | test.c:18:22:18:23 | i2 | the object pointed to by i2 | +| test.c:19:8:19:9 | g2 | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:5:15:5:16 | g1 | g1 | test.c:19:8:19:9 | g2 | the object pointed to by g2 | +| test.c:20:8:20:9 | i2 | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:16:17:16:18 | i1 | i1 | test.c:20:8:20:9 | i2 | the object pointed to by i2 | +| test.c:27:10:27:11 | g1 | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:23:19:23:20 | i5 | i5 | test.c:19:8:19:9 | g2 | the same source value | +| test.c:28:10:28:11 | g1 | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:22:19:22:20 | i4 | i4 | test.c:19:8:19:9 | g2 | the same source value | +| test.c:39:22:39:26 | & ... | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:39:17:39:18 | px | px | test.c:38:28:38:30 | & ... | v1 via address-of | +| test.c:45:10:45:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:42:19:42:20 | pz | pz | test.c:43:10:43:14 | & ... | v1 via address-of | +| test.c:46:10:46:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:41:19:41:20 | py | py | test.c:43:10:43:14 | & ... | v1 via address-of | +| test.c:46:10:46:14 | & ... | Assignment to restrict-qualified pointer $@ results in pointers aliasing $@. | test.c:41:19:41:20 | py | py | test.c:45:10:45:14 | & ... | v1 via address-of | diff --git a/c/cert/test/rules/EXP43-C/test.c b/c/cert/test/rules/EXP43-C/test.c index 705c2a171a..3bf7cfa490 100644 --- a/c/cert/test/rules/EXP43-C/test.c +++ b/c/cert/test/rules/EXP43-C/test.c @@ -60,8 +60,11 @@ void test_restrict_params() { copy(&i1, &i2, 1); // COMPLIANT int x[10]; - copy(&x[0], &x[1], 1); // COMPLIANT - non overlapping - copy(&x[0], &x[1], 2); // NON_COMPLIANT - overlapping + int *px = &x[0]; + copy(&x[0], &x[1], 1); // COMPLIANT - non overlapping + copy(&x[0], &x[1], 2); // NON_COMPLIANT - overlapping + copy(&x[0], (int *)x[0], 1); // COMPLIANT - non overlapping + copy(&x[0], px, 1); // NON_COMPLIANT - overlapping } void test_strcpy() { diff --git a/c/common/src/codingstandards/c/Variable.qll b/c/common/src/codingstandards/c/Variable.qll index 40ec32aec5..4231243be2 100644 --- a/c/common/src/codingstandards/c/Variable.qll +++ b/c/common/src/codingstandards/c/Variable.qll @@ -47,6 +47,7 @@ class FlexibleArrayMemberCandidate extends MemberVariable { Variable getAddressOfExprTargetBase(AddressOfExpr expr) { result = expr.getOperand().(ValueFieldAccess).getQualifier().(VariableAccess).getTarget() or + not expr.getOperand() instanceof ValueFieldAccess and result = expr.getOperand().(VariableAccess).getTarget() or result = expr.getOperand().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() From 2f91e33089ac7ca4bd4f1150ba811334037ea10e Mon Sep 17 00:00:00 2001 From: Nikita Kraiouchkine Date: Wed, 15 Feb 2023 22:17:14 +0100 Subject: [PATCH 36/36] EXP43-C: Refactor and remove test code Remove an accidental `isBarrier` predicate left in from development --- .../DoNotPassAliasedPointerToRestrictQualifiedParam.ql | 7 ++++--- .../EXP43-C/RestrictPointerReferencesOverlappingObject.ql | 4 ---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql index 04f88897d3..32e50b2112 100644 --- a/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql +++ b/c/cert/src/rules/EXP43-C/DoNotPassAliasedPointerToRestrictQualifiedParam.ql @@ -115,7 +115,8 @@ class PointerValueToRestrictArgConfig extends DataFlow::Configuration { } from - CallToFunctionWithRestrictParameters call, CallToFunctionWithRestrictParametersArgExpr arg1, + PointerValueToRestrictArgConfig config, CallToFunctionWithRestrictParameters call, + CallToFunctionWithRestrictParametersArgExpr arg1, CallToFunctionWithRestrictParametersArgExpr arg2, int argOffset1, int argOffset2, Expr source1, Expr source2, string sourceMessage1, string sourceMessage2 where @@ -124,8 +125,8 @@ where arg2 = call.getAPtrArg(_) and // enforce ordering to remove permutations if multiple restrict-qualified args exist (not arg2 = call.getARestrictPtrArg() or arg2.getParamIndex() > arg1.getParamIndex()) and - // check if two pointers address the same object - exists(PointerValueToRestrictArgConfig config | + ( + // check if two pointers address the same object config.hasFlow(DataFlow::exprNode(source1), DataFlow::exprNode(arg1.getAChild*())) and ( // one pointer value flows to both args diff --git a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql index 6d5ba288af..88f29d86b1 100644 --- a/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql +++ b/c/cert/src/rules/EXP43-C/RestrictPointerReferencesOverlappingObject.ql @@ -51,10 +51,6 @@ class AssignedValueToRestrictPtrValueConfiguration extends DataFlow::Configurati override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof AssignmentOrInitializationToRestrictPtrValueExpr } - - override predicate isBarrierIn(DataFlow::Node node) { - isSource(node) - } } from