提交 731da990 编写于 作者: R root

feature: mallocng memory addresses random and pointer confusion

Close #I6I6M9
Signed-off-by: Nwangchen <wangchen240@huawei.com>
上级 a31ed2a6
......@@ -28,6 +28,7 @@ musl编译框架时编译,使用BUILD.gn进行配置编译。
- 提供加载器namespace机制
- OHOS容器中能够运行依赖bionic的库
- musl全球化接口适配locale数据能力
- mallocng堆内存分配器安全增强,默认开启meta指针混淆。地址随机化通过MALLOC_SECURE_ALL宏开关。可在编译命令中增加--gn-args="musl_secure_level=3"开启
等。在新增特性基础上,也进行了对于musl接口功能的完善与错误的修复。
......
......@@ -12,57 +12,147 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <malloc.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include "test.h"
int main(int argc, char *argv[])
#define UNIT 16
#define OFF_OFFSET 2
#define FIRST_OFFSET (-4)
#define FIRST_OFF_OFFSET 8
#define MALLOC_SIZE_S (10 * sizeof(uintptr_t))
#define MALLOC_SIZE_L (50 * sizeof(uintptr_t))
#define TEST_NUM 512
struct meta_in {
struct meta_in *prev, *next;
uintptr_t *mem;
};
struct group_in {
struct meta_in *meta;
};
static struct group_in *get_group(const uint8_t *p)
{
uintptr_t *c0 = (uintptr_t *)malloc(sizeof(uintptr_t) * 10);
if (!c0) {
t_error("Malloc c0 failed: %s\n", strerror(errno));
return -1;
int offset = *(const uint16_t *)(p - OFF_OFFSET);
if (p[FIRST_OFFSET]) {
offset = *(uint32_t *)(p - FIRST_OFF_OFFSET);
}
/* Malloc dividing chunk to avoid combination of neighbouring freed chunk */
void *d0 = malloc(sizeof(uintptr_t) * 10);
if (!d0) {
t_error("Malloc d0 failed: %s\n", strerror(errno));
return -1;
struct group_in *base = (void *)(p - UNIT*offset - UNIT);
return base;
}
static void handler(int s)
{
}
static int child(void)
{
void *d0;
void *d1;
struct group_in *g0 = NULL;
struct group_in *g1 = NULL;
struct group_in *g2 = NULL;
for (int i = 0; i < TEST_NUM; ++i) {
d0 = malloc(MALLOC_SIZE_S);
if (!d0) {
t_error("Malloc d0 failed: %s\n", strerror(errno));
return -1;
}
g0 = get_group(d0);
d1 = malloc(MALLOC_SIZE_L);
if (!d1) {
t_error("Malloc d1 failed: %s\n", strerror(errno));
return -1;
}
g1 = get_group(d1);
if ((uintptr_t)g0->meta->mem == (uintptr_t)g0)
t_error("encoding pointer is equal to real pointer 1\n");
if ((uintptr_t)g1->meta->mem == (uintptr_t)g1)
t_error("encoding pointer is equal to real pointer 2\n");
if ((uintptr_t)g0->meta->prev->next == (uintptr_t)g0->meta->mem)
t_error("encoding pointer is equal to real pointer 1\n");
if ((uintptr_t)g1->meta->prev->next == (uintptr_t)g1->meta->mem)
t_error("encoding pointer is equal to real pointer 2\n");
free(d0);
free(d1);
}
return 0;
}
static pid_t start_child(void)
{
pid_t pid;
int ret;
pid = fork();
if (pid == 0) {
ret = child();
t_error("child process normally out with %d\n", ret);
return ret;
}
return pid;
}
uintptr_t *c1 = (uintptr_t *)malloc(sizeof(uintptr_t) * 10);
if (!c1) {
t_error("Malloc c1 failed: %s\n", strerror(errno));
int main(int argc, char *argv[])
{
sigset_t set;
int status;
pid_t pid;
int flag = 0;
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_BLOCK, &set, 0);
signal(SIGCHLD, handler);
pid = start_child();
if (pid == -1) {
t_error("%s fork failed: %s\n", argv[0], strerror(errno));
return -1;
}
/* Malloc dividing chunk to avoid combination of neighbouring freed chunk */
void *d1 = malloc(sizeof(uintptr_t) * 10);
if (!d1) {
t_error("Malloc d1 failed: %s\n", strerror(errno));
if (sigtimedwait(&set, 0, &(struct timespec){5, 0}) == -1) { /* Wait for 5 seconds */
if (errno == EAGAIN)
flag = 1;
else
t_error("%s sigtimedwait failed: %s\n", argv[0], strerror(errno));
if (kill(pid, SIGKILL) == -1)
t_error("%s kill failed: %s\n", argv[0], strerror(errno));
}
if (waitpid(pid, &status, 0) != pid) {
t_error("%s waitpid failed: %s\n", argv[0], strerror(errno));
return -1;
}
/* Free the chunk, with same size, they're put into the same bin */
/* -------- -------- --------
* | c0 | | c1 | | bin |
* -->| next |----->| next |----->| next |-----
* | | prev |<-----| prev |<-----| prev | |
* | ------- ------- ------- |
* --------------------------------------------
*/
free(c0);
free(c1);
uintptr_t xoraddr = c0[0]; /* Get the next of c0 */
uintptr_t realaddr = (uintptr_t)((char *)c1 - sizeof(uintptr_t) * 2);
if (xoraddr == realaddr) {
t_error("encoding pointer is equal to real pointer\n");
if (flag) {
t_error("Child process time out\n");
}
free(d0);
free(d1);
return t_status;
if (WIFSIGNALED(status)) {
if (WTERMSIG(status) != SIGSEGV && WTERMSIG(status) != SIGILL) {
t_error("%s child process out with %s\n", argv[0], strsignal(WTERMSIG(status)));
return -1;
}
} else {
t_error("%s child process finished normally\n", argv[0]);
}
return 0;
}
......@@ -25,49 +25,61 @@ static void handler(int s)
{
}
volatile uintptr_t *p0;
volatile uintptr_t *p1;
volatile void *tmp[512];
uint8_t *p0;
uint8_t *p1;
#define UNIT 16
#define OFF_OFFSET 2
#define FIRST_OFFSET (-4)
#define FIRST_OFF_OFFSET 8
#define MALLOC_SIZE_S (10 * sizeof(uintptr_t))
#define TEST_NUM 512
volatile void *tmp[TEST_NUM];
struct meta_in {
struct meta_in *prev, *next;
uintptr_t *mem;
};
struct group_in {
struct meta_in *meta;
};
static struct group_in *get_group(uint8_t *p)
{
int offset = *(uint16_t *)(p - OFF_OFFSET);
if (p[FIRST_OFFSET]) {
offset = *(uint32_t *)(p - FIRST_OFF_OFFSET);
}
struct group_in *base = (void *)(p - UNIT*offset - UNIT);
return base;
}
static int child(void)
{
p0 = (uintptr_t *)malloc(10 * sizeof(uintptr_t));
struct group_in *g = NULL;
p0 = (uint8_t *)malloc(MALLOC_SIZE_S);
if (!p0) {
t_error("Malloc failed:%s\n", strerror(errno));
return -1;
}
/* Malloc a dividing chunk to avoid combination of neighbouring freed chunk */
tmp[0] = malloc(10 * sizeof(uintptr_t));
/* Malloc another chunk to get a key */
p1 = (uintptr_t *)malloc(10 * sizeof(uintptr_t));
if (!p1) {
t_error("Malloc failed:%s\n", strerror(errno));
return -1;
}
/* Malloc a dividing chunk to avoid combination of neighbouring freed chunk */
tmp[0] = malloc(10 * sizeof(uintptr_t));
free((void *)p0);
free((void *)p1);
uintptr_t *fake_ptr = (uintptr_t *)((uintptr_t)((char *)p1 - sizeof(size_t) * 2) ^ (uintptr_t)p0[0]);
p0[0] = (uintptr_t)fake_ptr;
p1[0] = (uintptr_t)fake_ptr;
/*
* The init procedure makes the freelist unpredictable. To make sure to trigger the ivalid ptr
* acess, here we create as many chunks as possible to make sure there are enough chunks in
* bin[j] of size "10 * sizeof(uintptr_t)". Basically this is heap spray.
*/
for (int i = 0; i < 512; ++i) {
tmp[i] = malloc(10 *sizeof(uintptr_t));
tmp[0] = malloc(MALLOC_SIZE_S);
g = get_group(p0);
free((void *)tmp[0]);
g->meta += 1;
for (int i = 0; i < TEST_NUM; ++i) {
tmp[i] = malloc(MALLOC_SIZE_S);
}
/*
* When freelist quarantine is on, the modifiy-pointer chunk maybe in quarantine. So here we
* need free the pointer.
*/
for (int i = 0; i < 512; ++i) {
for (int i = 0; i < TEST_NUM; ++i) {
free((void *)tmp[i]);
}
return 0;
......
......@@ -8,16 +8,27 @@
#define THREAD_MAX_N 8
#define SIZE_ALIGN (4 * sizeof(size_t))
#define THRESHOLD (0x1c00 * SIZE_ALIGN)
#define ITER_TIME 20
#define MMAP_THRESHOLD 131052
#define FREE_CYCLE 16
#define THRESHOLD (MMAP_THRESHOLD / 16)
#define ITER_TIME 80
#define NANOSEC_PER_SEC 1e9
#define MALLOC_TIME (ITER_TIME * (THRESHOLD / (SIZE_ALIGN + 1)))
void free_all(void **ptr)
{
for (int j = 0; j < FREE_CYCLE; j++) {
free(ptr[j]);
}
}
void *func(void *arg)
{
int *val = (int *)arg;
cpu_set_t mask;
struct timespec ts[2];
int num = 0;
void *ptr[FREE_CYCLE];
CPU_ZERO(&mask);
CPU_SET(0, &mask);
......@@ -28,13 +39,17 @@ void *func(void *arg)
for (int i = 0; i < ITER_TIME; ++i) {
for (size_t size = 0; size < THRESHOLD; size += SIZE_ALIGN + 1) {
void *ptr = malloc(size);
if (!ptr) {
if (num == FREE_CYCLE) {
free_all(ptr);
num = 0;
}
ptr[num] = malloc(size);
if (!ptr[num]) {
t_error("Thread %d malloc failed for size %u\n", *val, size);
*val = errno;
return NULL;
}
free(ptr);
num++;
}
}
......
/*
* Copyright (C) 2022 Huawei Device Co., Ltd.
* Licensed 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.
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include "test.h"
#define MALLOC_SIZE_S 0x20
#define MALLOC_SIZE_L 0x40
#define IDX_IN_CHUNK (-3)
static int get_slotnum(uint8_t *p)
{
return (p[IDX_IN_CHUNK] & 0x1f);
}
int main(void)
{
void *p = malloc(MALLOC_SIZE_S);
void *q = malloc(MALLOC_SIZE_S);
void *r = malloc(MALLOC_SIZE_S);
if (!p || !q || !r)
t_error("malloc returned NULL\n");
int slot_p_1 = get_slotnum((uint8_t *)p);
int slot_q_1 = get_slotnum((uint8_t *)q);
int slot_r_1 = get_slotnum((uint8_t *)r);
free(p);
free(q);
free(r);
p = malloc(MALLOC_SIZE_L);
q = malloc(MALLOC_SIZE_L);
r = malloc(MALLOC_SIZE_L);
if (!p || !q || !r)
t_error("malloc returned NULL\n");
int slot_p_2 = get_slotnum((uint8_t *)p);
int slot_q_2 = get_slotnum((uint8_t *)q);
int slot_r_2 = get_slotnum((uint8_t *)r);
free(p);
free(q);
free(r);
if (((slot_p_1 + 1) == slot_q_1) && ((slot_q_1 + 1) == slot_r_1)) {
if (((slot_p_2 + 1) == slot_q_2) && ((slot_q_2 + 1) == slot_r_2))
t_error("malloc(0) random error\n");
}
return t_status;
}
......@@ -21,6 +21,34 @@
#include <string.h>
#include "test.h"
#define UNIT 16
#define OFF_OFFSET 2
#define FIRST_OFFSET (-4)
#define FIRST_OFF_OFFSET 8
#define MALLOC_SIZE_S 50
#define MALLOC_SIZE_L 100
#define TEST_NUM 512
struct meta_in {
struct meta_in *prev, *next;
uintptr_t *mem;
};
struct group_in {
struct meta_in *meta;
};
static struct group_in *get_group(const uint8_t *p)
{
int offset = *(const uint16_t *)(p - OFF_OFFSET);
if (p[FIRST_OFFSET]) {
offset = *(uint32_t *)(p - FIRST_OFF_OFFSET);
}
struct group_in *base = (void *)(p - UNIT*offset - UNIT);
return base;
}
static void handler(int s)
{
}
......@@ -38,31 +66,29 @@ int set_devide_chunk(size_t size)
static int child(void)
{
uintptr_t *c;
uintptr_t *temp;
uint8_t *c1;
uint8_t *c2;
/* Set first dividing chunk */
if (set_devide_chunk(sizeof(size_t)))
return -1;
uint8_t *temp;
struct group_in *g1 = NULL;
struct group_in *g2 = NULL;
/*
* The init procedure makes the freelist unpredictable. To make sure trigger the safe-unlink
* check, Here we create as many chunks as possible to make sure there are enough chunks in
* bin[0] and malloc again. Basically this is heap spray.
*/
for (int i = 0; i < 512; ++i) {
if (set_devide_chunk(sizeof(size_t)))
for (int i = 0; i < TEST_NUM; ++i) {
c1 = (uint8_t *)malloc(MALLOC_SIZE_S);
if (!c1) {
t_error("Malloc failed: %s\n", strerror(errno));
return -1;
c = (uintptr_t *)malloc(sizeof(uintptr_t));
if (!c) {
}
g1 = get_group(c1);
c2 = (uint8_t *)malloc(MALLOC_SIZE_L);
if (!c2) {
t_error("Malloc failed: %s\n", strerror(errno));
return -1;
}
free(c);
/* exchange the prev and next pointer */
uintptr_t temp = c[0];
c[0] = c[1];
c[1] = temp;
g2 = get_group(c2);
g2->meta = g1->meta;
free(c2);
free(c1);
}
return 0;
......
......@@ -81,6 +81,7 @@ regression_list = [
if (musl_secure_level >= 3) {
regression_list += [
"malloc-random",
"malloc-overflow-check",
"malloc-uaf-check",
]
......
......@@ -19,31 +19,27 @@ extern int je_mallopt(int param, int value);
#ifdef MALLOC_SECURE_ALL
#include <fcntl.h>
#define RANDOM_BUFFER_LEN 64
#define RANDOM_BUFFER_LEN 512
static uint8_t buffer[RANDOM_BUFFER_LEN] = { 0 };
static size_t ri = RANDOM_BUFFER_LEN;
static void* get_ri(size_t width)
static uint8_t get_random8()
{
if ((ri + width > RANDOM_BUFFER_LEN) || (buffer[0] == 0)) {
uint8_t num;
if ((ri >= RANDOM_BUFFER_LEN) || (buffer[0] == 0)) {
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
return (void *)buffer;
num = (uint8_t)get_random_secret();
return num;
}
read(fd, buffer, RANDOM_BUFFER_LEN);
close(fd);
ri = 0;
}
ri += width;
return (void *)(buffer + ri - width);
}
static uint8_t get_random8()
{
uint8_t *pr = (uint8_t *)get_ri(sizeof(uint8_t));
return *pr;
num = buffer[ri];
ri++;
return num;
}
static int get_randomIdx(int avail_mask, int last_idx)
......
......@@ -75,7 +75,7 @@ int is_allzero(void *);
static inline void *encode_ptr(void *ptr, uint64_t key)
{
#ifdef MALLOC_FREELIST_HARDENED
return (void *)((uintptr_t)ptr ^ (key | 1));
return (void *)((uintptr_t)ptr ^ key);
#else
(void)key;
return ptr;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册