syslog: Improve fortify with clang
[glibc.git] / sysdeps / mach / hurd / mremap.c
blobda1c8a50bb3346b017008fd9ec0b33698e183474
1 /* Copyright (C) 2020-2024 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
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.
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.
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/>. */
18 #include <sys/types.h>
19 #include <sys/mman.h>
20 #include <errno.h>
21 #include <stdarg.h>
22 #include <hurd.h>
24 #include <stdio.h>
26 /* Remap pages mapped by the range [ADDR,ADDR+OLD_LEN) to new length
27 NEW_LEN. If MREMAP_MAYMOVE is set in FLAGS the returned address
28 may differ from ADDR. If MREMAP_FIXED is set in FLAGS the function
29 takes another parameter which is a fixed address at which the block
30 resides after a successful call. */
32 void *
33 __mremap (void *addr, size_t old_len, size_t new_len, int flags, ...)
35 error_t err;
36 vm_address_t vm_addr = (vm_address_t) addr;
37 vm_offset_t new_vm_addr = 0;
39 vm_address_t begin = vm_addr;
40 vm_address_t end;
41 vm_size_t len;
42 vm_prot_t prot;
43 vm_prot_t max_prot;
44 vm_inherit_t inherit;
45 boolean_t shared;
46 memory_object_name_t obj;
47 vm_offset_t offset;
49 if ((flags & ~(MREMAP_MAYMOVE | MREMAP_FIXED)) ||
50 ((flags & MREMAP_FIXED) && !(flags & MREMAP_MAYMOVE)) ||
51 (old_len == 0 && !(flags & MREMAP_MAYMOVE)))
52 return (void *) (long int) __hurd_fail (EINVAL);
54 if (flags & MREMAP_FIXED)
56 va_list arg;
57 va_start (arg, flags);
58 new_vm_addr = (vm_offset_t) va_arg (arg, void *);
59 va_end (arg);
62 err = __vm_region (__mach_task_self (),
63 &begin, &len, &prot, &max_prot, &inherit,
64 &shared, &obj, &offset);
65 if (err)
66 return (void *) (uintptr_t) __hurd_fail (err);
68 if (begin > vm_addr)
70 err = EFAULT;
71 goto out;
74 if (begin < vm_addr || (old_len != 0 && old_len != len))
76 err = EINVAL;
77 goto out;
80 end = begin + len;
82 if ((flags & MREMAP_FIXED) &&
83 ((new_vm_addr + new_len > vm_addr && new_vm_addr < end)))
85 /* Overlapping is not supported, like in Linux. */
86 err = EINVAL;
87 goto out;
90 /* FIXME: locked memory. */
92 if (old_len != 0 && !(flags & MREMAP_FIXED))
94 /* A mere change of the existing map. */
96 if (new_len == len)
98 new_vm_addr = vm_addr;
99 goto out;
102 if (new_len < len)
104 /* Shrink. */
105 __mach_port_deallocate (__mach_task_self (), obj);
106 err = __vm_deallocate (__mach_task_self (),
107 begin + new_len, len - new_len);
108 new_vm_addr = vm_addr;
109 goto out;
112 /* Try to expand. */
113 err = __vm_map (__mach_task_self (),
114 &end, new_len - len, 0, 0,
115 obj, offset + len, 0, prot, max_prot, inherit);
116 if (!err)
118 /* Ok, that worked. Now coalesce them. */
119 new_vm_addr = vm_addr;
121 /* XXX this is not atomic as it is in unix! */
122 err = __vm_deallocate (__mach_task_self (), begin, new_len);
123 if (err)
125 __vm_deallocate (__mach_task_self (), end, new_len - len);
126 goto out;
129 err = __vm_map (__mach_task_self (),
130 &begin, new_len, 0, 0,
131 obj, offset, 0, prot, max_prot, inherit);
132 if (err)
134 /* Oops, try to remap before reporting. */
135 __vm_map (__mach_task_self (),
136 &begin, len, 0, 0,
137 obj, offset, 0, prot, max_prot, inherit);
140 goto out;
144 if (!(flags & MREMAP_MAYMOVE))
146 /* Can not map here */
147 err = ENOMEM;
148 goto out;
151 err = __vm_map (__mach_task_self (),
152 &new_vm_addr, new_len, 0,
153 new_vm_addr == 0, obj, offset,
154 old_len == 0, prot, max_prot, inherit);
156 if (err == KERN_NO_SPACE && (flags & MREMAP_FIXED))
158 /* XXX this is not atomic as it is in unix! */
159 /* The region is already allocated; deallocate it first. */
160 err = __vm_deallocate (__mach_task_self (), new_vm_addr, new_len);
161 if (! err)
162 err = __vm_map (__mach_task_self (),
163 &new_vm_addr, new_len, 0,
164 0, obj, offset,
165 old_len == 0, prot, max_prot, inherit);
168 if (!err)
169 /* Alright, can remove old mapping. */
170 __vm_deallocate (__mach_task_self (), begin, len);
172 out:
173 __mach_port_deallocate (__mach_task_self (), obj);
174 if (err)
175 return (void *) (uintptr_t) __hurd_fail (err);
176 return (void *) new_vm_addr;
179 libc_hidden_def (__mremap)
180 weak_alias (__mremap, mremap)