Dynamic Array in Linux Kernel Module with kmalloc - c

I am writing a small program which prints the time it took to allocate the memory.
I want to free the memory later so I want to save it in a array, but since I can loop it as many times as I want I want to make a Dynamic array to store all the addresses from the allocated Memory. This is my
init code:
static __init int init_kmalloc(void)
{
int size = sizeof(char*);
char *buffer = kmalloc_array(loop_cnt, size, GFP_KERNEL);
unsigned int i = 0;
printk(KERN_DEBUG "Allocating %d times the memory size of %d\n", loop_cnt, alloc_size);
while(i < loop_cnt)
{
unsigned long long start;
unsigned long long stop;
start = get_rdtsc();
buffer[i] = kmalloc(alloc_size, GFP_KERNEL);
stop = get_rdtsc();
printk(KERN_DEBUG "%i: It took %lld ticks to allocate the memory\n", i, stop - start);
i++;
}
while(i > 0)
{
kfree(buffer[i]);
printk(KERN_DEBUG "Cleared\n");
i--;
}
return 0;
}
I Always get these errors:

What is wrong is that you choose char as the element of the array by selecting char* for the type of buffer. The elements of the array should pointers, so buffer should be a pointer to pointers like this (for example):
char **buffer = kmalloc_array(loop_cnt, size, GFP_KERNEL);
Use two *s, not one.

Related

memory corruption when manipulating a long string

I am writing a program to print out any line input that is longer than 3.
It works for some fairly long input lines, but for the string that is too long, I got a error message of memory corruption
*** Error in `./print-80': malloc(): memory corruption (fast): 0x00000000022ff030 ***
I don't know where the error is from. Can anyone explain me why there is the error and how to fix it?
Below is the program
#include <stdio.h>
#include <stdlib.h>
#define LIMIT 3
#define LEAST_LENGTH 3
//function prototype
void copy(char* from, char* to);
int getline(char* s, int capacity);
int increase_capacity(char* s, int capacity);
int main(void)
{
int length, i;
char* line = calloc(LIMIT, sizeof(char));
while ((length = getline(line, LIMIT)) > 0)
{
if (length > LEAST_LENGTH)
printf("Output: %s\n", line);
//reset the line
for (i = 0; i < length; i++)
*(line + i) = 0;
}
free(line);
return 0;
}
int getline(char* line, int capacity)
{
int c, length;
length = 0;
while ((c = getchar()) != EOF && c != '\n')
{
if (length > (capacity - 1))
{
capacity = increase_capacity(line, capacity);
printf("Address of line after increasing cap: %p\n", line);
}
line[length++] = c;
}
if (c == '\n')
line[length++] = '\0';
return length;
}
int increase_capacity(char* s, int capacity)
{
int i;
capacity *= 2;
char *new_s = calloc(capacity, sizeof(char));
copy(s, new_s);
s = new_s;
free(new_s);
return capacity;
}
void copy(char* from, char* to)
{
int i = 0;
while ((to[i] = from[i]) != '\0')
++i;
}
Your increase_capacity function can change the address at which the data is stored. But it doesn't return this information to its caller. So getline will write to the old buffer address. Similarly, main has no way to get the new address, so it will access the old address and free a block that may already be freed.
Also, your increase_capacity function allocates memory to hold the data and then frees that memory. That leaves no place to hold the data!
int increase_capacity(char* s, int capacity)
{
int i;
capacity *= 2;
char *new_s = calloc(capacity, sizeof(char)); // allocate a larger block
copy(s, new_s); // copy the data into the larger block
s = new_s; // stash a pointer to the larger block in a local
free(new_s); // free the block?!
return capacity;
}
So we allocate a new block, copy the data into it, and then free it. That makes no sense, we need to keep the larger block since that's the whole point of a function to increase capacity. We also don't return the address of the new block, so even if we didn't free it, no other code could access it and we'd just wind up leaking it. Double oops.
I suggest you create a struct that holds both the pointer to the block and its size. Pass a pointer to that struct to functions like increase_capacity so it can modify the pointer and the size in the structure and callers can see the changes.

pthreads memcpy and realloc in C

I am trying my luck with pthreads for a problem -
I have two questions about that and will add some excerpts from the code:
I have a struct, which contains an input array and one as output (or result) array. Where do I have to allocate the memory for this result, within the main-function or within the thread-function?
These results shall be merged into one big result array using realloc and memcpy. I think I am using memcpy and realloc not correctly in this case, or do I?
thread-Function:
struct thread_input {
unsigned char *buf;
unsigned char *result;
int start;
int end;
int tid;
};
void* thread_function(void* arg){
struct thread_input *param = (struct thread_input*)arg;
param->result = (unsigned char*)malloc((1+(param->end-param->start))*sizeof(unsigned char));
for(int i = param->start; i <= param->end; i++){
param->result[i] = some_function(param->buf[i]);
printf("%d %d\n", param->tid, param->result[i]);
}
return NULL;
}
So the some_function is working with every unsigned char and returns it to the param->result array.
This shall be merged here (where n_elemts is the number calculated in every thread):
unsigned char* arr1 = (unsigned char*)malloc(n_elements*sizeof(unsigned char));
for (int t = 0; t < n_threads; t++){
pthread_join(threads[t], NULL);
memcpy(arr1, thread_args[t].result, n_elements);
if(t<n_threads-1){
arr1 = (unsigned char*)realloc(arr1, (t+1)*n_elements*sizeof(unsigned char));
}
}
print_function(arr1, fileSize);
The problem given by the compiler is located at the realloc and called "invalid read of size 1", I suppose it is due to a wrong file_size, or array length, after the realloc. Plus the allocation of the param->result that I could not figure out completely.
Thanks for lookin at these excerpts and for any comment! Hope this question was asked in concise enough way, if not bombard me with critic.

Partition a 1D char* into 2D char**

There are a lot of questions about converting a 2D array into a 1D array, but I am attempting just the opposite. I'm trying to partition a string into substrings of constant length and house them in a 2D array. Each row of this 2D matrix should contain a substring of the initial string, and, if each row were to be read in succession and concatenated, the initial string should be reproduced.
I nearly have it working, but for some reason I am losing the first substring (partitions[0] -- length 8*blockSize) of the initial string (bin):
int main (void){
char* bin = "00011101010000100001111101001101000010110000111100000010000111110100111100010011010011100011110000011010";
int blockSize = 2; // block size in bytes
int numBlocks = strlen(bin)/(8*blockSize); // number of block to analyze
char** partitions = (char**)malloc((numBlocks+1)*sizeof(char)); // break text into block
for(int i = 0; i<numBlocks;++i){
partitions[i] = (char*)malloc((8*blockSize+1)*sizeof(char));
memcpy(partitions[i],&bin[8*i*blockSize],8*blockSize);
partitions[i][8*blockSize] = '\0';
printf("Printing partitions[%d]: %s\n", i, partitions[i]);
}
for(int j=0; j<numBlocks;++j)
printf("Printing partitions[%d]: %s\n", j,partitions[j]);
return 0;
}
The output is as follows:
Printing partitions[0]: 0001110101000010
Printing partitions[1]: 0001111101001101
Printing partitions[2]: 0000101100001111
Printing partitions[3]: 0000001000011111
Printing partitions[4]: 0100111100010011
Printing partitions[5]: 0100111000111100
Printing partitions[0]: Hj
Printing partitions[1]: 0001111101001101
Printing partitions[2]: 0000101100001111
Printing partitions[3]: 0000001000011111
Printing partitions[4]: 0100111100010011
Printing partitions[5]: 0100111000111100
The construction of partitions in the first for loop is successful. After construction at read out, the string at partitions[0] contains garbage values. Can anyone offer some insight?
int numBlocks = strlen(bin)/(8*blockSize); // number of block to analyze
char** partitions = (char**)malloc((numBlocks+1)*sizeof(char)); // break text into block
for(int i = 0; i<numBlocks;++i){
partitions[i] = (char*)malloc((8*blockSize+1)*sizeof(char));
memcpy(partitions[i],&bin[8*i*blockSize],8*blockSize);
partitions[i][8*blockSize] = '\0';
printf("Printing partitions[%d]: %s\n", i, partitions[i]);
}
This all looks suspicious to me; it's far too complex for the task, making it a prime suspect for errors.
For reasons explained in answers to this question, void * pointers which are returned by malloc and other functions shouldn't be casted.
There's no need to multiply by 1 (sizeof (char) is always 1 in C). In fact, in your first call to malloc you should be multiplying by sizeof (char *) (or better yet, sizeof *partitions, as in the example below), since that's the size of the type of element that partitions points at.
malloc might return NULL, resulting in undefined behaviour when you attempt to assign into the location it points at.
Anything else (i.e. everything that isn't NULL) that malloc, calloc or realloc returns will need to be freed when no longer in use, or else tools such as valgrind (a leak detection program, useful for people who habitually forget to free allocated objects and thus cause memory leaks) will report false positives and lose part of their usefulness.
numBlocks, i, or anything else that's for counting elements of an array, should be declared as a size_t to follow standard convention (e.g. check the strlen manual, synopsis section to see how strlen is declared, noting the type of the return value is size_t). Negative values caused by overflows here will obviously cause the program to misbehave.
I gather you've yet to think about any excess beyond the last group of 8 characters... This shouldn't be difficult to incorporate.
I suggest using a single allocation, such as:
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BLOCK_SIZE 8
int main(void) {
char const *bin = "00011101010000100001111101001101000010110000111100000010000111110100111100010011010011100011110000011010";
size_t bin_length = strlen(bin),
block_count = (bin_length / BLOCK_SIZE)
+ (bin_length % BLOCK_SIZE > 0); // excess as per point 6 above
char (*block)[BLOCK_SIZE + 1] = malloc(block_count * sizeof *block);
if (!block) { exit(EXIT_FAILURE); }
for (size_t x = 0; x < block_count; x++) {
snprintf(block[x], BLOCK_SIZE + 1, "%s", bin + x * BLOCK_SIZE);
printf("Printing partitions[%zu]: %s\n", x, block[x]);
}
for (size_t x = 0; x < block_count; x++) {
printf("Printing partitions[%zu]: %s\n", x, block[x]);
}
free(block);
exit(0);
}
Their are a few problems with your code.
You are allocating **partitions incorrectly.
Instead of:
char** partitions = (char**)malloc((numBlocks+1)*sizeof(char)); /* dont need +1, as numblocks is enough space. */
You need to allocate space for char* pointers, not char characters.
instead, this needs to be:
char** partitions = malloc((numBlocks+1)*sizeof(char*));
Also read Why not to cast result of malloc(), as it is not needed in C.
malloc() needs to be checked everytime, as it can return NULL when unsuccessful.
Once finished with the space allocated, it is always good to free() memory previously requested by malloc(). It is important to do this at some point in the program.
Here is some code which shows this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BLOCKSIZE 2
#define BLOCK_MULTIPLIER 8
int main(void) {
const char *bin = "00011101010000100001111101001101000010110000111100000010000111110100111100010011010011100011110000011010";
const size_t blocksize = BLOCKSIZE;
const size_t multiplier = BLOCK_MULTIPLIER;
const size_t numblocks = strlen(bin)/(multiplier * blocksize);
const size_t numbytes = multiplier * blocksize;
char **partitions = malloc(numblocks * sizeof(*partitions));
if (partitions == NULL) {
printf("Cannot allocate %zu spaces\n", numblocks);
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < numblocks; i++) {
partitions[i] = malloc(numbytes+1);
if (partitions[i] == NULL) {
printf("Cannot allocate %zu bytes for pointer\n", numbytes+1);
exit(EXIT_FAILURE);
}
memcpy(partitions[i], &bin[numbytes * i], numbytes);
partitions[i][numbytes] = '\0';
printf("Printing partitions[%zu]: %s\n", i, partitions[i]);
}
printf("\n");
for(size_t j = 0; j < numblocks; j++) {
printf("Printing partitions[%zu]: %s\n", j,partitions[j]);
free(partitions[j]);
partitions[j] = NULL;
}
free(partitions);
partitions = NULL;
return 0;
}
Which outputs non-garbage values:
Printing partitions[0]: 0001110101000010
Printing partitions[1]: 0001111101001101
Printing partitions[2]: 0000101100001111
Printing partitions[3]: 0000001000011111
Printing partitions[4]: 0100111100010011
Printing partitions[5]: 0100111000111100
Printing partitions[0]: 0001110101000010
Printing partitions[1]: 0001111101001101
Printing partitions[2]: 0000101100001111
Printing partitions[3]: 0000001000011111
Printing partitions[4]: 0100111100010011
Printing partitions[5]: 0100111000111100

C programming to increase the size of a string

So I wrote this code but it gives me the same answer everytime. I am increasing the memory allocated to the pointer in steps of 4 and then print the value.
#include <stdio.h>
int main(void) {
int n=0;
char *name = "hello";
scanf("%d",&n);
for(int i =0; i<n;i++){
name += sizeof(int);
printf("%d \n", (sizeof(&name)));
}
return 0;
}
can someone help me? I don't know whats wrong here. I don't need a different code, I just want to understand what's wrong with this.
Try the following, error checking was left out for clarity:
#include <stdio.h>
int main(void)
{
int n=0;
char *name = null;
scanf("%d",&n);
for(int i=0; i<n;i++)
{
char *buffer = null;
//allocate/reallocate the buffer. increases by 4 bytes every iteration
buffer = (char*) realloc(name, (i+1)*4);
name = buffer;
printf("%d \n", (sizeof(&name)));
}
//release the memory used by the buffer
free(name);
return 0;
}
Here are some explanations of what is happening.
#include <stdio.h>
int main(void) {
int n=0;
// this does not actually allocate any memory. It sets the POINTER name to point (like an arrow) to a read-only block that contains "hello"
char *name = "hello";
// string literals generally come in fixed read-only memory
scanf("%d",&n);
for(int i =0; i<n;i++){
// this causes the pointer memory address to be incremented by sizeof(int) (typically 4)
// after the first increment if it will point to a string "o" (incremented by 4 characters)
// after the second increment it will point to some undefined memory behind "hello" in your virtual address space and will have undefined behaviour when accessed
name += sizeof(int);
// sizeof(&name) will give you the size of a char **. Pointer to a character pointer.
// Wich is the same size as all pointers.
// = sizeof(void *) = 8 for 64-bit systems, 4 for 32-bit systems
printf("%d \n", (sizeof(&name)));
}
return 0;
}
This is the way to do it:
#include <stdio.h>
int main(void) {
int n=0;
// allocate 10 bytes of memory and assign that memory address to name
char *name = malloc(10);
// the size of that memory needs to be kept in a separate variable
size_t name_length = 10;
// copy the desired contents into that memory
memcpy(name, "hello", sizeof("hello"));
scanf("%d",&n);
for(int i =0; i<n;i++){
// reallocate the memory into something with sizeof(int) more bytes
void * tmp = realloc(name, name_length += sizeof(int));
// this can fail
if (tmp) {
name = tmp;
} else {
perror("realloc");
exit(-1);
}
printf("%d \n", name_length);
}
return 0;
}
You have not allocated any memory for the pointer at all in the code you provide. You will have to deal with dynamic memory if you want to change the size of the allocated chunk. You will have to initially use malloc and then use realloc to allocate more memory on each step.
Let's step through your code one by one:
char *name = "hello";
this create an array of chars 'h','e','l','l','o',0 and assignes the memory address of the first character to name
for(int i =0; i<n;i++){
name += sizeof(int);
printf("%d \n", (sizeof(&name)));
}
here you add to the name pointer the size of int, which increments this pointer by 4 each pass.
Since this is a char pointer, the pointer is incremented by 4 bytes - since sizeof(int) == 4
You cannot increase the size of your hello char array, since it is not a dynamic array.
If you wish to be able to resize the string, you should malloc and copy the chars to the bigger array.

Copying a file line by line into a char array with strncpy

So i am trying to read a text file line by line and save each line into a char array.
From my printout in the loop I can tell it is counting the lines and the number of characters per line properly but I am having problems with strncpy. When I try to print the data array it only displays 2 strange characters. I have never worked with strncpy so I feel my issue may have something to do with null-termination.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[])
{
FILE *f = fopen("/home/tgarvin/yes", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos); fread(bytes, pos, 1, f);
int i = 0;
int counter = 0;
char* data[counter];
int length;
int len=strlen(data);
int start = 0;
int end = 0;
for(; i<pos; i++)
{
if(*(bytes+i)=='\n'){
end = i;
length=end-start;
data[counter]=(char*)malloc(sizeof(char)*(length)+1);
strncpy(data[counter], bytes+start, length);
printf("%d\n", counter);
printf("%d\n", length);
start=end+1;
counter=counter+1;
}
}
printf("%s\n", data);
return 0;
}
Your "data[]" array is declared as an array of pointers to characters of size 0. When you assign pointers to it there is no space for them. This could cause no end of trouble.
The simplest fix would be to make a pass over the array to determine the number of lines and then do something like "char **data = malloc(number_of_lines * sizeof(char *))". Then doing assignments of "data[counter]" will work.
You're right that strncpy() is a problem -- it won't '\0' terminate the string if it copies the maximum number of bytes. After the strncpy() add "data[counter][length ] = '\0';"
The printf() at the end is wrong. To print all the lines use "for (i = 0; i < counter; i++) printf("%s\n", data[counter]);"
Several instances of bad juju, the most pertinent one being:
int counter = 0;
char* data[counter];
You've just declared data as a variable-length array with zero elements. Despite their name, VLAs are not truly variable; you cannot change the length of the array after allocating it. So when you execute the lines
data[counter]=(char*)malloc(sizeof(char)*(length)+1);
strncpy(data[counter], bytes+start, length);
data[counter] is referring to memory you don't own, so you're invoking undefined behavior.
Since you don't know how many lines you're reading from the file beforehand, you need to create a structure that can be extended dynamically. Here's an example:
/**
* Initial allocation of data array (array of pointer to char)
*/
char **dataAlloc(size_t initialSize)
{
char **data= malloc(sizeof *data * initialSize);
return data;
}
/**
* Extend data array; each extension doubles the length
* of the array. If the extension succeeds, the function
* will return 1; if not, the function returns 0, and the
* values of data and length are unchanged.
*/
int dataExtend(char ***data, size_t *length)
{
int r = 0;
char **tmp = realloc(*data, sizeof *tmp * 2 * *length);
if (tmp)
{
*length= 2 * *length;
*data = tmp;
r = 1;
}
return r;
}
Then in your main program, you would declare data as
char **data;
with a separate variable to track the size:
size_t dataLength = SOME_INITIAL_SIZE_GREATER_THAN_0;
You would allocate the array as
data = dataAlloc(dataLength);
initially. Then in your loop, you would compare your counter against the current array size and extend the array when they compare equal, like so:
if (counter == dataLength)
{
if (!dataExtend(&data, &dataLength))
{
/* Could not extend data array; treat as a fatal error */
fprintf(stderr, "Could not extend data array; exiting\n");
exit(EXIT_FAILURE);
}
}
data[counter] = malloc(sizeof *data[counter] * length + 1);
if (data[counter])
{
strncpy(data[counter], bytes+start, length);
data[counter][length] = 0; // add the 0 terminator
}
else
{
/* malloc failed; treat as a fatal error */
fprintf(stderr, "Could not allocate memory for string; exiting\n");
exit(EXIT_FAILURE);
}
counter++;
You are trying to print data with a format specifier %s, while your data is a array of pointer s to char.
Now talking about copying a string with giving size:
As far as I like it, I would suggest you to use
strlcpy() instead of strncpy()
size_t strlcpy( char *dst, const char *src, size_t siz);
as strncpy wont terminate the string with NULL,
strlcpy() solves this issue.
strings copied by strlcpy are always NULL terminated.
Allocate proper memory to the variable data[counter]. In your case counter is set to 0. Hence it will give segmentation fault if you try to access data[1] etc.
Declaring a variable like data[counter] is a bad practice. Even if counter changes in the subsequent flow of the program it wont be useful to allocate memory to the array data.
Hence use a double char pointer as stated above.
You can use your existing loop to find the number of lines first.
The last printf is wrong. You will be printing just the first line with it.
Iterate over the loop once you fix the above issue.
Change
int counter = 0;
char* data[counter];
...
int len=strlen(data);
...
for(; i<pos; i++)
...
strncpy(data[counter], bytes+start, length);
...
to
int counter = 0;
#define MAX_DATA_LINES 1024
char* data[MAX_DATA_LINES]; //1
...
for(; i<pos && counter < MAX_DATA_LINES ; i++) //2
...
strncpy(data[counter], bytes+start, length);
...
//1: to prepare valid memory storage for pointers to lines (e.g. data[0] to data[MAX_DATA_LINES]). Without doing this, you may hit into 'segmentation fault' error, if you do not, you are lucky.
//2: Just to ensure that if the total number of lines in the file are < MAX_DATA_LINES. You do not run into 'segmentation fault' error, because the memory storage for pointer to line data[>MAX_DATA_LINES] is no more valid.
I think that this might be a quicker implementation as you won't have to copy the contents of all the strings from the bytes array to a secondary array. You will of course lose your '\n' characters though.
It also takes into account files that don't end with a new line character and as pos is defined as long the array index used for bytes[] and also the length should be long.
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_LINE_ARRAY_DIM 100
int main(int argc, char* argv[])
{
FILE *f = fopen("test.c", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos+1); /* include an extra byte incase file isn't '\n' terminated */
fread(bytes, pos, 1, f);
if (bytes[pos-1]!='\n')
{
bytes[pos++] = '\n';
}
long i;
long length = 0;
int counter = 0;
size_t size=DEFAULT_LINE_ARRAY_DIM;
char** data=malloc(size*sizeof(char*));
data[0]=bytes;
for(i=0; i<pos; i++)
{
if (bytes[i]=='\n') {
bytes[i]='\0';
counter++;
if (counter>=size) {
size+=DEFAULT_LINE_ARRAY_DIM;
data=realloc(data,size*sizeof(char*));
if (data==NULL) {
fprintf(stderr,"Couldn't allocate enough memory!\n");
exit(1);
}
}
data[counter]=&bytes[i+1];
length = data[counter] - data[counter - 1] - 1;
printf("%d\n", counter);
printf("%ld\n", length);
}
}
for (i=0;i<counter;i++)
printf("%s\n", data[i]);
return 0;
}

Resources