blob: 9c792be7db886d2df13b1ce2aabf1a8a6cbd9f74 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Ilya Berezhniuk
*/
#include <sys/mman.h>
#include <limits.h>
#include <unistd.h>
#include "port_general.h"
#include "port_barriers.h"
#include "port_malloc.h"
#include "signals_internal.h"
#include "port_memaccess.h"
#include "port_thread_internal.h"
extern "C" void port_memcpy_asm(void* dst, const void* src, size_t size,
void** prestart_addr, void* memcpyaddr);
static int memcpy_internal(void* dst, const void* src, size_t size, int from)
{
int res;
port_tls_data_t* tlsdata = get_private_tls_data();
if (!tlsdata)
{
tlsdata = (port_tls_data_t*)STD_ALLOCA(sizeof(port_tls_data_t));
res = port_thread_attach_local(tlsdata, TRUE, TRUE, 0);
if (res != 0)
return res;
}
tlsdata->violation_flag = 1;
void* memcpyaddr = (void*)&memcpy;
port_memcpy_asm(dst, src, size, &tlsdata->restart_address, memcpyaddr);
if (tlsdata->violation_flag == 1)
{
tlsdata->violation_flag = 0;
port_thread_detach_temporary();
return 0;
}
// Try changing memory access
size_t page_size = (size_t)sysconf(_SC_PAGE_SIZE);
const void* addr = from ? src : dst;
POINTER_SIZE_INT start =
(POINTER_SIZE_INT)addr & ~(page_size - 1);
POINTER_SIZE_INT pastend =
((POINTER_SIZE_INT)addr + size + page_size - 1) & ~(page_size - 1);
int result = mprotect((void*)start, pastend - start,
PROT_READ | PROT_WRITE | PROT_EXEC);
if (result == EAGAIN)
{
timespec delay = {0, 10};
nanosleep(&delay, NULL);
result = mprotect((void*)start, pastend - start,
PROT_READ | PROT_WRITE | PROT_EXEC);
}
if (result != 0)
{
tlsdata->violation_flag = 0;
port_thread_detach_temporary();
return -1;
}
tlsdata->violation_flag = 1;
port_memcpy_asm(dst, src, size, &tlsdata->restart_address, memcpyaddr);
res = (tlsdata->violation_flag == 1) ? 0 : -1;
tlsdata->violation_flag = 0;
port_thread_detach_temporary();
return res;
}
int port_read_memory(void* addr, size_t size, void* buf)
{
if (!buf || !addr)
return -1;
if (size == 0)
return 0;
return memcpy_internal(buf, addr, size, 1);
}
int port_write_memory(void* addr, size_t size, void* buf)
{
if (!buf || !addr)
return -1;
if (size == 0)
return 0;
int result = memcpy_internal(addr, buf, size, 0);
if (result == 0)
{
port_rw_barrier();
}
return result;
}