xref: /third_party/musl/ldso/linux/dynlink_rand.c (revision 570af302)
1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "dynlink_rand.h"
17
18#include <stdlib.h>
19#include <sys/mman.h>
20#include <sys/random.h>
21#include <unistd.h>
22
23#define HANDLE_INCREASE 2
24#define TASK_BASE_CAPACITY 8
25
26// linked list node for handle randomization
27struct handle_node {
28    struct handle_node *next;
29    void *handle;
30    struct dso *dso;
31};
32
33// linked list for handle randomization
34static struct handle_node *handle_map_list = NULL;
35
36static uintptr_t saved_handle = 0;
37
38void *add_handle_node(void *handle, struct dso *dso)
39{
40    struct handle_node *node = __libc_malloc(sizeof(*node));
41    if (!node) {
42        return NULL;
43    }
44    node->handle = handle;
45    node->dso = dso;
46    node->next = handle_map_list;
47    handle_map_list = node;
48    return node;
49}
50
51struct dso *find_dso_by_handle(void *handle)
52{
53    struct handle_node *node = handle_map_list;
54    while (node) {
55        if (node->handle == handle) {
56            return node->dso;
57        }
58        node = node->next;
59    }
60    return NULL;
61}
62
63void *find_handle_by_dso(struct dso *dso)
64{
65    struct handle_node *node = handle_map_list;
66    while (node) {
67        if (node->dso == dso) {
68            return node->handle;
69        }
70        node = node->next;
71    }
72    return 0;
73}
74
75void remove_handle_node(void *handle)
76{
77    struct handle_node *node = handle_map_list;
78    struct handle_node *pre_node = NULL;
79    while (node) {
80        if (node->handle == handle) {
81            if (pre_node) {
82                pre_node->next = node->next;
83            } else {
84                handle_map_list = node->next;
85            }
86            __libc_free(node);
87            return;
88        } else {
89            pre_node = node;
90            node = node->next;
91        }
92    }
93}
94
95static void *gen_handle(void)
96{
97    uintptr_t handle = saved_handle;
98    do {
99        if (getrandom(&handle, sizeof handle, GRND_RANDOM | GRND_NONBLOCK) == -1) {
100            handle += HANDLE_INCREASE;
101            saved_handle = handle;
102        }
103    } while (find_dso_by_handle((void *)handle) || handle == 0);
104    return (void *)handle;
105}
106
107void *assign_valid_handle(struct dso *p)
108{
109    void *handle = find_handle_by_dso(p);
110    if (handle == 0) {
111        handle = gen_handle();
112        if (!add_handle_node(handle, p)) {
113            handle = 0;
114        }
115    }
116    return handle;
117}
118
119struct loadtasks *create_loadtasks(void)
120{
121    struct loadtasks *tasks = __libc_malloc(sizeof(struct loadtasks));
122    if (tasks) {
123        tasks->array = NULL;
124        tasks->capacity = 0;
125        tasks->length = 0;
126        return tasks;
127    }
128    return NULL;
129}
130
131bool append_loadtasks(struct loadtasks *tasks, struct loadtask *item)
132{
133    if (tasks->length + 1 > tasks->capacity) {
134        size_t new_cap = 0;
135        new_cap = tasks->capacity + TASK_BASE_CAPACITY;
136        void *realloced = NULL;
137        if (tasks->array) {
138            realloced = __libc_realloc(tasks->array, new_cap * sizeof(struct loadtask *));
139        } else {
140            realloced = __libc_malloc(TASK_BASE_CAPACITY * sizeof(struct loadtask *));
141        }
142        if (realloced) {
143            tasks->array = realloced;
144            tasks->capacity = new_cap;
145        } else {
146            return false;
147        }
148    }
149    tasks->array[tasks->length] = item;
150    tasks->length += 1;
151    return true;
152}
153
154void free_task(struct loadtask *task)
155{
156    if (task == NULL) {
157        return;
158    }
159    if (task->name) {
160        __libc_free(task->name);
161        task->name = NULL;
162    }
163    if (task->allocated_buf) {
164        __libc_free(task->allocated_buf);
165        task->allocated_buf = NULL;
166    }
167    if (task->shdr_allocated_buf != MAP_FAILED) {
168        munmap(task->shdr_allocated_buf, task->shsize);
169        task->shdr_allocated_buf = MAP_FAILED;
170    }
171    if (task->dyn_map_len) {
172        munmap(task->dyn_map, task->dyn_map_len);
173        task->dyn_map = NULL;
174        task->dyn_map_len = 0;
175    }
176    if (task->str_map_len) {
177        munmap(task->str_map, task->str_map_len);
178        task->str_map = NULL;
179        task->str_map_len = 0;
180    }
181    if (task->fd != -1 && task->fd) {
182        close(task->fd);
183        task->fd = -1;
184    }
185    __libc_free(task);
186}
187
188struct loadtask *get_loadtask(struct loadtasks *tasks, size_t index)
189{
190    if (tasks && tasks->array && (index < tasks->length)) {
191        return tasks->array[index];
192    } else {
193        return NULL;
194    }
195}
196
197void free_loadtasks(struct loadtasks *tasks)
198{
199    if (tasks) {
200        if (tasks->length) {
201            for (int i = 0; i < tasks->length; i++) {
202                free_task(get_loadtask(tasks, i));
203            }
204            tasks->length = 0;
205        }
206        if (tasks->array) {
207            __libc_free(tasks->array);
208            tasks->array = NULL;
209        }
210        tasks->capacity = 0;
211        __libc_free(tasks);
212    }
213}
214
215void shuffle_loadtasks(struct loadtasks *tasks)
216{
217    size_t index = 0;
218    struct loadtask *task = NULL;
219    for (size_t i = 0; i < tasks->length; i++) {
220        // Use flag GRND_RANDOM should "block" or "nonblock with retry". This will result in performance loss.
221        if (getrandom(&index, sizeof index, GRND_NONBLOCK) == -1) {
222            return;
223        } else {
224            index %= tasks->length;
225            task = tasks->array[i];
226            tasks->array[i] = tasks->array[index];
227            tasks->array[index] = task;
228        }
229    }
230}
231
232struct loadtask *create_loadtask(const char *name, struct dso *needed_by, ns_t *ns, bool check_inherited)
233{
234    size_t name_len = strlen(name);
235    char *name_buf = (char *)__libc_malloc(name_len + 1);
236    if (!name_buf) {
237        return NULL;
238    }
239    struct loadtask *task = __libc_calloc(1, sizeof(struct loadtask));
240    if (!task) {
241        return NULL;
242    }
243    strcpy(name_buf, name);
244    task->name = name_buf;
245    task->needed_by = needed_by;
246    task->namespace = ns;
247    task->check_inherited = check_inherited;
248    task->shdr_allocated_buf = MAP_FAILED;
249    return task;
250}
251