DynamoRIO / DynamoRIO/dynamorio
Modified code is never detected without a cache maintenance operation
- Dominant language
- C
- Stars
- 3.2k
- Forks
- 629
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 31
Description
The ARM architecture allows a branch instruction to be replaced with another branch instruction and the code to be executed with no intervening cache maintenance. In this case either the previous or the new instruction will be executed, and eventually the new instruction will be executed.
When run natively, the program below was found to print "PASS", typically after fewer than a million iterations. Under DynamoRIO it never terminates.
I do not know of any real software that depends on this behaviour so this issue presumably has low priority, for now.
```
/* **********************************************************
* Copyright (c) 2016 ARM Limited. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of ARM Limited nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL ARM LIMITED OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include
#include
#include
#include
#if defined(__arm__)
# define BRANCH1 0xeaffffff /* b . + 4 */
# define BRANCH2 0xea000000 /* b . + 8 */
# define MOV0 0xe3a00000 /* mov r0, #0 */
# define RET 0xe12fff1e /* bx lr */
#elif defined(__aarch64__)
# define BRANCH1 0x14000001 /* b . + 4 */
# define BRANCH2 0x14000002 /* b . + 8 */
# define MOV0 0x52800000 /* mov w0, #0 */
# define RET 0xd65f03c0 /* ret */
#else
# error NYI
#endif
int main()
{
uint32_t *x = mmap(0, 12, PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
int (*f)(int) = (int (*)(int))x;
unsigned long long n;
int i;
/* Initial version of f ignores its argument and returns 0. */
x[0] = BRANCH1;
x[1] = MOV0;
x[2] = RET;
__clear_cache(x, x + 3);
/* Execute it a few times to warm up the cache. */
for (i = 0; i < 1000; i++) {
int r = f(i);
if (r != 0) {
printf("FAIL 1: %d %d\n", i, r);
exit(1);
}
}
/* Modified version of f returns its argument. */
x[0] = BRANCH2;
/* We do not clear the cache but expect the change to take effect eventually. */
for (n = 0; ; n++) {
int r = f(7);
if (r == 7)
break;
if (r != 0) {
printf("FAIL 2: %llu %d\n", n, r);
exit(1);
}
}
printf("PASS: %llu\n", n);
return 0;
}
```
Contributor guide
Assessment
This issue has not been assessed yet.