1 | /* Copyright (C) 1991-2022 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <errno.h> |
19 | #include <hurd.h> |
20 | #include <hurd/resource.h> |
21 | #include <lock-intern.h> /* For `struct mutex'. */ |
22 | #include <vm_param.h> |
23 | |
24 | #include "set-hooks.h" |
25 | |
26 | |
27 | /* Initial maximum size of the data segment (this is arbitrary). */ |
28 | #define DATA_SIZE (128 * 1024 * 1024) |
29 | |
30 | /* Up to the page including this address is allocated from the kernel. |
31 | This address is the data resource limit. */ |
32 | vm_address_t _hurd_data_end; |
33 | |
34 | /* Up to this address is actually available to the user. |
35 | Pages beyond the one containing this address allow no access. */ |
36 | vm_address_t _hurd_brk = 0; |
37 | |
38 | /* This name is used by the Linux crtbeginS.o for reasons you don't even |
39 | want to think about it. It's just easier to provide some definition for |
40 | it than even to explain the braindamage involved. */ |
41 | weak_alias (_hurd_brk, ___brk_addr) |
42 | |
43 | struct mutex _hurd_brk_lock; |
44 | |
45 | static vm_address_t brk_start; |
46 | |
47 | |
48 | /* Set the end of the process's data space to INADDR. |
49 | Return 0 if successful, -1 if not. */ |
50 | int |
51 | __brk (void *inaddr) |
52 | { |
53 | int ret; |
54 | HURD_CRITICAL_BEGIN; |
55 | __mutex_lock (&_hurd_brk_lock); |
56 | ret = _hurd_set_brk ((vm_address_t) inaddr); |
57 | __mutex_unlock (&_hurd_brk_lock); |
58 | HURD_CRITICAL_END; |
59 | return ret; |
60 | } |
61 | weak_alias (__brk, brk) |
62 | |
63 | |
64 | int |
65 | _hurd_set_brk (vm_address_t addr) |
66 | { |
67 | error_t err = 0; |
68 | vm_address_t pagend = round_page (addr); |
69 | vm_address_t pagebrk = round_page (_hurd_brk); |
70 | long int rlimit; |
71 | |
72 | if (pagend <= pagebrk) |
73 | { |
74 | if (pagend < pagebrk) |
75 | { |
76 | /* XXX wish this were atomic... */ |
77 | /* First deallocate the memory to release its backing space. */ |
78 | __vm_deallocate (__mach_task_self (), pagend, pagebrk - pagend); |
79 | /* Now reallocate it with no access allowed. */ |
80 | err = __vm_map (__mach_task_self (), |
81 | &pagend, pagebrk - pagend, |
82 | 0, 0, MACH_PORT_NULL, 0, 0, |
83 | 0, VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE, |
84 | VM_INHERIT_COPY); |
85 | /* XXX what if error? */ |
86 | } |
87 | _hurd_brk = addr; |
88 | return 0; |
89 | } |
90 | |
91 | __mutex_lock (&_hurd_rlimit_lock); |
92 | rlimit = _hurd_rlimits[RLIMIT_DATA].rlim_cur; |
93 | __mutex_unlock (&_hurd_rlimit_lock); |
94 | |
95 | if (addr - brk_start > rlimit) |
96 | { |
97 | /* Need to increase the resource limit. */ |
98 | errno = ENOMEM; |
99 | return -1; |
100 | } |
101 | |
102 | if (pagend > _hurd_data_end) |
103 | { |
104 | vm_address_t alloc_start = _hurd_data_end; |
105 | |
106 | /* We didn't allocate enough space! Hopefully we can get some more! */ |
107 | |
108 | if (_hurd_data_end > pagebrk) |
109 | /* First finish allocation. */ |
110 | err = __vm_protect (__mach_task_self (), pagebrk, |
111 | alloc_start - pagebrk, 0, |
112 | VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); |
113 | if (! err) |
114 | _hurd_brk = alloc_start; |
115 | |
116 | if (! err) |
117 | err = __vm_allocate (__mach_task_self (), &alloc_start, |
118 | pagend - alloc_start, 0); |
119 | |
120 | if (! err) |
121 | _hurd_data_end = pagend; |
122 | } |
123 | else |
124 | /* Make the memory accessible. */ |
125 | err = __vm_protect (__mach_task_self (), pagebrk, pagend - pagebrk, |
126 | 0, VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE); |
127 | |
128 | if (err) |
129 | return __hurd_fail (err); |
130 | |
131 | _hurd_brk = addr; |
132 | return 0; |
133 | } |
134 | |
135 | static void attribute_used_retain |
136 | init_brk (void) |
137 | { |
138 | vm_address_t pagend; |
139 | |
140 | __mutex_init (&_hurd_brk_lock); |
141 | |
142 | brk_start = (vm_address_t) BRK_START; |
143 | |
144 | /* If _hurd_brk is already set, don't change it. The assumption is that |
145 | it was set in a previous run before something like Emacs's unexec was |
146 | called and dumped all the data up to the break at that point. */ |
147 | if (_hurd_brk == 0) { |
148 | _hurd_brk = (vm_address_t) BRK_START; |
149 | } |
150 | |
151 | pagend = round_page (_hurd_brk); |
152 | |
153 | _hurd_data_end = round_page (brk_start + DATA_SIZE); |
154 | |
155 | if (pagend < _hurd_data_end) |
156 | { |
157 | /* We use vm_map to allocate and change permissions atomically. */ |
158 | if (__vm_map (__mach_task_self (), &pagend, _hurd_data_end - pagend, |
159 | 0, 0, MACH_PORT_NULL, 0, 0, |
160 | 0, VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE, |
161 | VM_INHERIT_COPY)) |
162 | /* Couldn't allocate the memory. The break will be very short. */ |
163 | _hurd_data_end = pagend; |
164 | } |
165 | } |
166 | SET_RELHOOK (_hurd_preinit_hook, init_brk); |
167 | |