Page 1 of 1

weird issue slab freelist points to non canonical address

Posted: Thu Apr 25, 2024 4:30 am
by RayanMargham
Okay so, I'm working on a slab allocator and heres the thing. after enough allocations the slab allocator freelist starts pointing to non-canonical addresses? (2006000067E01B). I do not understand what im doing wrong? my slab allocator SHOULD work and this like makes no sense

this is my slab allocator code

Code: Select all

struct pmm_node
{
    struct pmm_node *next;
};
struct Header
{
    size_t size;
    struct Header *next;
    struct pmm_node *freelist;
};
struct cache
{
    struct Header *slabs;
    size_t size;
};

struct cache kmalloc_cache[8];

void init_cache(struct cache *cache, size_t size)
{
    char ****[64] = "";
    cache->size = size;
    void *page = pmm_alloc_singlep();
    struct Header *h = page + hhdm_request.response->offset;
    h->size = size;
    cache->slabs = (uint64_t)h;
    
    size_t obj_amount = (4096 - sizeof(struct Header)) / (size);
    write_color(ctx, "Slab: Creating Cache of Object Count: ", 0);
    write(ctx, itoa(****, obj_amount));
    write(ctx, " and Size: ");
    write(ctx, itoa(****, size));
    write(ctx, "\n");
    struct pmm_node *start = (uint64_t)h + sizeof(struct Header);
    h->freelist = start;
    struct pmm_node *prev = start;
    for (int i = 1; i < obj_amount; i++)
    {
        struct pmm_node *new = (uint64_t)start + (i * size);
        prev->next = new;
        prev = new;
    }
    write_color(ctx, "Slab: Cache Created!\n", 0);
}

struct Header *make_slab(size_t size)
{
    char ****[64] = "";
    void *page = pmm_alloc_singlep();
    struct Header *h = page + hhdm_request.response->offset;
    h->size = size;
    size_t obj_amount = (4096 - sizeof(struct Header)) / (size);
    struct pmm_node *start = (uint64_t)h + sizeof(struct Header);
    h->freelist = start;
    struct pmm_node *prev = start;
    for (int i = 1; i < obj_amount; i++)
    {
        struct pmm_node *new = (uint64_t)start + (i * size);
        prev->next = new;
        prev = new;
    }
    return h;
}
size_t next_pow2(size_t v)
{
    v--;
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    v++;
    return v;
}
static inline struct cache *search_for_bigenoughcache(size_t size)
{
    for (int i = 0; i < 8; i++)
    {
        if (kmalloc_cache[i].size >= size)
        {
            return &kmalloc_cache[i];
        }
    }
    return NULL;
}
void *slab_alloc(struct cache *cache)
{
    char ****[64] = "";
    struct Header *h = cache->slabs;
    while (h != NULL)
    {
        if (h->freelist != NULL)
        {
            struct pmm_node *him = h->freelist;
            serial_print("memory address: ");
            serial_print(itoah(****, (uint64_t)him));
            serial_print(" size: ");
            serial_print(itoa(****, h->size));
            serial_print("\n");
            h->freelist = him->next;
            
            return him;
        }
        else
        {
            if (h->next == NULL)
            {
                break;
            }
            h = h->next; // go to next header
        }
    }
    serial_print("um\n");
    // no slabs at all on this cache
    // allocate new slab
    struct Header *new_slab = make_slab(cache->size);
    h->next = new_slab;
    struct pmm_node *we = new_slab->freelist;
    new_slab->freelist = new_slab->freelist->next;
    serial_print(itoah(****, (uint64_t)new_slab->freelist));
    serial_print("making new slab\n");
    return we;
}
void free(void *addr)
{
    serial_print("before\n");
    char da[64] = "";
    if ((uint64_t)addr == 0)
    {
        return;
    }
    struct Header *h = (uint64_t)addr & ~0xFFF;
    struct cache *rightcache = NULL;
    for (int i = 0; i < 8; i++)
    {
        if (kmalloc_cache[i].size == h->size)
        {
            rightcache = &kmalloc_cache[i];
        }
    }
    if (rightcache == NULL)
    {
        return;
    }
    struct pmm_node *new = (uint64_t)addr;
    new->next = h->freelist;
    h->freelist = new;
    // put header into header freelist if not
    struct Header *prev = NULL;
    struct Header ***** = rightcache->slabs;
    while (**** != NULL)
    {
        if (**** == h)
        {
            return; // no need to do anything
        }
        else
        {
            prev = ****;
            **** = ****->next;
        }
    }
    // slab was fully used and now needs to be put back onto the header list
    serial_print("go back\n");
    prev->next = h;
    return;
}

void *kmalloc(size_t size)
{
    char ****[64] = "";
    struct cache *ca = search_for_bigenoughcache(next_pow2(size));
    if (ca == NULL)
    {
        serial_print("COUDNLT FIND FOR THIS SIZE BOZO!\n");
        return;
    }
    return slab_alloc(ca);
}
void init_slabs()
{
    init_cache(&kmalloc_cache[0], 8);
    init_cache(&kmalloc_cache[1], 16);
    init_cache(&kmalloc_cache[2], 32);
    init_cache(&kmalloc_cache[3], 64);
    init_cache(&kmalloc_cache[4], 128);
    init_cache(&kmalloc_cache[5], 256);
    init_cache(&kmalloc_cache[6], 512);
    init_cache(&kmalloc_cache[7], 1024);
}
i do NOT understand whats going on its causing me a lot of frustration

Re: weird issue slab freelist points to non canonical addres

Posted: Thu Apr 25, 2024 5:41 am
by iansjack
Have you tried running the code under a debugger? That should make the error fairly easy to find.

Re: weird issue slab freelist points to non canonical addres

Posted: Thu Apr 25, 2024 6:20 am
by RayanMargham
I have, I could not find the issue.

Re: weird issue slab freelist points to non canonical addres

Posted: Thu Apr 25, 2024 6:33 am
by iansjack
Look at the values returned by each allocation. Do they all look reasonable apart from the failing one. If so, set a breakpoint after the last correct allocation and single-step through the failing allocation.

My guess would be that it’s not just the last allocation that is unreasonable; it just the one that causes an error rather than returning an erroneous result.

You can run the allocator as a user process under your host OS (Linux or Windows - or Mac OS) to make the debugging easier.

Re: weird issue slab freelist points to non canonical addres

Posted: Thu Apr 25, 2024 6:37 am
by RayanMargham
I tried, i printed everything as well. the only thing weird is that one of the frees uacpi (the acpi library that is stress testing my allocator) calls points to a header that makes absolutely no sense but i have a check in my free function if the header makes like zero sense and it shouldnt really affect anything?

Re: weird issue slab freelist points to non canonical addres

Posted: Thu Apr 25, 2024 8:39 am
by iansjack
I find it difficult to follow your code as it is full of oddities like

Code: Select all

char ****[64] = "";
undeclared variables and functions

Code: Select all

write(ctx, itoa(****, obj_amount));
and, most confusingly, few comments describing the purpose and expected inputs and outputs of the various functions (what is the function

Code: Select all

size_t next_pow2(size_t v)
supposed to do?). Have you tested the functions individually to check that they provide the expected output for a given input?

As it is, the code won't compile so there's not much more I can add.

Re: weird issue slab freelist points to non canonical addres

Posted: Thu Apr 25, 2024 10:06 am
by RayanMargham
sorry this is just a snippit of my code.

I'll commit the code and upload to github

heres the repo: https://github.com/rayanmargham/NyaOS/tree/master

Re: weird issue slab freelist points to non canonical addres

Posted: Thu Apr 25, 2024 6:25 pm
by RayanMargham
Solved issue of slab by memzeroing memory

New Issue with my vmm tho... Causing memory corruption in the slab allocator

love my life!

Re: weird issue slab freelist points to non canonical addres

Posted: Sat Apr 27, 2024 2:37 am
by thewrongchristian
RayanMargham wrote:Solved issue of slab by memzeroing memory

New Issue with my vmm tho... Causing memory corruption in the slab allocator

love my life!
In debug builds (gated by the DEBUG macro), add code to initialise allocated memory to known sentinel values, that are unlikely to be interpreted as a valid pointer if dereferenced.

That way, any allocated memory that you happen to dereference that you have not initialised will likely result in a fault, and such memory is also easily identifiable as uninitialised just by inspection of the data.

I fill my free'd memory with 0xf8, and my newly allocated memory with 0x0a (not ideal, because that may dereference to a user address, better change that!)

The other thing that might be worth doing in debug mode is tagging allocations with source file and line information. That way, should you get repeatable corruption, you have a chance of finding the source of the corrupted allocation, which may give you clues as to where the corruption is happening.