mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 10:56:07 +00:00

The main goal of this work is to allow developers to express the need to place instances of a class or structure in the read-only part of the program memory. Such a placement is desirable to prevent any further modifications to the instances of a given structure, by leveraging the read-only run time protection. To achieve this, we are introducing a new attribute that can be attached to any record definition or a declaration. The compiler enforces that every instance of this type can be placed in the read-only segment of the program memory, provided the target triplet supports such a placement. If an instance of a given type bearing this attribute doesn’t satisfy such a placement, the compiler attaches an appropriate warning at suitable program locations. In other words, adding this attribute to a type requires every instance of this type to be a global const, which are placed in the read-only segments for most target triplets. However, this is *not a language feature* and it *need not* be true for *all target triplets*. The current patch emits a warning at global variable declaration sites of types bearing the attribute without const qualification and corresponding note attached to the type definition/declaration. Differential Revision: https://reviews.llvm.org/D135851
171 lines
6.0 KiB
C++
171 lines
6.0 KiB
C++
// RUN: %clang_cc1 -Wread-only-types %s -verify -fsyntax-only
|
|
// RUN: %clang_cc1 -std=c++2a -Wread-only-types %s -verify -fsyntax-only
|
|
// RUN: %clang_cc1 -std=c++17 -Wread-only-types %s -verify -fsyntax-only
|
|
|
|
struct __attribute__((enforce_read_only_placement)) A { // #A_DECL
|
|
};
|
|
|
|
A a1; // expected-warning {{object of type 'A' cannot be placed in read-only memory}}
|
|
// expected-note@#A_DECL {{type was declared read-only here}}
|
|
const A a2[10]; // no-warning
|
|
A a3[20]; // expected-warning {{object of type 'A' cannot be placed in read-only memory}}
|
|
// expected-note@#A_DECL {{type was declared read-only here}}
|
|
|
|
|
|
|
|
struct B;
|
|
struct __attribute__((enforce_read_only_placement)) B { //#B_DECL
|
|
};
|
|
|
|
B b1; // expected-warning {{object of type 'B' cannot be placed in read-only memory}}
|
|
// expected-note@#B_DECL {{type was declared read-only here}}
|
|
const B b2; // no-warning
|
|
const B b3[4]; // no-warning
|
|
B b4[5]; // expected-warning {{object of type 'B' cannot be placed in read-only memory}}
|
|
// expected-note@#B_DECL {{type was declared read-only here}}
|
|
B b5[5][5]; // expected-warning {{object of type 'B' cannot be placed in read-only memory}}
|
|
// expected-note@#B_DECL {{type was declared read-only here}}
|
|
B b10[5][5][5]; // expected-warning {{object of type 'B' cannot be placed in read-only memory}}
|
|
// expected-note@#B_DECL {{type was declared read-only here}}
|
|
|
|
void method1() {
|
|
static const B b6;
|
|
static B b7;// expected-warning {{object of type 'B' cannot be placed in read-only memory}}
|
|
// expected-note@#B_DECL {{type was declared read-only here}}
|
|
B b8; // no-warning
|
|
const B b9; // no-warning
|
|
}
|
|
|
|
struct C;
|
|
struct __attribute__((enforce_read_only_placement)) C; // expected-note {{type was declared read-only here}}
|
|
struct C { // no-note. The note should be attached to the definition/declaration bearing the attribute
|
|
};
|
|
|
|
C c1; // expected-warning {{object of type 'C' cannot be placed in read-only memory}}
|
|
|
|
// Cases to be handled by the follow-up patches.
|
|
|
|
// Attaching and checking the attribute in reverse, where the attribute is attached after the
|
|
// type definition
|
|
struct D;
|
|
struct D { //expected-note{{previous definition is here}}
|
|
};
|
|
struct __attribute__((enforce_read_only_placement)) D; // #3
|
|
// expected-warning@#3{{attribute declaration must precede definition}}
|
|
|
|
D d1; // We do not emit a warning here, as there is another warning for declaring
|
|
// a type after the definition
|
|
|
|
|
|
// Cases where the attribute must be explicitly attached to another type
|
|
// Case 1: Inheriting from a type that has the attribute
|
|
struct E : C { // FIXME: warn the user declarations of type `E`, that extends `C`, won't be
|
|
// checked for read only placement because `E` is not marked as `C` is.
|
|
};
|
|
|
|
// Case 2: Declaring a field of the type that has the attribute
|
|
struct F {
|
|
C c1; // FIXME: warn the user type `F` that wraps type `C` won't be checked for
|
|
// read only placement
|
|
};
|
|
|
|
struct BaseWithoutAttribute {
|
|
int a;
|
|
};
|
|
|
|
struct __attribute__((enforce_read_only_placement)) J : BaseWithoutAttribute { // no-warning
|
|
};
|
|
|
|
struct __attribute__((enforce_read_only_placement)) BaseWithAttribute {
|
|
int i;
|
|
};
|
|
|
|
struct __attribute__((enforce_read_only_placement)) Derived : BaseWithAttribute { // no-warning
|
|
int j;
|
|
};
|
|
|
|
struct __attribute__((enforce_read_only_placement)) WrapperToAttributeInstance { // no-warning
|
|
BaseWithAttribute b;
|
|
};
|
|
|
|
struct __attribute__((enforce_read_only_placement)) WrapperToNoAttributeInstance { // no-warning
|
|
BaseWithoutAttribute b;
|
|
};
|
|
|
|
// Cases where the const qualification doesn't ensure read-only memory placement
|
|
// of an instance.
|
|
|
|
// Case 1: The type defines/inherits mutable data members
|
|
struct __attribute__((enforce_read_only_placement)) G {
|
|
mutable int x; // FIXME: warn the user type `G` won't be placed in the read only program memory
|
|
};
|
|
|
|
struct __attribute__((enforce_read_only_placement)) H : public G { // FIXME: Warn the user type `H`
|
|
// won't be placed in the read only program memory
|
|
};
|
|
|
|
struct __attribute__((enforce_read_only_placement)) K { // FIXME : Warn the user type `K` w on't be
|
|
// placed in the read only program memory
|
|
G g;
|
|
};
|
|
|
|
|
|
// Case 2: The type has a constructor that makes its fields modifiable
|
|
struct __attribute__((enforce_read_only_placement)) L {
|
|
int b;
|
|
L(int val) { // FIXME: warn the user type `L` won't be placed in the read only program memory
|
|
b = val;
|
|
}
|
|
};
|
|
|
|
struct __attribute__((enforce_read_only_placement)) ConstInClassInitializers { // no-warning
|
|
int b = 12;
|
|
|
|
ConstInClassInitializers() = default;
|
|
};
|
|
|
|
int foo();
|
|
struct __attribute__((enforce_read_only_placement)) NonConstInClassInitializers {
|
|
int b = foo(); // FIXME: warn the user type `NonConstInClassInitializers` won't be placed
|
|
// in the read only program memory
|
|
|
|
NonConstInClassInitializers() = default;
|
|
};
|
|
|
|
#if (__cplusplus >= 202002L)
|
|
struct __attribute__((enforce_read_only_placement)) ConstevalCtor {
|
|
int b;
|
|
|
|
consteval ConstevalCtor(int B) : b(B) {} // no-warning
|
|
};
|
|
#endif
|
|
|
|
#if (__cplusplus >= 201103L)
|
|
struct __attribute__((enforce_read_only_placement)) ConstExprCtor { // no-warning
|
|
int b;
|
|
|
|
constexpr ConstExprCtor(int B) : b(B) {}
|
|
};
|
|
|
|
constexpr ConstExprCtor cec1(10); // no-warning
|
|
|
|
#endif
|
|
|
|
// Cases where an object is allocated on the heap or on the stack
|
|
C *c2 = new C; // FIXME: warn the user this instance of 'C' won't be placed in the read only program memory
|
|
|
|
void func1(C c); // FIXME: warn the user the instance of 'C' won't be placed in the read only program memory
|
|
|
|
void func2(const C c); // FIXME: warn the user the instance of 'C' won't be placed in the read
|
|
// only program memory
|
|
|
|
C func3(); // FIXME: warn the user the instance of 'C' won't be placed in the read only program memory
|
|
|
|
void func4() {
|
|
C c; // FIXME: warn the user the instance of 'C' won't be placed in the read only program memory
|
|
}
|
|
|
|
#if (__cplusplus >= 202002L)
|
|
consteval void func4(C c); // no-warning
|
|
#endif
|