Sanitize vcoreid from untrusted sources
[akaros.git] / kern / src / smallidpool.c
1 /* Copyright (c) 2015 Google Inc.
2  * Ron Minnich <rminnich@google.com>
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  *
5  * Trivial thread-safe ID pool for small sets of things (< 64K)
6  * implemented as a stack.
7  */
8
9 #include <smallidpool.h>
10 #include <kmalloc.h>
11 #include <atomic.h>
12 #include <stdio.h>
13 #include <assert.h>
14
15 struct u16_pool *create_u16_pool(unsigned int size)
16 {
17         struct u16_pool *id;
18
19         /* We could have size be a u16, but this might catch bugs where users
20          * tried to ask for more than 2^16 and had it succeed. */
21         if (size > MAX_U16_POOL_SZ)
22                 return NULL;
23         /* ids and check are alloced and aligned right after the id struct */
24         id = kmalloc(sizeof(*id) + sizeof(uint16_t) * size + size, MEM_WAIT);
25         spinlock_init_irqsave(&id->lock);
26         id->size = size;
27         id->ids = (void *)&id[1];
28         id->check = (void *)&id->ids[id->size];
29         for (int i = 0; i < id->size; i++) {
30                 id->ids[i] = i;
31                 // fe rhymes with "free"
32                 id->check[i] = 0xfe;
33         }
34         id->tos = 0;
35         return id;
36 }
37
38 /* Returns an unused u16, or -1 on failure (pool full or corruption).
39  *
40  * The invariant is that the stackpointer (TOS) will always point to the next
41  * slot that can be popped, if there are any.  All free slots will be below the
42  * TOS, ranging from indexes [0, TOS), where if TOS == 0, then there are no free
43  * slots to push.  The last valid slot is when TOS == size - 1. */
44 int get_u16(struct u16_pool *id)
45 {
46         uint16_t v;
47
48         spin_lock_irqsave(&id->lock);
49         if (id->tos == id->size) {
50                 spin_unlock_irqsave(&id->lock);
51                 return -1;
52         }
53         v = id->ids[id->tos++];
54         spin_unlock_irqsave(&id->lock);
55         /* v is ours, we can freely read and write its check field */
56         if (id->check[v] != 0xfe) {
57                 printk("BAD! %d is already allocated (0x%x)\n", v,
58                        id->check[v]);
59                 return -1;
60         }
61         id->check[v] = 0x5a;
62         return v;
63 }
64
65 void put_u16(struct u16_pool *id, int v)
66 {
67         /* we could check for if v is in range before dereferencing. */
68         if (id->check[v] != 0x5a) {
69                 printk("BAD! freeing non-allocated: %d(0x%x)\n", v,
70                        id->check[v]);
71                 return;
72         }
73         id->check[v] = 0xfe;
74         spin_lock_irqsave(&id->lock);
75         id->ids[--id->tos] = v;
76         spin_unlock_irqsave(&id->lock);
77 }