Skip to content

Commit 0459e83

Browse files
angelhofdislogical
authored andcommitted
Extend the harness for priority queue s_swap with fc assertions (#415)
* Extend the harness for priority queue s_swap with functional correctness assertions * Extend priority_queue_is_valid to check that all backpointers point to NULL or correctly allocated memory * Stub out s_swap in s_sift harnesses to make them run in reasonable time * Type and format check * Introduce the AWS_DEEP_CHECKS macro to guard costly validity checks * Add AWS_DEEP_CHECKS macro
1 parent c04e503 commit 0459e83

File tree

16 files changed

+172
-14
lines changed

16 files changed

+172
-14
lines changed

.cbmc-batch/jobs/Makefile.common

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ CBMCFLAGS += --object-bits $(CBMC_OBJECT_BITS)
7070
DEFINES += -DCBMC_OBJECT_BITS=$(CBMC_OBJECT_BITS)
7171
DEFINES += -DCBMC=1
7272

73+
################################################################
74+
# Don't execute deep checks by default
75+
AWS_DEEP_CHECKS ?= 0
76+
DEFINES += -DAWS_DEEP_CHECKS=$(AWS_DEEP_CHECKS)
77+
7378
################################################################
7479
# We always override allocator functions with our own allocator
7580
# Removing the function from the goto program helps CBMC's

.cbmc-batch/jobs/aws_priority_queue_s_sift_down/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ include ../Makefile.aws_priority_queue_sift
2727
# log(NUMBER_PRIO_QUEUE_ITEMS) times.
2828
UNWINDSET += aws_priority_queue_s_sift_down_harness.0:$(shell echo $$((1 + $(MAX_PRIORITY_QUEUE_ITEMS))))
2929
UNWINDSET += __CPROVER_file_local_priority_queue_c_s_sift_down.0:$(MAX_HEAP_HEIGHT)
30+
UNWINDSET += aws_priority_queue_backpointers_valid_deep.0:$(shell echo $$((1 + $(MAX_PRIORITY_QUEUE_ITEMS))))
3031

3132
CBMCFLAGS +=
3233

@@ -39,6 +40,9 @@ DEPENDENCIES += $(SRCDIR)/source/array_list.c
3940
DEPENDENCIES += $(SRCDIR)/source/common.c
4041
DEPENDENCIES += $(SRCDIR)/source/priority_queue.c
4142

43+
ABSTRACTIONS += $(HELPERDIR)/stubs/s_swap_override_no_op.c
44+
45+
ADDITIONAL_REMOVE_FUNCTION_BODY += --remove-function-body __CPROVER_file_local_priority_queue_c_s_swap
4246

4347
ENTRY = aws_priority_queue_s_sift_down_harness
4448
###########

.cbmc-batch/jobs/aws_priority_queue_s_sift_down/aws_priority_queue_s_sift_down_harness.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,5 @@ void aws_priority_queue_s_sift_down_harness() {
4747

4848
/* Assert the postconditions */
4949
assert(aws_priority_queue_is_valid(&queue));
50+
assert(aws_priority_queue_backpointers_valid_deep(&queue));
5051
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
jobos: ubuntu16
2-
cbmcflags: "--bounds-check;--div-by-zero-check;--float-overflow-check;--nan-check;--pointer-check;--pointer-overflow-check;--signed-overflow-check;--undefined-shift-check;--unsigned-overflow-check;--unwind;1;--unwinding-assertions;--unwindset;aws_priority_queue_s_sift_down_harness.0:6,__CPROVER_file_local_priority_queue_c_s_sift_down.0:3;--object-bits;8"
2+
cbmcflags: "--bounds-check;--div-by-zero-check;--float-overflow-check;--nan-check;--pointer-check;--pointer-overflow-check;--signed-overflow-check;--undefined-shift-check;--unsigned-overflow-check;--unwind;1;--unwinding-assertions;--unwindset;aws_priority_queue_s_sift_down_harness.0:6,__CPROVER_file_local_priority_queue_c_s_sift_down.0:3,aws_priority_queue_backpointers_valid_deep.0:6;--object-bits;8"
33
goto: aws_priority_queue_s_sift_down_harness.goto
44
expected: "SUCCESSFUL"

.cbmc-batch/jobs/aws_priority_queue_s_sift_either/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ include ../Makefile.aws_priority_queue_sift
2828
UNWINDSET += __CPROVER_file_local_priority_queue_c_s_sift_down.0:$(MAX_HEAP_HEIGHT)
2929
UNWINDSET += __CPROVER_file_local_priority_queue_c_s_sift_up.0:$(MAX_HEAP_HEIGHT)
3030
UNWINDSET += aws_priority_queue_s_sift_either_harness.0:$(shell echo $$((1 + $(MAX_PRIORITY_QUEUE_ITEMS))))
31-
31+
UNWINDSET += aws_priority_queue_backpointers_valid_deep.0:$(shell echo $$((1 + $(MAX_PRIORITY_QUEUE_ITEMS))))
3232

3333
CBMCFLAGS +=
3434

@@ -41,6 +41,9 @@ DEPENDENCIES += $(SRCDIR)/source/array_list.c
4141
DEPENDENCIES += $(SRCDIR)/source/common.c
4242
DEPENDENCIES += $(SRCDIR)/source/priority_queue.c
4343

44+
ABSTRACTIONS += $(HELPERDIR)/stubs/s_swap_override_no_op.c
45+
46+
ADDITIONAL_REMOVE_FUNCTION_BODY += --remove-function-body __CPROVER_file_local_priority_queue_c_s_swap
4447

4548
ENTRY = aws_priority_queue_s_sift_either_harness
4649
###########

.cbmc-batch/jobs/aws_priority_queue_s_sift_either/aws_priority_queue_s_sift_either_harness.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,5 @@ void aws_priority_queue_s_sift_either_harness() {
5050

5151
/* Assert the postconditions */
5252
assert(aws_priority_queue_is_valid(&queue));
53+
assert(aws_priority_queue_backpointers_valid_deep(&queue));
5354
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
jobos: ubuntu16
2-
cbmcflags: "--bounds-check;--div-by-zero-check;--float-overflow-check;--nan-check;--pointer-check;--pointer-overflow-check;--signed-overflow-check;--undefined-shift-check;--unsigned-overflow-check;--unwind;1;--unwinding-assertions;--unwindset;__CPROVER_file_local_priority_queue_c_s_sift_down.0:3,__CPROVER_file_local_priority_queue_c_s_sift_up.0:3,aws_priority_queue_s_sift_either_harness.0:6;--object-bits;8"
2+
cbmcflags: "--bounds-check;--div-by-zero-check;--float-overflow-check;--nan-check;--pointer-check;--pointer-overflow-check;--signed-overflow-check;--undefined-shift-check;--unsigned-overflow-check;--unwind;1;--unwinding-assertions;--unwindset;__CPROVER_file_local_priority_queue_c_s_sift_down.0:3,__CPROVER_file_local_priority_queue_c_s_sift_up.0:3,aws_priority_queue_s_sift_either_harness.0:6,aws_priority_queue_backpointers_valid_deep.0:6;--object-bits;8"
33
goto: aws_priority_queue_s_sift_either_harness.goto
44
expected: "SUCCESSFUL"

.cbmc-batch/jobs/aws_priority_queue_s_sift_up/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ include ../Makefile.aws_priority_queue_sift
2121
# - 300s for MAX_PRIORITY_QUEUE_ITEMS=3 items
2222
# - 450s for MAX_PRIORITY_QUEUE_ITEMS=5 items
2323

24-
2524
# Note:
2625
# In order to reach full coverage we need to unwind the harness loop
2726
# as many times as the number of queue items, and the sift down loop
2827
# log(NUMBER_PRIO_QUEUE_ITEMS) times.
2928
UNWINDSET += aws_priority_queue_s_sift_up_harness.0:$(shell echo $$((1 + $(MAX_PRIORITY_QUEUE_ITEMS))))
3029
UNWINDSET += __CPROVER_file_local_priority_queue_c_s_sift_up.0:$(MAX_HEAP_HEIGHT)
30+
UNWINDSET += aws_priority_queue_backpointers_valid_deep.0:$(shell echo $$((1 + $(MAX_PRIORITY_QUEUE_ITEMS))))
3131

3232
CBMCFLAGS +=
3333

@@ -40,6 +40,9 @@ DEPENDENCIES += $(SRCDIR)/source/array_list.c
4040
DEPENDENCIES += $(SRCDIR)/source/common.c
4141
DEPENDENCIES += $(SRCDIR)/source/priority_queue.c
4242

43+
ABSTRACTIONS += $(HELPERDIR)/stubs/s_swap_override_no_op.c
44+
45+
ADDITIONAL_REMOVE_FUNCTION_BODY += --remove-function-body __CPROVER_file_local_priority_queue_c_s_swap
4346

4447
ENTRY = aws_priority_queue_s_sift_up_harness
4548
###########

.cbmc-batch/jobs/aws_priority_queue_s_sift_up/aws_priority_queue_s_sift_up_harness.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,5 @@ void aws_priority_queue_s_sift_up_harness() {
5050

5151
/* Assert the postconditions */
5252
assert(aws_priority_queue_is_valid(&queue));
53+
assert(aws_priority_queue_backpointers_valid_deep(&queue));
5354
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
jobos: ubuntu16
2-
cbmcflags: "--bounds-check;--div-by-zero-check;--float-overflow-check;--nan-check;--pointer-check;--pointer-overflow-check;--signed-overflow-check;--undefined-shift-check;--unsigned-overflow-check;--unwind;1;--unwinding-assertions;--unwindset;aws_priority_queue_s_sift_up_harness.0:6,__CPROVER_file_local_priority_queue_c_s_sift_up.0:3;--object-bits;8"
2+
cbmcflags: "--bounds-check;--div-by-zero-check;--float-overflow-check;--nan-check;--pointer-check;--pointer-overflow-check;--signed-overflow-check;--undefined-shift-check;--unsigned-overflow-check;--unwind;1;--unwinding-assertions;--unwindset;aws_priority_queue_s_sift_up_harness.0:6,__CPROVER_file_local_priority_queue_c_s_sift_up.0:3,aws_priority_queue_backpointers_valid_deep.0:6;--object-bits;8"
33
goto: aws_priority_queue_s_sift_up_harness.goto
44
expected: "SUCCESSFUL"

.cbmc-batch/jobs/aws_priority_queue_s_swap/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ include ../Makefile.aws_array_list
2020

2121
# This is the minimum bound to reach full coverage rate
2222
UNWINDSET += __CPROVER_file_local_array_list_c_aws_array_list_mem_swap.0:$(shell echo $$(($(MAX_ITEM_SIZE) + 1)))
23+
UNWINDSET += memcpy_impl.0:$(shell echo $$(($(MAX_ITEM_SIZE) + 1)))
2324

2425
CBMCFLAGS +=
2526

2627
DEPENDENCIES += $(HELPERDIR)/source/make_common_data_structures.c
2728
DEPENDENCIES += $(HELPERDIR)/source/proof_allocators.c
2829
DEPENDENCIES += $(HELPERDIR)/source/utils.c
2930
DEPENDENCIES += $(HELPERDIR)/stubs/error.c
30-
DEPENDENCIES += $(HELPERDIR)/stubs/memcpy_override_havoc.c
31+
DEPENDENCIES += $(HELPERDIR)/stubs/memcpy_override.c
3132
DEPENDENCIES += $(SRCDIR)/source/array_list.c
3233
DEPENDENCIES += $(SRCDIR)/source/common.c
3334
DEPENDENCIES += $(SRCDIR)/source/priority_queue.c

.cbmc-batch/jobs/aws_priority_queue_s_swap/aws_priority_queue_s_swap_harness.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,36 @@ void aws_priority_queue_s_swap_harness() {
4343
can_fail_malloc(sizeof(struct aws_priority_queue_node));
4444
}
4545

46+
/* save current state of the data structure */
47+
struct aws_array_list old = queue.container;
48+
struct store_byte_from_buffer old_byte;
49+
save_byte_from_array((uint8_t *)old.data, old.current_size, &old_byte);
50+
51+
size_t item_sz = queue.container.item_size;
52+
size_t offset;
53+
__CPROVER_assume(offset < item_sz);
54+
/* save a byte of the element at index a */
55+
struct store_byte_from_buffer old_a_byte;
56+
old_a_byte.index = a * item_sz + offset;
57+
old_a_byte.byte = ((uint8_t *)queue.container.data)[old_a_byte.index];
58+
59+
/* save a byte of the element at index b */
60+
struct store_byte_from_buffer old_b_byte;
61+
old_b_byte.index = b * item_sz + offset;
62+
old_b_byte.byte = ((uint8_t *)queue.container.data)[old_b_byte.index];
63+
4664
/* Perform operation under verification */
4765
__CPROVER_file_local_priority_queue_c_s_swap(&queue, a, b);
4866

4967
/* Assert the postconditions */
5068
assert(aws_priority_queue_is_valid(&queue));
69+
70+
/* All the elements in the container except for a and b should stay unchanged */
71+
size_t ob_i = old_byte.index;
72+
if (ob_i < a * item_sz && ob_i >= (a + 1) * item_sz && ob_i < b * item_sz && ob_i >= (b + 1) * item_sz) {
73+
assert_array_list_equivalence(&queue.container, &old, &old_byte);
74+
}
75+
/* The new element at index a must be equal to the old element in index b and vice versa */
76+
assert(old_a_byte.byte == ((uint8_t *)queue.container.data)[old_b_byte.index]);
77+
assert(old_b_byte.byte == ((uint8_t *)queue.container.data)[old_a_byte.index]);
5178
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
jobos: ubuntu16
2-
cbmcflags: "--bounds-check;--div-by-zero-check;--float-overflow-check;--nan-check;--pointer-check;--pointer-overflow-check;--signed-overflow-check;--undefined-shift-check;--unsigned-overflow-check;--unwind;1;--unwinding-assertions;--unwindset;__CPROVER_file_local_array_list_c_aws_array_list_mem_swap.0:3;--object-bits;8"
2+
cbmcflags: "--bounds-check;--div-by-zero-check;--float-overflow-check;--nan-check;--pointer-check;--pointer-overflow-check;--signed-overflow-check;--undefined-shift-check;--unsigned-overflow-check;--unwind;1;--unwinding-assertions;--unwindset;__CPROVER_file_local_array_list_c_aws_array_list_mem_swap.0:3,memcpy_impl.0:3;--object-bits;8"
33
goto: aws_priority_queue_s_swap_harness.goto
44
expected: "SUCCESSFUL"
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use
5+
* this file except in compliance with the License. A copy of the License is
6+
* located at
7+
*
8+
* http://aws.amazon.com/apache2.0/
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed on an
11+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12+
* implied. See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
/**
17+
* FUNCTION: s_swap
18+
*
19+
* This function overrides s_swap and only swaps the backpointer
20+
* indexes. It is used to improve the running time of the CBMC proofs,
21+
* as this function adds heavy overhead on the proofs, especially the
22+
* ones that loop over the elements of the priority queue (such as
23+
* s_sift_up, s_sift_down, s_sift_either).
24+
*
25+
* It is safe to stub s_swap out, as long as no assertions about the
26+
* values of the elements in the priority queue are made
27+
* afterwards. Therefore, this stub cannot be used in proofs about the
28+
* order of the elements in the priority queue, or any other
29+
* functional correctness property concerning the values of the
30+
* elements in the queue.
31+
*
32+
*/
33+
34+
#include <aws/common/priority_queue.h>
35+
36+
void __CPROVER_file_local_priority_queue_c_s_swap(struct aws_priority_queue *queue, size_t a, size_t b) {
37+
assert(aws_priority_queue_is_valid(queue));
38+
assert(a < queue->container.length);
39+
assert(b < queue->container.length);
40+
assert(aws_priority_queue_backpointer_index_valid(queue, a));
41+
assert(aws_priority_queue_backpointer_index_valid(queue, b));
42+
43+
if (queue->backpointers.data) {
44+
assert(queue->backpointers.length > a);
45+
assert(queue->backpointers.length > b);
46+
}
47+
}

include/aws/common/priority_queue.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,26 @@ void aws_priority_queue_init_static(
9191
size_t item_size,
9292
aws_priority_queue_compare_fn *pred);
9393

94+
/**
95+
* Checks that the backpointer at a specific index of the queue is
96+
* NULL or points to a correctly allocated aws_priority_queue_node.
97+
*/
98+
bool aws_priority_queue_backpointer_index_valid(const struct aws_priority_queue *const queue, size_t index);
99+
100+
/**
101+
* Checks that the backpointers of the priority queue are either NULL
102+
* or correctly allocated to point at aws_priority_queue_nodes. This
103+
* check is O(n), as it accesses every backpointer in a loop, and thus
104+
* shouldn't be used carelessly.
105+
*/
106+
bool aws_priority_queue_backpointers_valid_deep(const struct aws_priority_queue *const queue);
107+
108+
/**
109+
* Checks that the backpointers of the priority queue satisfy validity
110+
* constraints.
111+
*/
112+
bool aws_priority_queue_backpointers_valid(const struct aws_priority_queue *const queue);
113+
94114
/**
95115
* Set of properties of a valid aws_priority_queue.
96116
*/

source/priority_queue.c

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ static void s_swap(struct aws_priority_queue *queue, size_t a, size_t b) {
2525
AWS_PRECONDITION(aws_priority_queue_is_valid(queue));
2626
AWS_PRECONDITION(a < queue->container.length);
2727
AWS_PRECONDITION(b < queue->container.length);
28+
AWS_PRECONDITION(aws_priority_queue_backpointer_index_valid(queue, a));
29+
AWS_PRECONDITION(aws_priority_queue_backpointer_index_valid(queue, b));
2830

2931
aws_array_list_swap(&queue->container, a, b);
3032

@@ -49,6 +51,8 @@ static void s_swap(struct aws_priority_queue *queue, size_t a, size_t b) {
4951
}
5052
}
5153
AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
54+
AWS_POSTCONDITION(aws_priority_queue_backpointer_index_valid(queue, a));
55+
AWS_POSTCONDITION(aws_priority_queue_backpointer_index_valid(queue, b));
5256
}
5357

5458
/* Precondition: with the exception of the given root element, the container must be
@@ -180,15 +184,35 @@ void aws_priority_queue_init_static(
180184
AWS_POSTCONDITION(aws_priority_queue_is_valid(queue));
181185
}
182186

183-
bool aws_priority_queue_is_valid(const struct aws_priority_queue *const queue) {
184-
/* Pointer validity checks */
187+
bool aws_priority_queue_backpointer_index_valid(const struct aws_priority_queue *const queue, size_t index) {
188+
if (queue->backpointers.data == NULL) {
189+
return true;
190+
}
191+
if (index < queue->backpointers.length) {
192+
struct aws_priority_queue_node *node = ((struct aws_priority_queue_node **)queue->backpointers.data)[index];
193+
return (node == NULL) || AWS_MEM_IS_WRITABLE(node, sizeof(struct aws_priority_queue_node));
194+
}
195+
return false;
196+
}
197+
198+
bool aws_priority_queue_backpointers_valid_deep(const struct aws_priority_queue *const queue) {
185199
if (!queue) {
186200
return false;
187201
}
188-
bool pred_is_valid = (queue->pred != NULL);
202+
for (size_t i = 0; i < queue->backpointers.length; i++) {
203+
if (!aws_priority_queue_backpointer_index_valid(queue, i)) {
204+
return false;
205+
}
206+
}
207+
return true;
208+
}
189209

190-
/* Internal container validity checks */
191-
bool container_is_valid = aws_array_list_is_valid(&queue->container);
210+
bool aws_priority_queue_backpointers_valid(const struct aws_priority_queue *const queue) {
211+
if (!queue) {
212+
return false;
213+
}
214+
215+
/* Internal container validity */
192216
bool backpointer_list_is_valid = aws_array_list_is_valid(&queue->backpointers);
193217

194218
/* Backpointer struct should either be zero or should be
@@ -198,12 +222,33 @@ bool aws_priority_queue_is_valid(const struct aws_priority_queue *const queue) {
198222
bool backpointer_list_item_size = queue->backpointers.item_size == sizeof(struct aws_priority_queue_node *);
199223
bool lists_equal_lengths = queue->backpointers.length == queue->container.length;
200224
bool backpointers_non_zero_current_size = queue->backpointers.current_size > 0;
225+
226+
/* This check must be guarded, as it is not efficient, neither
227+
* when running tests nor CBMC */
228+
#if (AWS_DEEP_CHECKS == 1)
229+
bool backpointers_valid_deep = aws_priority_queue_backpointers_valid_deep(queue);
230+
#else
231+
bool backpointers_valid_deep = true;
232+
#endif
201233
bool backpointers_zero =
202234
(queue->backpointers.current_size == 0 && queue->backpointers.length == 0 && queue->backpointers.data == NULL);
203235
bool backpointer_struct_is_valid =
204-
backpointers_zero || (backpointer_list_item_size && lists_equal_lengths && backpointers_non_zero_current_size);
236+
backpointers_zero || (backpointer_list_item_size && lists_equal_lengths && backpointers_non_zero_current_size &&
237+
backpointers_valid_deep);
238+
239+
return backpointer_list_is_valid && backpointer_struct_is_valid;
240+
}
241+
242+
bool aws_priority_queue_is_valid(const struct aws_priority_queue *const queue) {
243+
/* Pointer validity checks */
244+
if (!queue) {
245+
return false;
246+
}
247+
bool pred_is_valid = (queue->pred != NULL);
248+
bool container_is_valid = aws_array_list_is_valid(&queue->container);
205249

206-
return pred_is_valid && container_is_valid && backpointer_list_is_valid && backpointer_struct_is_valid;
250+
bool backpointers_valid = aws_priority_queue_backpointers_valid(queue);
251+
return pred_is_valid && container_is_valid && backpointers_valid;
207252
}
208253

209254
void aws_priority_queue_clean_up(struct aws_priority_queue *queue) {

0 commit comments

Comments
 (0)