Skip to content

Commit 7b00367

Browse files
committed
Merge branch 'main' into mathiasvp/reverse-read-take-3
2 parents 258d041 + 2c09f9a commit 7b00367

File tree

120 files changed

+3531
-2007
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

120 files changed

+3531
-2007
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ If you have an idea for a query that you would like to share with other CodeQL u
3838

3939
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode/procedures/about-codeql-for-vscode.html).
4040

41+
If you prefer, you can use this [pre-commit hook](misc/scripts/pre-commit) that automatically checks whether your files are correctly formatted. See the [pre-commit hook installation guide](docs/install-pre-commit-hook.md) for instructions on how to install the hook.
42+
4143
4. **Compilation**
4244

4345
- Compilation of the query and any associated libraries and tests must be resilient to future development of the [supported](docs/supported-queries.md) libraries. This means that the functionality cannot use internal libraries, cannot depend on the output of `getAQlClass`, and cannot make use of regexp matching on `toString`.

cpp/ql/src/semmle/code/cpp/Function.qll

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -391,20 +391,30 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
391391
/** Holds if this function has a `noexcept` exception specification. */
392392
predicate isNoExcept() { getADeclarationEntry().isNoExcept() }
393393

394-
/** Gets a function that overloads this one. */
394+
/**
395+
* Gets a function that overloads this one.
396+
*
397+
* Note: if _overrides_ are wanted rather than _overloads_ then
398+
* `MemberFunction::getAnOverridingFunction` should be used instead.
399+
*/
395400
Function getAnOverload() {
396-
result.getName() = getName() and
397-
result.getNamespace() = getNamespace() and
398-
result != this and
399-
// If this function is declared in a class, only consider other
400-
// functions from the same class. Conversely, if this function is not
401-
// declared in a class, only consider other functions not declared in a
402-
// class.
403401
(
404-
if exists(getDeclaringType())
405-
then result.getDeclaringType() = getDeclaringType()
406-
else not exists(result.getDeclaringType())
402+
// If this function is declared in a class, only consider other
403+
// functions from the same class.
404+
exists(string name, Class declaringType |
405+
candGetAnOverloadMember(name, declaringType, this) and
406+
candGetAnOverloadMember(name, declaringType, result)
407+
)
408+
or
409+
// Conversely, if this function is not
410+
// declared in a class, only consider other functions not declared in a
411+
// class.
412+
exists(string name, Namespace namespace |
413+
candGetAnOverloadNonMember(name, namespace, this) and
414+
candGetAnOverloadNonMember(name, namespace, result)
415+
)
407416
) and
417+
result != this and
408418
// Instantiations and specializations don't participate in overload
409419
// resolution.
410420
not (
@@ -462,6 +472,19 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
462472
override AccessHolder getEnclosingAccessHolder() { result = this.getDeclaringType() }
463473
}
464474

475+
pragma[noinline]
476+
private predicate candGetAnOverloadMember(string name, Class declaringType, Function f) {
477+
f.getName() = name and
478+
f.getDeclaringType() = declaringType
479+
}
480+
481+
pragma[noinline]
482+
private predicate candGetAnOverloadNonMember(string name, Namespace namespace, Function f) {
483+
f.getName() = name and
484+
f.getNamespace() = namespace and
485+
not exists(f.getDeclaringType())
486+
}
487+
465488
/**
466489
* A particular declaration or definition of a C/C++ function. For example the
467490
* declaration and definition of `MyFunction` in the following code are each a
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
int user_input();
2+
void sink(int);
3+
4+
struct A {
5+
int* p;
6+
int x;
7+
};
8+
9+
void pointer_without_allocation(const A& ra) {
10+
*ra.p = user_input();
11+
sink(*ra.p); // $ MISSING: ast,ir
12+
}
13+
14+
void argument_source(void*);
15+
void sink(void*);
16+
17+
void pointer_without_allocation_2() {
18+
char *raw;
19+
argument_source(raw);
20+
sink(raw); // $ ast MISSING: ir
21+
}
22+
23+
A* makeA() {
24+
return new A;
25+
}
26+
27+
void no_InitializeDynamicAllocation_instruction() {
28+
A* pa = makeA();
29+
pa->x = user_input();
30+
sink(pa->x); // $ ast MISSING: ir
31+
}
32+
33+
void fresh_or_arg(A* arg, bool unknown) {
34+
A* pa;
35+
pa = unknown ? arg : new A;
36+
pa->x = user_input();
37+
sink(pa->x); // $ ast MISSING: ir
38+
}
39+
40+
struct LinkedList {
41+
LinkedList* next;
42+
int y;
43+
44+
LinkedList() = default;
45+
LinkedList(LinkedList* next) : next(next) {}
46+
};
47+
48+
// Note: This example also suffers from #113: there is no ChiInstruction that merges the result of the
49+
// InitializeDynamicAllocation instruction into {AllAliasedMemory}. But even when that's fixed there's
50+
// still no dataflow because `ll->next->y = user_input()` writes to {AllAliasedMemory}.
51+
void too_many_indirections() {
52+
LinkedList* ll = new LinkedList;
53+
ll->next = new LinkedList;
54+
ll->next->y = user_input();
55+
sink(ll->next->y); // $ ast MISSING: ir
56+
}
57+
58+
void too_many_indirections_2(LinkedList* next) {
59+
LinkedList* ll = new LinkedList(next);
60+
ll->next->y = user_input();
61+
sink(ll->next->y); // $ ast MISSING: ir
62+
}

cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ postWithInFlow
125125
| by_reference.cpp:127:30:127:38 | inner_ptr [inner post update] | PostUpdateNode should not be the target of local flow. |
126126
| complex.cpp:11:22:11:23 | a_ [post update] | PostUpdateNode should not be the target of local flow. |
127127
| complex.cpp:12:22:12:23 | b_ [post update] | PostUpdateNode should not be the target of local flow. |
128+
| conflated.cpp:10:3:10:7 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
129+
| conflated.cpp:10:7:10:7 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
130+
| conflated.cpp:29:7:29:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
131+
| conflated.cpp:36:7:36:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
132+
| conflated.cpp:53:7:53:10 | next [post update] | PostUpdateNode should not be the target of local flow. |
133+
| conflated.cpp:54:13:54:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
134+
| conflated.cpp:60:13:60:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
128135
| constructors.cpp:20:24:20:25 | a_ [post update] | PostUpdateNode should not be the target of local flow. |
129136
| constructors.cpp:21:24:21:25 | b_ [post update] | PostUpdateNode should not be the target of local flow. |
130137
| qualifiers.cpp:9:36:9:36 | a [post update] | PostUpdateNode should not be the target of local flow. |

cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
uniqueEnclosingCallable
22
uniqueType
33
uniqueNodeLocation
4+
| E.cpp:15:31:15:33 | buf | Node should have one location but has 2. |
5+
| aliasing.cpp:2:11:2:13 | (unnamed parameter 0) | Node should have one location but has 2. |
6+
| conflated.cpp:2:11:2:13 | (unnamed parameter 0) | Node should have one location but has 2. |
7+
| conflated.cpp:14:22:14:25 | buf | Node should have one location but has 2. |
48
| file://:0:0:0:0 | (unnamed parameter 0) | Node should have one location but has 0. |
59
| file://:0:0:0:0 | (unnamed parameter 0) | Node should have one location but has 0. |
610
| file://:0:0:0:0 | (unnamed parameter 0) | Node should have one location but has 0. |
@@ -66,6 +70,7 @@ postHasUniquePre
6670
| complex.cpp:49:9:49:10 | Outer output argument | PostUpdateNode should have one pre-update node but has 0. |
6771
| complex.cpp:50:9:50:10 | Outer output argument | PostUpdateNode should have one pre-update node but has 0. |
6872
| complex.cpp:51:9:51:10 | Outer output argument | PostUpdateNode should have one pre-update node but has 0. |
73+
| conflated.cpp:59:20:59:39 | LinkedList output argument | PostUpdateNode should have one pre-update node but has 0. |
6974
| constructors.cpp:34:11:34:26 | Foo output argument | PostUpdateNode should have one pre-update node but has 0. |
7075
| constructors.cpp:35:11:35:26 | Foo output argument | PostUpdateNode should have one pre-update node but has 0. |
7176
| constructors.cpp:36:11:36:37 | Foo output argument | PostUpdateNode should have one pre-update node but has 0. |

cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,22 @@
256256
| complex.cpp:62:7:62:8 | b2 | AST only |
257257
| complex.cpp:65:7:65:8 | b3 | AST only |
258258
| complex.cpp:68:7:68:8 | b4 | AST only |
259+
| conflated.cpp:10:3:10:7 | * ... | AST only |
260+
| conflated.cpp:11:12:11:12 | ra | IR only |
261+
| conflated.cpp:19:19:19:21 | raw | AST only |
262+
| conflated.cpp:20:8:20:10 | raw | AST only |
263+
| conflated.cpp:29:7:29:7 | x | AST only |
264+
| conflated.cpp:30:12:30:12 | pa | IR only |
265+
| conflated.cpp:36:7:36:7 | x | AST only |
266+
| conflated.cpp:37:12:37:12 | pa | IR only |
267+
| conflated.cpp:53:7:53:10 | next | AST only |
268+
| conflated.cpp:54:13:54:13 | y | AST only |
269+
| conflated.cpp:55:12:55:15 | ll | IR only |
270+
| conflated.cpp:55:18:55:18 | next | IR only |
271+
| conflated.cpp:59:35:59:38 | next | AST only |
272+
| conflated.cpp:60:13:60:13 | y | AST only |
273+
| conflated.cpp:61:12:61:15 | ll | IR only |
274+
| conflated.cpp:61:18:61:18 | next | IR only |
259275
| constructors.cpp:18:22:18:23 | this | IR only |
260276
| constructors.cpp:19:22:19:23 | this | IR only |
261277
| constructors.cpp:20:24:20:25 | a_ | AST only |

cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,21 @@
234234
| complex.cpp:55:6:55:10 | inner |
235235
| complex.cpp:56:3:56:4 | b3 |
236236
| complex.cpp:56:6:56:10 | inner |
237+
| conflated.cpp:10:4:10:5 | ra |
238+
| conflated.cpp:11:9:11:10 | ra |
239+
| conflated.cpp:29:3:29:4 | pa |
240+
| conflated.cpp:30:8:30:9 | pa |
241+
| conflated.cpp:36:3:36:4 | pa |
242+
| conflated.cpp:37:8:37:9 | pa |
243+
| conflated.cpp:53:3:53:4 | ll |
244+
| conflated.cpp:54:3:54:4 | ll |
245+
| conflated.cpp:54:7:54:10 | next |
246+
| conflated.cpp:55:8:55:9 | ll |
247+
| conflated.cpp:55:12:55:15 | next |
248+
| conflated.cpp:60:3:60:4 | ll |
249+
| conflated.cpp:60:7:60:10 | next |
250+
| conflated.cpp:61:8:61:9 | ll |
251+
| conflated.cpp:61:12:61:15 | next |
237252
| constructors.cpp:18:22:18:23 | this |
238253
| constructors.cpp:19:22:19:23 | this |
239254
| constructors.cpp:20:24:20:25 | this |

cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,23 @@
378378
| complex.cpp:62:7:62:8 | b2 |
379379
| complex.cpp:65:7:65:8 | b3 |
380380
| complex.cpp:68:7:68:8 | b4 |
381+
| conflated.cpp:10:3:10:7 | * ... |
382+
| conflated.cpp:10:4:10:5 | ra |
383+
| conflated.cpp:19:19:19:21 | raw |
384+
| conflated.cpp:20:8:20:10 | raw |
385+
| conflated.cpp:29:3:29:4 | pa |
386+
| conflated.cpp:29:7:29:7 | x |
387+
| conflated.cpp:36:3:36:4 | pa |
388+
| conflated.cpp:36:7:36:7 | x |
389+
| conflated.cpp:53:3:53:4 | ll |
390+
| conflated.cpp:53:7:53:10 | next |
391+
| conflated.cpp:54:3:54:4 | ll |
392+
| conflated.cpp:54:7:54:10 | next |
393+
| conflated.cpp:54:13:54:13 | y |
394+
| conflated.cpp:59:35:59:38 | next |
395+
| conflated.cpp:60:3:60:4 | ll |
396+
| conflated.cpp:60:7:60:10 | next |
397+
| conflated.cpp:60:13:60:13 | y |
381398
| constructors.cpp:20:24:20:25 | a_ |
382399
| constructors.cpp:20:24:20:25 | this |
383400
| constructors.cpp:21:24:21:25 | b_ |

cpp/ql/test/library-tests/dataflow/fields/path-flow.expected

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,27 @@ edges
351351
| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] |
352352
| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] |
353353
| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] |
354+
| conflated.cpp:19:19:19:21 | ref arg raw | conflated.cpp:20:8:20:10 | raw |
355+
| conflated.cpp:29:3:29:4 | pa [post update] [x] | conflated.cpp:30:8:30:9 | pa [x] |
356+
| conflated.cpp:29:3:29:22 | ... = ... | conflated.cpp:29:3:29:4 | pa [post update] [x] |
357+
| conflated.cpp:29:11:29:20 | call to user_input | conflated.cpp:29:3:29:22 | ... = ... |
358+
| conflated.cpp:30:8:30:9 | pa [x] | conflated.cpp:30:12:30:12 | x |
359+
| conflated.cpp:36:3:36:4 | pa [post update] [x] | conflated.cpp:37:8:37:9 | pa [x] |
360+
| conflated.cpp:36:3:36:22 | ... = ... | conflated.cpp:36:3:36:4 | pa [post update] [x] |
361+
| conflated.cpp:36:11:36:20 | call to user_input | conflated.cpp:36:3:36:22 | ... = ... |
362+
| conflated.cpp:37:8:37:9 | pa [x] | conflated.cpp:37:12:37:12 | x |
363+
| conflated.cpp:54:3:54:4 | ll [post update] [next, y] | conflated.cpp:55:8:55:9 | ll [next, y] |
364+
| conflated.cpp:54:3:54:28 | ... = ... | conflated.cpp:54:7:54:10 | next [post update] [y] |
365+
| conflated.cpp:54:7:54:10 | next [post update] [y] | conflated.cpp:54:3:54:4 | ll [post update] [next, y] |
366+
| conflated.cpp:54:17:54:26 | call to user_input | conflated.cpp:54:3:54:28 | ... = ... |
367+
| conflated.cpp:55:8:55:9 | ll [next, y] | conflated.cpp:55:12:55:15 | next [y] |
368+
| conflated.cpp:55:12:55:15 | next [y] | conflated.cpp:55:18:55:18 | y |
369+
| conflated.cpp:60:3:60:4 | ll [post update] [next, y] | conflated.cpp:61:8:61:9 | ll [next, y] |
370+
| conflated.cpp:60:3:60:28 | ... = ... | conflated.cpp:60:7:60:10 | next [post update] [y] |
371+
| conflated.cpp:60:7:60:10 | next [post update] [y] | conflated.cpp:60:3:60:4 | ll [post update] [next, y] |
372+
| conflated.cpp:60:17:60:26 | call to user_input | conflated.cpp:60:3:60:28 | ... = ... |
373+
| conflated.cpp:61:8:61:9 | ll [next, y] | conflated.cpp:61:12:61:15 | next [y] |
374+
| conflated.cpp:61:12:61:15 | next [y] | conflated.cpp:61:18:61:18 | y |
354375
| constructors.cpp:26:15:26:15 | f [a_] | constructors.cpp:28:10:28:10 | f [a_] |
355376
| constructors.cpp:26:15:26:15 | f [b_] | constructors.cpp:29:10:29:10 | f [b_] |
356377
| constructors.cpp:28:10:28:10 | f [a_] | constructors.cpp:28:12:28:12 | call to a |
@@ -864,6 +885,32 @@ nodes
864885
| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | semmle.label | b2 [inner, f, b_] |
865886
| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | semmle.label | b3 [inner, f, a_] |
866887
| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | semmle.label | b3 [inner, f, b_] |
888+
| conflated.cpp:19:19:19:21 | ref arg raw | semmle.label | ref arg raw |
889+
| conflated.cpp:20:8:20:10 | raw | semmle.label | raw |
890+
| conflated.cpp:29:3:29:4 | pa [post update] [x] | semmle.label | pa [post update] [x] |
891+
| conflated.cpp:29:3:29:22 | ... = ... | semmle.label | ... = ... |
892+
| conflated.cpp:29:11:29:20 | call to user_input | semmle.label | call to user_input |
893+
| conflated.cpp:30:8:30:9 | pa [x] | semmle.label | pa [x] |
894+
| conflated.cpp:30:12:30:12 | x | semmle.label | x |
895+
| conflated.cpp:36:3:36:4 | pa [post update] [x] | semmle.label | pa [post update] [x] |
896+
| conflated.cpp:36:3:36:22 | ... = ... | semmle.label | ... = ... |
897+
| conflated.cpp:36:11:36:20 | call to user_input | semmle.label | call to user_input |
898+
| conflated.cpp:37:8:37:9 | pa [x] | semmle.label | pa [x] |
899+
| conflated.cpp:37:12:37:12 | x | semmle.label | x |
900+
| conflated.cpp:54:3:54:4 | ll [post update] [next, y] | semmle.label | ll [post update] [next, y] |
901+
| conflated.cpp:54:3:54:28 | ... = ... | semmle.label | ... = ... |
902+
| conflated.cpp:54:7:54:10 | next [post update] [y] | semmle.label | next [post update] [y] |
903+
| conflated.cpp:54:17:54:26 | call to user_input | semmle.label | call to user_input |
904+
| conflated.cpp:55:8:55:9 | ll [next, y] | semmle.label | ll [next, y] |
905+
| conflated.cpp:55:12:55:15 | next [y] | semmle.label | next [y] |
906+
| conflated.cpp:55:18:55:18 | y | semmle.label | y |
907+
| conflated.cpp:60:3:60:4 | ll [post update] [next, y] | semmle.label | ll [post update] [next, y] |
908+
| conflated.cpp:60:3:60:28 | ... = ... | semmle.label | ... = ... |
909+
| conflated.cpp:60:7:60:10 | next [post update] [y] | semmle.label | next [post update] [y] |
910+
| conflated.cpp:60:17:60:26 | call to user_input | semmle.label | call to user_input |
911+
| conflated.cpp:61:8:61:9 | ll [next, y] | semmle.label | ll [next, y] |
912+
| conflated.cpp:61:12:61:15 | next [y] | semmle.label | next [y] |
913+
| conflated.cpp:61:18:61:18 | y | semmle.label | y |
867914
| constructors.cpp:26:15:26:15 | f [a_] | semmle.label | f [a_] |
868915
| constructors.cpp:26:15:26:15 | f [b_] | semmle.label | f [b_] |
869916
| constructors.cpp:28:10:28:10 | f [a_] | semmle.label | f [a_] |
@@ -1074,6 +1121,11 @@ nodes
10741121
| complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input |
10751122
| complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input |
10761123
| complex.cpp:43:18:43:18 | call to b | complex.cpp:56:19:56:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:56:19:56:28 | call to user_input | call to user_input |
1124+
| conflated.cpp:20:8:20:10 | raw | conflated.cpp:19:19:19:21 | ref arg raw | conflated.cpp:20:8:20:10 | raw | raw flows from $@ | conflated.cpp:19:19:19:21 | ref arg raw | ref arg raw |
1125+
| conflated.cpp:30:12:30:12 | x | conflated.cpp:29:11:29:20 | call to user_input | conflated.cpp:30:12:30:12 | x | x flows from $@ | conflated.cpp:29:11:29:20 | call to user_input | call to user_input |
1126+
| conflated.cpp:37:12:37:12 | x | conflated.cpp:36:11:36:20 | call to user_input | conflated.cpp:37:12:37:12 | x | x flows from $@ | conflated.cpp:36:11:36:20 | call to user_input | call to user_input |
1127+
| conflated.cpp:55:18:55:18 | y | conflated.cpp:54:17:54:26 | call to user_input | conflated.cpp:55:18:55:18 | y | y flows from $@ | conflated.cpp:54:17:54:26 | call to user_input | call to user_input |
1128+
| conflated.cpp:61:18:61:18 | y | conflated.cpp:60:17:60:26 | call to user_input | conflated.cpp:61:18:61:18 | y | y flows from $@ | conflated.cpp:60:17:60:26 | call to user_input | call to user_input |
10771129
| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input |
10781130
| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input |
10791131
| constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* The queries `FormatInvalid.ql`, `FormatMissingArgument.ql`, and `FormatUnusedArgument.ql` have been merged into a single `FormatInvalid.ql` query.

0 commit comments

Comments
 (0)