mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
171 lines
3.3 KiB
C++
171 lines
3.3 KiB
C++
namespace Simple
|
|
{
|
|
int user_input()
|
|
{
|
|
return 42;
|
|
}
|
|
|
|
void sink(int x)
|
|
{
|
|
}
|
|
|
|
class Foo
|
|
{
|
|
int a_;
|
|
int b_;
|
|
|
|
public:
|
|
int a() { return a_; }
|
|
int b() { return b_; }
|
|
void setA(int a) { a_ = a; }
|
|
void setB(int b) { b_ = b; }
|
|
|
|
Foo(int a, int b) : a_(a), b_(b){};
|
|
};
|
|
|
|
void bar(Foo &f)
|
|
{
|
|
sink(f.a()); // $ ast=39:12 ast=41:12 ir=39:12 ir=41:12
|
|
sink(f.b()); // $ ast=40:12 ast=42:12 ir=40:12 ir=42:12
|
|
}
|
|
|
|
void foo()
|
|
{
|
|
Foo f(0, 0);
|
|
Foo g(0, 0);
|
|
Foo h(0, 0);
|
|
Foo i(0, 0);
|
|
|
|
f.setA(user_input());
|
|
g.setB(user_input());
|
|
h.setA(user_input());
|
|
h.setB(user_input());
|
|
|
|
// Only a() should alert
|
|
bar(f);
|
|
|
|
// Only b() should alert
|
|
bar(g);
|
|
|
|
// Both a() and b() should alert
|
|
bar(h);
|
|
|
|
// Nothing should alert
|
|
bar(i);
|
|
}
|
|
|
|
struct A
|
|
{
|
|
int i;
|
|
};
|
|
|
|
void single_field_test()
|
|
{
|
|
A a;
|
|
a.i = user_input();
|
|
A a2 = a;
|
|
sink(a2.i); //$ ast,ir
|
|
}
|
|
|
|
struct C {
|
|
int f1;
|
|
};
|
|
|
|
struct C2
|
|
{
|
|
C f2;
|
|
|
|
int getf2f1() {
|
|
return f2.f1;
|
|
}
|
|
|
|
void m() {
|
|
f2.f1 = user_input();
|
|
sink(getf2f1()); //$ ast,ir
|
|
}
|
|
};
|
|
|
|
typedef A A_typedef;
|
|
|
|
void single_field_test_typedef(A_typedef a)
|
|
{
|
|
a.i = user_input();
|
|
A_typedef a2 = a;
|
|
sink(a2.i); //$ ast,ir
|
|
}
|
|
|
|
namespace TestAdditionalCallTargets {
|
|
|
|
using TakesIntReturnsVoid = void(*)(int);
|
|
template<TakesIntReturnsVoid F>
|
|
void call_template_argument(int);
|
|
|
|
void call_sink(int x) {
|
|
sink(x); // $ ir
|
|
}
|
|
|
|
void test_additional_call_targets() {
|
|
int x = user_input();
|
|
call_template_argument<call_sink>(x);
|
|
}
|
|
|
|
}
|
|
|
|
void post_update_to_phi_input(bool b)
|
|
{
|
|
A a;
|
|
if(b) {
|
|
a.i = user_input();
|
|
}
|
|
sink(a.i); // $ ast,ir
|
|
}
|
|
|
|
void write_to_param(int* p) {
|
|
*p = user_input();
|
|
}
|
|
|
|
void alias_with_fields(bool b) {
|
|
A a;
|
|
int* q;
|
|
if(b) {
|
|
q = &a.i;
|
|
} else {
|
|
q = nullptr;
|
|
}
|
|
write_to_param(q);
|
|
sink(a.i); // $ MISSING: ast,ir
|
|
}
|
|
|
|
template<typename T>
|
|
union U_with_two_instantiations_of_different_size {
|
|
int x;
|
|
T y;
|
|
};
|
|
|
|
struct LargeStruct {
|
|
int data[64];
|
|
};
|
|
|
|
void test_union_with_two_instantiations_of_different_sizes() {
|
|
// A union's fields is partitioned into "chunks" for field-flow in order to
|
|
// improve performance (so that a write to a field of a union does not flow
|
|
// to too many reads that don't happen at runtime). The partitioning is based
|
|
// the size of the types in the union. So a write to a field of size k only
|
|
// flows to a read of size k.
|
|
// Since field-flow is based on uninstantiated types a field can have
|
|
// multiple sizes if the union is instantiated with types of
|
|
// different sizes. So to compute the partition we pick the maximum size.
|
|
// Because of this there are `Content`s corresponding to the union
|
|
// `U_with_two_instantiations_of_different_size<T>`: The one for size
|
|
// `sizeof(int)`, and the one for size `sizeof(LargeStruct)` (because
|
|
// `LargeStruct` is larger than `int`). So the write to `x` writes to the
|
|
// `Content` for size `sizeof(int)`, and the read of `y` reads from the
|
|
// `Content` for size `sizeof(LargeStruct)`.
|
|
U_with_two_instantiations_of_different_size<int> u_int;
|
|
U_with_two_instantiations_of_different_size<LargeStruct> u_very_large;
|
|
|
|
u_int.x = user_input();
|
|
sink(u_int.y); // $ MISSING: ir
|
|
}
|
|
|
|
} // namespace Simple
|