1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2016 Joyent, Inc.
26 * Copyright 2024 Oxide Computer Company
27 */
28
29 /* Copyright (c) 1988 AT&T */
30 /* All Rights Reserved */
31
32 #include "lint.h"
33 #include "mallint.h"
34 #include "mtlib.h"
35 #include <stdalign.h>
36
37 #define _misaligned(p) ((unsigned)(p) & 3)
38 /* 4-byte "word" alignment is considered ok in LP64 */
39 #define _nextblk(p, size) ((TREE *)((uintptr_t)(p) + (size)))
40
41 /*
42 * memalign(align, nbytes)
43 *
44 * Description:
45 * Returns a block of specified size on a specified alignment boundary.
46 *
47 * Algorithm:
48 * Malloc enough to ensure that a block can be aligned correctly.
49 * Find the alignment point and return the fragments
50 * before and after the block.
51 *
52 * Errors:
53 * Returns NULL and sets errno as follows:
54 * [EINVAL]
55 * if nbytes = 0,
56 * or if alignment is misaligned,
57 * or if the heap has been detectably corrupted.
58 * [ENOMEM]
59 * if the requested memory could not be allocated.
60 */
61
62 void *
memalign(size_t align,size_t nbytes)63 memalign(size_t align, size_t nbytes)
64 {
65 size_t reqsize; /* Num of bytes to get from malloc() */
66 TREE *p; /* Ptr returned from malloc() */
67 TREE *blk; /* For addressing fragment blocks */
68 size_t blksize; /* Current (shrinking) block size */
69 TREE *alignedp; /* Ptr to properly aligned boundary */
70 TREE *aligned_blk; /* The block to be returned */
71 size_t frag_size; /* size of fragments fore and aft */
72 size_t x;
73
74 if (!primary_link_map) {
75 errno = ENOTSUP;
76 return (NULL);
77 }
78
79 /*
80 * check for valid size and alignment parameters
81 * MAX_ALIGN check prevents overflow in later calculation.
82 */
83 if (nbytes == 0 || _misaligned(align) || align == 0 ||
84 align > MAX_ALIGN) {
85 errno = EINVAL;
86 return (NULL);
87 }
88
89 /*
90 * Malloc enough memory to guarantee that the result can be
91 * aligned correctly. The worst case is when malloc returns
92 * a block so close to the next alignment boundary that a
93 * fragment of minimum size cannot be created. In order to
94 * make sure we can handle this, we need to force the
95 * alignment to be at least as large as the minimum frag size
96 * (MINSIZE + WORDSIZE).
97 */
98
99 /* check for size that could overflow calculations */
100 if (nbytes > MAX_MALLOC) {
101 errno = ENOMEM;
102 return (NULL);
103 }
104 ROUND(nbytes);
105 if (nbytes < MINSIZE)
106 nbytes = MINSIZE;
107 ROUND(align);
108 while (align < MINSIZE + WORDSIZE)
109 align <<= 1;
110 reqsize = nbytes + align + (MINSIZE + WORDSIZE);
111
112 /* check for overflow */
113 if (reqsize < nbytes) {
114 errno = ENOMEM;
115 return (NULL);
116 }
117
118 p = (TREE *)malloc(reqsize);
119 if (p == (TREE *)NULL) {
120 /* malloc sets errno */
121 return (NULL);
122 }
123 (void) mutex_lock(&libc_malloc_lock);
124
125 /*
126 * get size of the entire block (overhead and all)
127 */
128 blk = BLOCK(p); /* back up to get length word */
129 blksize = SIZE(blk);
130 CLRBITS01(blksize);
131
132 /*
133 * locate the proper alignment boundary within the block.
134 */
135 x = (size_t)p;
136 if (x % align != 0)
137 x += align - (x % align);
138 alignedp = (TREE *)x;
139 aligned_blk = BLOCK(alignedp);
140
141 /*
142 * Check out the space to the left of the alignment
143 * boundary, and split off a fragment if necessary.
144 */
145 frag_size = (size_t)aligned_blk - (size_t)blk;
146 if (frag_size != 0) {
147 /*
148 * Create a fragment to the left of the aligned block.
149 */
150 if (frag_size < MINSIZE + WORDSIZE) {
151 /*
152 * Not enough space. So make the split
153 * at the other end of the alignment unit.
154 * We know this yields enough space, because
155 * we forced align >= MINSIZE + WORDSIZE above.
156 */
157 frag_size += align;
158 aligned_blk = _nextblk(aligned_blk, align);
159 }
160 blksize -= frag_size;
161 SIZE(aligned_blk) = blksize | BIT0;
162 frag_size -= WORDSIZE;
163 SIZE(blk) = frag_size | BIT0 | ISBIT1(SIZE(blk));
164 _free_unlocked(DATA(blk));
165 }
166
167 /*
168 * Is there a (sufficiently large) fragment to the
169 * right of the aligned block?
170 */
171 frag_size = blksize - nbytes;
172 if (frag_size >= MINSIZE + WORDSIZE) {
173 /*
174 * split and free a fragment on the right
175 */
176 blksize = SIZE(aligned_blk);
177 SIZE(aligned_blk) = nbytes;
178 blk = NEXT(aligned_blk);
179 SETOLD01(SIZE(aligned_blk), blksize);
180 frag_size -= WORDSIZE;
181 SIZE(blk) = frag_size | BIT0;
182 _free_unlocked(DATA(blk));
183 }
184 (void) mutex_unlock(&libc_malloc_lock);
185 return (DATA(aligned_blk));
186 }
187
188 /*
189 * This is the ISO/IEC C11 version of memalign. We have kept it as a separate
190 * function, but it is almost the same thing. aligned_alloc allows any alignment
191 * that is a fundamental alignment of one of the data types. However,
192 * aligned_alloc (like malloc) is required to ensure that the alignment is good
193 * for any of the base data objects. Our expectation is that memalign guarantees
194 * this. To work with memalign(), we round up any smaller alignments to the
195 * alignment of a pointer which is generally the requirement of memalign(3C).
196 *
197 * Note, aligned_alloc is implemented in terms of just calling memalign this way
198 * so that way interposing libraries can just interpose on that.
199 */
200 void *
aligned_alloc(size_t align,size_t size)201 aligned_alloc(size_t align, size_t size)
202 {
203 if (align == 0 || (align & (align - 1)) != 0) {
204 errno = EINVAL;
205 return (NULL);
206 }
207
208 if (align < alignof (uintptr_t))
209 align = alignof (uintptr_t);
210
211 return (memalign(align, size));
212 }
213