Sanitize vcoreid from untrusted sources
[akaros.git] / kern / src / kmalloc.c
1 /* Copyright (c) 2009 The Regents of the University of California.
2  * See the COPYRIGHT files at the top of this source tree for full
3  * license information.
4  *
5  * Barret Rhoden <brho@cs.berkeley.edu>
6  * Kevin Klues <klueska@cs.berkeley.edu>
7  */
8 #include <ros/common.h>
9 #include <error.h>
10 #include <pmap.h>
11 #include <kmalloc.h>
12 #include <stdio.h>
13 #include <slab.h>
14 #include <assert.h>
15
16 #define kmallocdebug(args...)  //printk(args)
17
18 //List of physical pages used by kmalloc
19 static spinlock_t pages_list_lock = SPINLOCK_INITIALIZER;
20 static page_list_t pages_list;
21
22 struct kmem_cache *kmalloc_caches[NUM_KMALLOC_CACHES];
23
24 static void __kfree_release(struct kref *kref);
25
26 void kmalloc_init(void)
27 {
28         char kc_name[KMC_NAME_SZ];
29
30         /* we want at least a 16 byte alignment of the tag so that the bufs
31          * kmalloc returns are 16 byte aligned.  we used to check the actual
32          * size == 16, since we adjusted the KMALLOC_SMALLEST based on that. */
33         static_assert(ALIGNED(sizeof(struct kmalloc_tag), 16));
34         /* build caches of common sizes.  this size will later include the tag
35          * and the actual returned buffer. */
36         size_t ksize = KMALLOC_SMALLEST;
37
38         for (int i = 0; i < NUM_KMALLOC_CACHES; i++) {
39                 snprintf(kc_name, KMC_NAME_SZ, "kmalloc_%d", ksize);
40                 kmalloc_caches[i] = kmem_cache_create(kc_name, ksize,
41                                                       KMALLOC_ALIGNMENT, 0,
42                                                       NULL, 0, 0, NULL);
43                 ksize <<= 1;
44         }
45 }
46
47 void *kmalloc(size_t size, int flags)
48 {
49         // reserve space for bookkeeping and preserve alignment
50         size_t ksize = size + sizeof(struct kmalloc_tag);
51         void *buf;
52         int cache_id;
53         // determine cache to pull from
54         if (ksize <= KMALLOC_SMALLEST)
55                 cache_id = 0;
56         else
57                 cache_id = LOG2_UP(ksize) - LOG2_UP(KMALLOC_SMALLEST);
58         // if we don't have a cache to handle it, alloc cont pages
59         if (cache_id >= NUM_KMALLOC_CACHES) {
60                 /* The arena allocator will round up too, but we want to know in
61                  * advance so that krealloc can avoid extra allocations. */
62                 size_t amt_alloc = ROUNDUP(size + sizeof(struct kmalloc_tag),
63                                            PGSIZE);
64
65                 buf = kpages_alloc(amt_alloc, flags);
66                 if (!buf)
67                         panic("Kmalloc failed!  Handle me!");
68                 // fill in the kmalloc tag
69                 struct kmalloc_tag *tag = buf;
70                 tag->flags = KMALLOC_TAG_PAGES;
71                 tag->amt_alloc = amt_alloc;
72                 tag->canary = KMALLOC_CANARY;
73                 kref_init(&tag->kref, __kfree_release, 1);
74                 return buf + sizeof(struct kmalloc_tag);
75         }
76         // else, alloc from the appropriate cache
77         buf = kmem_cache_alloc(kmalloc_caches[cache_id], flags);
78         if (!buf)
79                 panic("Kmalloc failed!  Handle me!");
80         // store a pointer to the buffers kmem_cache in it's bookkeeping space
81         struct kmalloc_tag *tag = buf;
82         tag->flags = KMALLOC_TAG_CACHE;
83         tag->my_cache = kmalloc_caches[cache_id];
84         tag->canary = KMALLOC_CANARY;
85         kref_init(&tag->kref, __kfree_release, 1);
86         return buf + sizeof(struct kmalloc_tag);
87 }
88
89 void *kzmalloc(size_t size, int flags)
90 {
91         void *v = kmalloc(size, flags);
92
93         if (!v)
94                 return v;
95         memset(v, 0, size);
96         return v;
97 }
98
99 void *kmalloc_align(size_t size, int flags, size_t align)
100 {
101         void *addr, *retaddr;
102         int *tag_flags, offset;
103
104         /* alignment requests must be a multiple of long, even though we only
105          * need int in the current code. */
106         assert(ALIGNED(align, sizeof(long)));
107         /* must fit in the space reserved for the offset amount, which is at
108          * most 'align'. */
109         assert(align < (1 << (32 - KMALLOC_ALIGN_SHIFT)));
110         assert(IS_PWR2(align));
111         addr = kmalloc(size + align, flags);
112         if (!addr)
113                 return 0;
114         if (ALIGNED(addr, align))
115                 return addr;
116         retaddr = ROUNDUP(addr, align);
117         offset = retaddr - addr;
118         assert(offset < align);
119         /* we might not have room for a full tag.  we might have only 8 bytes.
120          * but we'll at least have room for the flags part. */
121         tag_flags = (int*)(retaddr - sizeof(int));
122         *tag_flags = (offset << KMALLOC_ALIGN_SHIFT) | KMALLOC_TAG_UNALIGN;
123         return retaddr;
124 }
125
126 void *kzmalloc_align(size_t size, int flags, size_t align)
127 {
128         void *v = kmalloc_align(size, flags, align);
129
130         if (!v)
131                 return v;
132         memset(v, 0, size);
133         return v;
134 }
135
136 static struct kmalloc_tag *__get_km_tag(void *buf)
137 {
138         struct kmalloc_tag *tag =
139                 (struct kmalloc_tag*)(buf - sizeof(struct kmalloc_tag));
140         if (tag->canary != KMALLOC_CANARY){
141                 printk("kmalloc bad canary: %08lx@%p, buf %p, expected %08lx\n",
142                        tag->canary, &tag->canary, buf, KMALLOC_CANARY);
143                 hexdump((void *)(buf - sizeof(struct kmalloc_tag)), 256);
144                 panic("Bad canary");
145         }
146         return tag;
147 }
148
149 /* If we kmalloc_aligned, the buf we got back (and are now trying to perform
150  * some operation on) might not be the original, underlying, unaligned buf.
151  *
152  * This returns the underlying, unaligned buf, or 0 if the buf was not realigned
153  * in the first place. */
154 static void *__get_unaligned_orig_buf(void *buf)
155 {
156         int *tag_flags = (int*)(buf - sizeof(int));
157
158         if ((*tag_flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_UNALIGN)
159                 return (buf - (*tag_flags >> KMALLOC_ALIGN_SHIFT));
160         else
161                 return 0;
162 }
163
164 void *krealloc(void* buf, size_t size, int flags)
165 {
166         void *nbuf;
167         size_t osize = 0;
168         struct kmalloc_tag *tag;
169
170         if (buf){
171                 if (__get_unaligned_orig_buf(buf))
172                         panic("krealloc of a kmalloc_align not supported");
173                 tag = __get_km_tag(buf);
174                 /* whatever we got from either a slab or the page allocator is
175                  * meant for both the buf+size as well as the kmalloc tag */
176                 if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_CACHE) {
177                         osize = tag->my_cache->obj_size
178                                 - sizeof(struct kmalloc_tag);
179                 } else if ((tag->flags & KMALLOC_FLAG_MASK)
180                            == KMALLOC_TAG_PAGES) {
181                         osize = tag->amt_alloc - sizeof(struct kmalloc_tag);
182                 } else {
183                         panic("Probably a bad tag, flags %p\n", tag->flags);
184                 }
185                 if (osize >= size)
186                         return buf;
187         }
188
189         nbuf = kmalloc(size, flags);
190
191         /* would be more interesting to user error(...) here. */
192         /* but in any event, NEVER destroy buf! */
193         if (! nbuf)
194                 return NULL;
195
196         if (osize)
197                 memmove(nbuf, buf, osize);
198
199         if (buf)
200                 kfree(buf);
201
202         return nbuf;
203 }
204
205 /* Grabs a reference on a buffer.  Release with kfree().
206  *
207  * Note that a krealloc on a buffer with ref > 1 that needs a new, underlying
208  * buffer will result in two buffers existing.  In this case, the krealloc is a
209  * kmalloc and a kfree, but that kfree does not completely free since the
210  * original ref > 1. */
211 void kmalloc_incref(void *buf)
212 {
213         void *orig_buf = __get_unaligned_orig_buf(buf);
214
215         buf = orig_buf ? orig_buf : buf;
216         /* if we want a smaller tag, we can extract the code from kref and
217          * manually set the release method in kfree. */
218         kref_get(&__get_km_tag(buf)->kref, 1);
219 }
220
221 int kmalloc_refcnt(void *buf)
222 {
223         void *orig_buf = __get_unaligned_orig_buf(buf);
224
225         buf = orig_buf ? orig_buf : buf;
226         return kref_refcnt(&__get_km_tag(buf)->kref);
227 }
228
229 static void __kfree_release(struct kref *kref)
230 {
231         struct kmalloc_tag *tag = container_of(kref, struct kmalloc_tag, kref);
232
233         if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_CACHE)
234                 kmem_cache_free(tag->my_cache, tag);
235         else if ((tag->flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_PAGES)
236                 kpages_free(tag, tag->amt_alloc);
237         else
238                 panic("Bad flag 0x%x in %s", tag->flags, __FUNCTION__);
239 }
240
241 void kfree(void *buf)
242 {
243         void *orig_buf;
244         if (buf == NULL)
245                 return;
246         orig_buf = __get_unaligned_orig_buf(buf);
247         buf = orig_buf ? orig_buf : buf;
248         kref_put(&__get_km_tag(buf)->kref);
249 }
250
251 void kmalloc_canary_check(char *str)
252 {
253         struct kmalloc_tag *tag;
254
255         if (!debug_canary)
256                 return;
257         tag = (struct kmalloc_tag*)(debug_canary - sizeof(struct kmalloc_tag));
258         if (tag->canary != KMALLOC_CANARY)
259                 panic("\t\t KMALLOC CANARY CHECK FAILED %s\n", str);
260 }
261
262 struct sized_alloc *sized_kzmalloc(size_t size, int flags)
263 {
264         struct sized_alloc *sza;
265
266         sza = kzmalloc(sizeof(struct sized_alloc) + size, flags);
267         if (!sza)
268                 return NULL;
269         sza->buf = sza + 1;
270         sza->size = size;
271         return sza;
272 }
273
274 void sza_printf(struct sized_alloc *sza, const char *fmt, ...)
275 {
276         va_list ap;
277
278         va_start(ap, fmt);
279         sza->sofar += vsnprintf(sza->buf + sza->sofar, sza->size - sza->sofar,
280                                 fmt, ap);
281         va_end(ap);
282 }