1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
|
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/common.h>
#include <limits.h>
#include <proof_helpers/make_common_data_structures.h>
#include <stdint.h>
#include <stdlib.h>
bool aws_byte_buf_is_bounded(const struct aws_byte_buf *const buf, const size_t max_size) {
return (buf->capacity <= max_size);
}
bool aws_byte_buf_has_allocator(const struct aws_byte_buf *const buf) {
return (buf->allocator == aws_default_allocator());
}
void ensure_byte_buf_has_allocated_buffer_member(struct aws_byte_buf *const buf) {
if (buf) {
buf->allocator = (nondet_bool()) ? NULL : aws_default_allocator();
buf->buffer = (buf->capacity == 0) ? NULL : malloc(sizeof(*(buf->buffer)) * buf->capacity);
}
}
void ensure_ring_buffer_has_allocated_members(struct aws_ring_buffer *ring_buf, const size_t size) {
ring_buf->allocator = aws_default_allocator();
/* The `aws_ring_buffer_init` function requires size > 0. */
__CPROVER_assume(size > 0 && size <= MAX_MALLOC);
ring_buf->allocation = malloc(sizeof(*(ring_buf->allocation)) * size);
size_t position_head;
size_t position_tail;
__CPROVER_assume(position_head < size);
__CPROVER_assume(position_tail < size);
if (ring_buf->allocation != NULL) {
aws_atomic_store_ptr(&ring_buf->head, (ring_buf->allocation + position_head));
aws_atomic_store_ptr(&ring_buf->tail, (ring_buf->allocation + position_tail));
ring_buf->allocation_end = ring_buf->allocation + size;
} else {
aws_atomic_store_ptr(&ring_buf->head, NULL);
aws_atomic_store_ptr(&ring_buf->tail, NULL);
ring_buf->allocation_end = NULL;
}
}
/**
* Constrain a buffer to point-into and be contained within a range [lo,hi]
*/
void ensure_byte_buf_has_allocated_buffer_member_in_range(struct aws_byte_buf *buf, uint8_t *lo, uint8_t *hi) {
assert(lo < hi);
size_t space = hi - lo;
size_t pos;
__CPROVER_assume(pos < space);
buf->buffer = lo + pos;
size_t max_capacity = hi - buf->buffer;
assert(0 < max_capacity);
__CPROVER_assume(0 < buf->capacity && buf->capacity <= max_capacity);
}
/**
* Constrain a buffer to point-into the valid elements of a ring_buffer
*/
void ensure_byte_buf_has_allocated_buffer_member_in_ring_buf(
struct aws_byte_buf *buf,
struct aws_ring_buffer *ring_buf) {
buf->allocator = (nondet_bool()) ? NULL : aws_default_allocator();
uint8_t *head = aws_atomic_load_ptr(&ring_buf->head);
uint8_t *tail = aws_atomic_load_ptr(&ring_buf->tail);
if (head < tail) { /* [....H T....] */
if (nondet_bool()) {
__CPROVER_assume(tail < ring_buf->allocation_end);
ensure_byte_buf_has_allocated_buffer_member_in_range(buf, tail, ring_buf->allocation_end);
} else {
__CPROVER_assume(ring_buf->allocation < head);
ensure_byte_buf_has_allocated_buffer_member_in_range(buf, ring_buf->allocation, head);
}
} else { /* [ T....H ] */
ensure_byte_buf_has_allocated_buffer_member_in_range(buf, tail, head);
}
}
bool aws_byte_cursor_is_bounded(const struct aws_byte_cursor *const cursor, const size_t max_size) {
return cursor->len <= max_size;
}
void ensure_byte_cursor_has_allocated_buffer_member(struct aws_byte_cursor *const cursor) {
if (cursor != NULL) {
cursor->ptr = malloc(cursor->len);
}
}
bool aws_array_list_is_bounded(
const struct aws_array_list *const list,
const size_t max_initial_item_allocation,
const size_t max_item_size) {
bool item_size_is_bounded = list->item_size <= max_item_size;
bool length_is_bounded = list->length <= max_initial_item_allocation;
return item_size_is_bounded && length_is_bounded;
}
void ensure_array_list_has_allocated_data_member(struct aws_array_list *const list) {
list->data = malloc(list->current_size);
list->alloc = nondet_bool() ? NULL : aws_default_allocator();
}
void ensure_linked_list_is_allocated(struct aws_linked_list *const list, size_t max_length) {
list->head.prev = NULL;
list->tail.next = NULL;
struct aws_linked_list_node *curr = &list->head;
for (size_t i = 0; i < max_length; i++) {
struct aws_linked_list_node *node = malloc(sizeof(struct aws_linked_list_node));
if (!node)
break;
curr->next = node;
node->prev = curr;
curr = node;
}
curr->next = &list->tail;
list->tail.prev = curr;
}
bool aws_priority_queue_is_bounded(
const struct aws_priority_queue *const queue,
const size_t max_initial_item_allocation,
const size_t max_item_size) {
bool container_is_bounded =
aws_array_list_is_bounded(&queue->container, max_initial_item_allocation, max_item_size);
/* The backpointer list holds pointers to [struct
* aws_priority_queue_node] and so the max_item_size should be
* larger than the pointer size. */
bool backpointers_list_is_bounded = aws_array_list_is_bounded(
&queue->backpointers, max_initial_item_allocation, sizeof(struct aws_priority_queue_node *));
return container_is_bounded && backpointers_list_is_bounded;
}
void ensure_priority_queue_has_allocated_members(struct aws_priority_queue *const queue) {
ensure_array_list_has_allocated_data_member(&queue->container);
ensure_array_list_has_allocated_data_member(&queue->backpointers);
queue->pred = nondet_compare;
}
void ensure_allocated_hash_table(struct aws_hash_table *map, size_t max_table_entries) {
if (map == NULL) {
return;
}
size_t num_entries;
__CPROVER_assume(num_entries <= max_table_entries);
__CPROVER_assume(aws_is_power_of_two(num_entries));
size_t required_bytes;
__CPROVER_assume(!hash_table_state_required_bytes(num_entries, &required_bytes));
struct hash_table_state *impl = malloc(required_bytes);
if (impl) {
impl->size = num_entries;
map->p_impl = impl;
} else {
map->p_impl = NULL;
}
}
void ensure_hash_table_has_valid_destroy_functions(struct aws_hash_table *map) {
map->p_impl->destroy_key_fn = nondet_bool() ? NULL : hash_proof_destroy_noop;
map->p_impl->destroy_value_fn = nondet_bool() ? NULL : hash_proof_destroy_noop;
}
bool aws_hash_table_has_an_empty_slot(const struct aws_hash_table *const map, size_t *const rval) {
return hash_table_state_has_an_empty_slot(map->p_impl, rval);
}
bool hash_table_state_has_an_empty_slot(const struct hash_table_state *const state, size_t *const rval) {
__CPROVER_assume(state->entry_count > 0);
size_t empty_slot_idx;
__CPROVER_assume(empty_slot_idx < state->size);
*rval = empty_slot_idx;
return state->slots[empty_slot_idx].hash_code == 0;
}
/**
* A correct implementation of the hash_destroy function should never have a memory
* error on valid input. There is the question of whether the destroy functions themselves
* are correctly called (i.e. only on valid input, no double free, etc.). This will be tested in
* future proofs.
*/
void hash_proof_destroy_noop(void *p) {}
struct aws_string *ensure_string_is_allocated_nondet_length() {
/* Considers any size up to the maximum possible size for the array [bytes] in aws_string */
return nondet_allocate_string_bounded_length(SIZE_MAX - 1 - sizeof(struct aws_string));
}
struct aws_string *nondet_allocate_string_bounded_length(size_t max_size) {
size_t len;
__CPROVER_assume(len < max_size);
return ensure_string_is_allocated(len);
}
struct aws_string *ensure_string_is_allocated(size_t len) {
struct aws_string *str = malloc(sizeof(struct aws_string) + len + 1);
if (str == NULL) {
return NULL;
}
/* Fields are declared const, so we need to copy them in like this */
if (str != NULL) {
*(struct aws_allocator **)(&str->allocator) = nondet_bool() ? aws_default_allocator() : NULL;
*(size_t *)(&str->len) = len;
*(uint8_t *)&str->bytes[len] = '\0';
}
return str;
}
const char *ensure_c_str_is_allocated(size_t max_size) {
size_t cap;
__CPROVER_assume(cap > 0 && cap <= max_size);
const char *str = malloc(cap);
/* Ensure that its a valid c string. Since all bytes are nondeterminstic, the actual
* string length is 0..str_cap
*/
__CPROVER_assume(IMPLIES(str != NULL, str[cap - 1] == '\0'));
return str;
}
|