Skip to content

Commit cbe96ad

Browse files
authored
Faster CBMC stubs for memset and memcpy (#300)
* new memset and memcpy functions that are often faster for CBMC to analyze * PR comments addressed
1 parent b53085e commit cbe96ad

File tree

12 files changed

+439
-0
lines changed

12 files changed

+439
-0
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You may not use
4+
# this file except in compliance with the License. A copy of the License is
5+
# located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is distributed on an
10+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
11+
# implied. See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
###########
15+
# Max needs to be big enough to have multiple loop unrollings to have full coverage
16+
# 160 is well larger than that, and still completes quite fast: ~ 40s
17+
MAX = 160
18+
DEFINES += -DMAX=$(MAX)
19+
20+
UNWINDSET += memcpy_impl.0:$(shell echo $$(($(MAX) + 1)))
21+
UNWINDSET += memcpy_using_uint64_impl.0:$(shell echo $$(( $$(( $(MAX) / 8 )) + 1)))
22+
23+
CBMCFLAGS +=
24+
25+
DEPENDENCIES += $(HELPERDIR)/source/utils.c
26+
DEPENDENCIES += $(HELPERDIR)/stubs/memcpy_override.c
27+
DEPENDENCIES += $(HELPERDIR)/stubs/memcpy_using_uint64.c
28+
29+
ENTRY = memcpy_using_uint64_harness
30+
31+
###########
32+
33+
include ../Makefile.common
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
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;memcpy_impl.0:161,memcpy_using_uint64_impl.0:21"
3+
goto: memcpy_using_uint64_harness.goto
4+
expected: "SUCCESSFUL"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
#include <proof_helpers/utils.h>
17+
#include <stddef.h>
18+
19+
void *memcpy_impl(void *dst, const void *src, size_t n);
20+
void *memcpy_using_uint64_impl(void *dst, const void *src, size_t n);
21+
22+
/*
23+
* Check that the optimized version of memcpy is memory safe
24+
* And that it matches the naive version
25+
*/
26+
void memcpy_using_uint64_harness() {
27+
char s[MAX];
28+
char d1[MAX];
29+
char d2[MAX];
30+
31+
unsigned size;
32+
__CPROVER_assume(size < MAX);
33+
memcpy_impl(d1, s, size);
34+
memcpy_using_uint64_impl(d2, s, size);
35+
assert_bytes_match(d1, d2, size);
36+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You may not use
4+
# this file except in compliance with the License. A copy of the License is
5+
# located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is distributed on an
10+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
11+
# implied. See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
###########
15+
# Max needs to be big enough to have multiple loop unrollings to have full coverage
16+
# 160 is well larger than that, and still completes quite fast: ~ 40s
17+
MAX = 160
18+
DEFINES += -DMAX=$(MAX)
19+
20+
UNWINDSET += memset_impl.0:$(shell echo $$(($(MAX) + 1)))
21+
UNWINDSET += memset_override_0_impl.0:$(shell echo $$(( $$(( $(MAX) / 8 )) + 1)))
22+
23+
CBMCFLAGS +=
24+
25+
DEPENDENCIES += $(HELPERDIR)/source/utils.c
26+
DEPENDENCIES += $(HELPERDIR)/stubs/memset_override.c
27+
DEPENDENCIES += $(HELPERDIR)/stubs/memset_override_0.c
28+
29+
ENTRY = memset_override_0_harness
30+
31+
###########
32+
33+
include ../Makefile.common
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
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;memset_impl.0:161,memset_override_0_impl.0:21"
3+
goto: memset_override_0_harness.goto
4+
expected: "SUCCESSFUL"
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
#include <proof_helpers/utils.h>
17+
#include <stddef.h>
18+
19+
void *memset_impl(void *dst, int c, size_t n);
20+
void *memset_override_0_impl(void *dst, int c, size_t n);
21+
22+
/*
23+
* Check that the optimized version of memset is memory safe
24+
* And that it matches the naive version
25+
*/
26+
void memset_override_0_harness() {
27+
28+
short d1[MAX];
29+
short d2[MAX];
30+
int c;
31+
__CPROVER_assume(c == 0); // asserted in the implementation
32+
33+
unsigned size;
34+
__CPROVER_assume((size & 0x7) == 0); // asserted in the implementation
35+
__CPROVER_assume(size < MAX);
36+
memset_impl(d1, c, size);
37+
memset_override_0_impl(d2, c, size);
38+
assert_bytes_match(d1, d2, size);
39+
assert_all_bytes_are(d2, c, size);
40+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You may not use
4+
# this file except in compliance with the License. A copy of the License is
5+
# located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is distributed on an
10+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
11+
# implied. See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
###########
15+
# Max needs to be big enough to have multiple loop unrollings to have full coverage
16+
# 160 is well larger than that, and still completes quite fast: ~ 40s
17+
MAX = 160
18+
DEFINES += -DMAX=$(MAX)
19+
UNWINDSET += memset_impl.0:$(shell echo $$(($(MAX) + 1)))
20+
UNWINDSET += memset_using_uint64_impl.0:$(shell echo $$(( $$(( $(MAX) / 8 )) + 1)))
21+
22+
CBMCFLAGS +=
23+
24+
DEPENDENCIES += $(HELPERDIR)/source/utils.c
25+
DEPENDENCIES += $(HELPERDIR)/stubs/memset_override.c
26+
DEPENDENCIES += $(HELPERDIR)/stubs/memset_using_uint64.c
27+
28+
ENTRY = memset_using_uint64_harness
29+
30+
###########
31+
32+
include ../Makefile.common
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
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;memset_impl.0:161,memset_using_uint64_impl.0:21"
3+
goto: memset_using_uint64_harness.goto
4+
expected: "SUCCESSFUL"
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
#include <proof_helpers/utils.h>
17+
#include <stddef.h>
18+
19+
void *memset_impl(void *dst, int c, size_t n);
20+
void *memset_using_uint64_impl(void *dst, int c, size_t n);
21+
22+
/*
23+
* Check that the optimized version of memset is memory safe
24+
* And that it matches the naive version
25+
*/
26+
void memset_using_uint64_harness() {
27+
28+
short d1[MAX];
29+
short d2[MAX];
30+
int c;
31+
unsigned size;
32+
__CPROVER_assume(size < MAX);
33+
memset_impl(d1, c, size);
34+
memset_using_uint64_impl(d2, c, size);
35+
assert_bytes_match(d1, d2, size);
36+
assert_all_bytes_are(d2, c, size);
37+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2018 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: memcpy
18+
*
19+
* This function overrides the original implementation of the memcpy function
20+
* from string.h. It copies the values of n bytes from the *src to the *dst.
21+
* It also checks if the size of the arrays pointed to by both the *dst and
22+
* *src parameters are at least n bytes and if they overlap.
23+
*
24+
* This takes advantage of the fact that 64bit operations require fewer array updates,
25+
* which can make this version faster than the naive unrolling when used in CBMC.
26+
* Benchmark your particular proof to know for sure.
27+
*/
28+
29+
#include <stddef.h>
30+
#include <stdint.h>
31+
32+
void *memcpy_using_uint64_impl(void *dst, const void *src, size_t n) {
33+
__CPROVER_precondition(
34+
__CPROVER_POINTER_OBJECT(dst) != __CPROVER_POINTER_OBJECT(src) ||
35+
((const char *)src >= (const char *)dst + n) || ((const char *)dst >= (const char *)src + n),
36+
"memcpy src/dst overlap");
37+
__CPROVER_precondition(__CPROVER_r_ok(src, n), "memcpy source region readable");
38+
__CPROVER_precondition(__CPROVER_w_ok(dst, n), "memcpy destination region writeable");
39+
40+
size_t num_uint64s = n >> 3;
41+
size_t rem = n & 0x7;
42+
43+
uint8_t *d = (uint8_t *)dst;
44+
const uint8_t *s = (const uint8_t *)src;
45+
46+
// Use fallthrough to unroll the remainder loop
47+
switch (rem) {
48+
case 7:
49+
d[6] = s[6];
50+
case 6:
51+
d[5] = s[5];
52+
case 5:
53+
d[4] = s[4];
54+
case 4:
55+
d[3] = s[3];
56+
case 3:
57+
d[2] = s[2];
58+
case 2:
59+
d[1] = s[1];
60+
case 1:
61+
d[0] = s[0];
62+
}
63+
64+
d += rem;
65+
s += rem;
66+
67+
for (size_t i = 0; i < num_uint64s; ++i) {
68+
((uint64_t *)d)[i] = ((const uint64_t *)s)[i];
69+
}
70+
71+
return dst;
72+
}
73+
74+
void *memcpy(void *dst, const void *src, size_t n) {
75+
return memcpy_using_uint64_impl(dst, src, n);
76+
}
77+
78+
void *__builtin___memcpy_chk(void *dst, const void *src, __CPROVER_size_t n, __CPROVER_size_t size) {
79+
(void)size;
80+
return memcpy_using_uint64_impl(dst, src, n);
81+
}

0 commit comments

Comments
 (0)