未验证 提交 0f176f00 编写于 作者: O openharmony_ci 提交者: Gitee

!836 mallocgng 安全性能优化及对应测试用例

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