Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory comparison causes system halt

Tags:

c

linux-kernel

I am working on a kernel module and I need to compare two buffers to find out if they are equivalent. I am using the memcmp function defined in the Linux kernel to do so. My first buffer is like this:

cache_buffer = (unsigned char *)vmalloc(4097);
cache_buffer[4096] = '/0';

The second buffer is from a page using the page_address() function.

page = bio_page(bio);
kmap(page);
write_buffer = (char *)page_address(page);
kunmap(page);

I have printed the contents of both buffers before hand and not only to they print correctly, but they also have the same content. So next, I do this:

result = memcmp(write_buffer, cache_buffer, 2048); // only comparing up to 2048 positions

This causes the kernel to freeze up and I cannot figure out why. I checked the implementation of memcmp and saw nothing that would cause the freeze. Can anyone suggest a cause?

Here is the memcmp implementation:

int memcmp(const void *cs, const void *ct, size_t count)
{
    const unsigned char *su1, *su2;
    int res = 0;

    for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
           if ((res = *su1 - *su2) != 0)
                     break;
    return res;
}

EDIT: The function causing the freeze is memcmp. When I commented it out, everything worked. Also, when I did I memcmp as follows

memcmp(write_buffer, write_buffer, 2048); //comparing two write_buffers

Everything worked as well. Only when I throw the cache_buffer into the mix is when I get the error. Also, above is a simplification of my actual code. Here is the entire function:

static int compare_data(sector_t location, struct bio * bio, struct cache_c * dmc)
{
struct dm_io_region where;
unsigned long bits;
int segno;
struct bio_vec * bvec;
struct page * page;
unsigned char * cache_data;
    char * temp_data;
char * write_data;
int result, length, i;

cache_data = (unsigned char *)vmalloc((dmc->block_size * 512) + 1);

where.bdev = dmc->cache_dev->bdev;
where.count = dmc->block_size;
where.sector = location << dmc->block_shift;

printk(KERN_DEBUG "place: %llu\n", where.sector);

dm_io_sync_vm(1, &where, READ, cache_data, &bits, dmc);

length = 0;

bio_for_each_segment(bvec, bio, segno)
{
    if(segno == 0)
    {
        page = bio_page(bio);
        kmap(page);
        write_data = (char *)page_address(page);
        //kunmap(page);
        length += bvec->bv_len;
    }
    else
    {
        page = bio_page(bio);
        kmap(page);
        temp_data = strcat(write_data, (char *)page_address(page));
        //kunmap(page);
        write_data = temp_data;
        length += bvec->bv_len;
    }
}

printk(KERN_INFO "length: %u\n", length);

cache_data[dmc->block_size * 512] = '\0';

for(i = 0; i < 2048; i++)
{
    printk("%c", write_data[i]);
}

printk("\n");

for(i = 0; i < 2048; i++)
{
    printk("%c", cache_data[i]);
}

printk("\n");    

result = memcmp(write_data, cache_data, length);

return result;    
}

EDIT #2: Sorry guys. The problem was not memcmp. It was the result of memcmp. When ever it returned a positive or negative number, the function that called my function would play with some pointers, one of which was uninitialized. I don't know why I didn't realize it before. Thanks for trying to help though!

like image 732
Gregory-Turtle Avatar asked Nov 16 '25 10:11

Gregory-Turtle


1 Answers

I'm no kernel expert, but I would assume you need to keep this memory mapped while doing the comparison? In other words, don't call kunmap until after the memcmp is complete. I would presume that calling it before will result in write_buffer pointing to a page which is no longer mapped.

Taking your code in the other question, here is a rough attempt at incremental. Still needs some cleanup, I'm sure:

static int compare_data(sector_t location, struct bio * bio, struct cache_c * dmc)
{
  struct dm_io_region where;
  unsigned long bits;
  int segno;
  struct bio_vec * bvec;
  struct page * page;
  unsigned char * cache_data;
  char * temp_data;
  char * write_data;
  int length, i;

  int result = 0;
  size_t position = 0;
  size_t max_size = (dmc->block_size * 512) + 1;

  cache_data = (unsigned char *)vmalloc(max_size);

  where.bdev = dmc->cache_dev->bdev;
  where.count = dmc->block_size;
  where.sector = location << dmc->block_shift;

  printk(KERN_DEBUG "place: %llu\n", where.sector);

  dm_io_sync_vm(1, &where, READ, cache_data, &bits, dmc);

  bio_for_each_segment(bvec, bio, segno)
  {
    // Map the page into memory
    page = bio_page(bio);
    write_data = (char *)kmap(page);
    length = bvec->bv_len;

    // Make sure we don't go past the end
    if(position >= max_size)
      break;
    if(position + length > max_size)
      length = max_size - position;

    // Compare the data
    result = memcmp(write_data, cache_data + position, length);
    position += length;

    kunmap(page);

    // If the memory is not equal, bail out now and return the result
    if(result != 0)
      break;
  }

  cache_data[dmc->block_size * 512] = '\0';

  return result;    
}
like image 126
Dark Falcon Avatar answered Nov 18 '25 23:11

Dark Falcon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!