Memory comparison causes system halt - c

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!

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;
}

Related

Null String when inserting characters (C)

I am currently working on a text editing program in C, which uses Linked Lists for rows of text. I have so far written functions for resizing the list etc., but I have now attempted to write the insert_char(Row* row, int idx, char c) however whenever I try resizing it, the resulting char* array is NULL. I am confident it's not a memory leak, as I have checked and I am free()ing all of my malloc()'d memory, so I really don't know where the problem is.
I have also tried some printf("%c", c) debugging to view the character, however the character itself is also NULL. Can anyone help me with this?
Here is the struct for a Row:
typedef struct {
char* data; // pointer to Malloc()'d char array.
int datalen;
} Row;
Here are the functions for resizing the row and allocating the Row pointer.
Row* alloc_row(char* data)
{
Row* row = (Row*) malloc(sizeof(Row));
char* data2 = (char*) malloc((sizeof(char) * strlen(data))+1);
strcpy(data2, data);
row->data = data2;
row->datalen = strlen(data);
return row;
}
// Row resize
Row* resize_row(Row* oldrow, char* data)
{
Row* new_row = (Row*) malloc(sizeof(Row));
new_row->data = data;
new_row->datalen = strlen(data);
// free() the old row
free(oldrow->data);
free(oldrow);
return new_row;
}
And here is the function I am having trouble with - it should take a Row*, create a buffer, strcpy() the Row->data up to idx, insert the char c and then copy the rest of the string afterwards, such that if I called alloc_row(Row* {.data = "Hello" .strlen=5}, 2, 'A') I would receive HeAllo (counting from zero). However, the string is NULL:
Row* insert_char(Row* row, int idx, char c)
{
char* new_row = (char*)malloc(sizeof(char) * (strlen(row->data) + 2)); // 1 char for null, char for the appended data
if (idx < strlen(row->data)) {
for (int i = 0; i < strlen(row->data)+1; i++) {
if (i < idx) new_row[i] = row->data[i];
if (i == idx) new_row[idx] = c;
if (i > idx) new_row[i] = row->data[i-1];
}
} else {
row->data[strlen(row->data)] = '\0';
strncpy(new_row, row->data, strlen(row->data));
new_row[strlen(row->data)-1] = c;
}
Row* nr = resize_row(row, new_row);
return nr;
}
Is there something wrong with my approach, and is there a cleaner and faster way of doing this?
At least these problems:
Not a string
new_row[] is not a string as it lacks a null character. Later code relies on that.
Result: undefined behavior (UB).
char* new_row = (char*)malloc(sizeof(char) * (strlen(row->data) + 2));
if (idx < strlen(row->data)) {
...
} else {
row->data[strlen(row->data)] = '\0';
strncpy(new_row, row->data, strlen(row->data));
// At this point `new_row[]` lacks a '\0'
new_row[strlen(row->data)-1] = c;
}
It is unclear exactly what OP's wants in the else block, but I think it may be:
} else {
size_t len = strlen(row->data);
strcpy(new_row, row->data);
new_row[len++] = c;
new_row[len] = '\0';
}
Minor: conceptually wrong size
The below works OK because (sizeof(char) is 1.
char* data2 = (char*) malloc((sizeof(char) * strlen(data))+1);
But should be:
char* data2 = (char*) malloc(sizeof(char) * (strlen(data) + 1));
Even better, drop the unneeded cast and size to the referenced object, not the type.
char* data2 = malloc(sizeof *data2 * (strlen(data) + 1u));
// or
char* data2 = malloc(sizeof data2[0] * (strlen(data) + 1u));
Untested alternate code
typedef struct {
char *data; // pointer to Malloc()'d char array.
//int datalen;
size_t datalen;
} Row;
// Row* insert_char(Row *row, int idx, char c) {
Row* insert_char(Row *row, size_t idx, char c) {
assert(c != 0); // Unclear what OP wants in this case
//char *new_row = (char*) malloc(sizeof(char) * (strlen(row->data) + 2));
// Why use strlen(row->data) when the length is in row->datalen ?
// Since row->data was getting free'd later in OP's code,
// let us just re-allocate instead and re-use the old row node.
char *new_row = realloc(row->data, row->datalen + 2);
assert(new_row); // TBD code to handle out-of-memory
// When idx large, simply append
if (idx > row->datalen) {
idx = row->datalen;
}
// Shift the right side over 1
memmove(new_row + idx + 1, new_row + idx, row->datalen - idx + 1); // Moves \0 too
new_row[idx] = c;
row->data = new_row;
row->datalen++;
return row;
}
I tried the following code and it works (I modified certain things to print it directly and corrected some of your suggestions on how to call the function):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char* data; // pointer to Malloc()'d char array.
int datalen;
} Row;
char* insert_char(Row* row, int idx, char c)
{
char* new_row = (char*)malloc(sizeof(char) * (strlen(row->data) + 2)); // 1 char for null, char for the appended data
if (idx < strlen(row->data)) {
for (int i = 0; i < strlen(row->data)+1; i++) {
if (i < idx) new_row[i] = row->data[i];
if (i == idx) new_row[idx] = c;
if (i > idx) new_row[i] = row->data[i-1];
}
} else {
row->data[strlen(row->data)] = '\0';
strncpy(new_row, row->data, strlen(row->data));
new_row[strlen(row->data)-1] = c;
}
return new_row;
}
int main()
{
printf("%s\n", insert_char(&(Row) {.data = "Hello", .datalen=5}, 2, 'A'));
return 0;
}
However, I think that your problem is in the for where you need +2 instead of +1 in the ending condition (since you are copying the entire array and malloc doesn't necessarly set the last char as '\0' [although calloc could do that]).
Using some of your great ideas, I have come up with the following sample which uses calloc() to initialise a section of memory to 0. I believe my issue was in fact a missing NULL byte, and I have also cleaned things significantly. Here is my improved snippet:
Row* insert_char(Row* row, int idx, char* str)
{
char* new_row = calloc(row->datalen + strlen(str) + 1, sizeof(char));
strncpy(new_row, row->data, idx);
strcat(new_row, str);
strcat(new_row, row->data + idx);
return resize_row(row, new_row);
}
NOTE: I have modified the input from a char to a char* because I plan to be inserting strings in the future, and not just single characters.
The same resize_row() method is used as in the original:
Row* resize_row(Row* oldrow, char* data)
{
Row* new_row = (Row*) malloc(sizeof(Row));
new_row->data = data;
new_row->datalen = strlen(data);
// free() the old row
free(oldrow->data);
free(oldrow);
return new_row;
}

Circular buffer does not give the correct size of the buffer after 6-th element

I have written the code for the circular buffer in C and it works well until some extent. I took the size of the buffer being equal to 10. When I fill the buffer till element 6 - it works fine. But at the moment when I fill the 7-th element - I get the result "The size of the buffer is equal to 767". For the element 8 - it does not work. I use "head" to write and "tail" to extract values. Could you please help me with this?
#include<stdio.h>
#include<stdint.h>
#include <stdbool.h>
typedef struct RingBuffer {
uint16_t* buffer;
size_t head;
size_t tail;
size_t max;
bool full;
}*cbuf_handle_t;
cbuf_handle_t init_RingBuffer (uint8_t* buffer, size_t size){
cbuf_handle_t cbuf = malloc (sizeof(cbuf_handle_t));
cbuf->buffer = buffer;
cbuf->max = size;
return cbuf;
}
void RingBuffer_free(cbuf_handle_t cbuf){
free(cbuf);
}
void RingBuffer_reset(cbuf_handle_t cbuf){
cbuf->head = 0;
cbuf->tail = 0;
cbuf->full = false;
}
bool RingBuffer_full (cbuf_handle_t cbuf){
return cbuf->full;
}
bool RingBuffer_empty(cbuf_handle_t cbuf){
return (!cbuf->full && (cbuf->tail == cbuf->head));
}
size_t RingBuffer_Capacity(cbuf_handle_t cbuf){
return cbuf->max;
}
size_t RingBuffer_size(cbuf_handle_t cbuf){
size_t size = cbuf->max;
if (!cbuf->full){
if (cbuf->head >= cbuf->tail)
{
size = (cbuf->head - cbuf->tail);}
else
{
size = (cbuf->head - cbuf->tail + cbuf->max);
}
}
return size;
}
void RingBuffer_AdvancePointer(cbuf_handle_t cbuf){
if (cbuf->full){
cbuf->tail = (cbuf->tail+1)%cbuf->max;
}
cbuf->head = (cbuf->head + 1)%cbuf->max;
cbuf->full = (cbuf->head == cbuf->tail);
}
void RingBuffer_retreatPointer (cbuf_handle_t cbuf){
cbuf->full = false;
cbuf->tail = (cbuf->tail + 1)%cbuf->max;
}
void RingBuffer_addValue (cbuf_handle_t cbuf, uint8_t data){
cbuf->buffer[cbuf->head] = data;
RingBuffer_AdvancePointer(cbuf);
}
int RingBuffer_Remove (cbuf_handle_t cbuf, uint8_t *data){
int r = -1;
if (!RingBuffer_empty(cbuf)){
*data = cbuf->buffer[cbuf->tail];
RingBuffer_retreatPointer(cbuf);
r = 0;
}
return r;
}
int main (){
uint8_t arr[10];
cbuf_handle_t cpt = init_RingBuffer(arr, 10);
//initialzie the buffer, tail and head and max
int i = 0;
RingBuffer_reset(cpt);
for ( i = 0 ; i< 6; i++){
RingBuffer_addValue(cpt, i);
}
size_t size = RingBuffer_size(cpt);
printf("The size of the buffer %d", size);
}
Thank you in advance!
Regards
Rostyslav
As said in comments, the declaration of the structure as a pointer is generally not recommended. However you can fix that bug by changing the way you allocate it using malloc :
cbuf_handle_t cbuf = malloc (sizeof(*cbuf));
This is because, cbuf being a pointer to the structure, if you dereference it you get the structure and thus its real size when you pass it to sizeof.

retrieve information from a structure with ptrace

Here, I explain my problem, I am a beginner on the ptrace function and I would like to succeed in recovering the hard information of a structure.
For example with this command, I will have strace -e trace = fstat ls
a line: fstat (3, {st_mode = ..., st_size = ...}
and I would like to successfully retrieve the contents of the structure (st_mode) and (st_size).
I try this but to no avail:
int buffer(unsigned long long addr, pid_t child, size_t size, void *buffer)
{
size_t byte = 0;
size_t data;
unsigned long tmp;
while (byte < size) {
tmp = ptrace(PTRACE_PEEKDATA, child, addr + byte);
if ((size - byte) / sizeof(tmp))
data = sizeof(tmp);
else
data = size % sizeof(tmp);
memcpy((void *)(buffer + byte), &tmp, data);
byte += data;
}
}
and in params :
struct stat stat_i;
buffer(addr, pid, sizeof(stat_i), &stat_i);
printf("%lu", stat_i.st_size); -> fake value :/
Thank'ks !
From the man page,
PTRACE_PEEKTEXT, PTRACE_PEEKDATA
Read a word at the address addr in the tracee's memory,
returning the word as the result of the ptrace() call. Linux
does not have separate text and data address spaces, so these
two requests are currently equivalent. (data is ignored; but
see NOTES.)
Thus you must understand that tmp would hold the actually value that was read.
Your checks are wrong - you should set errno = 0 before the call and then check if it has changed. If it has - you've got an error. If it hasn't - you can be assured that tmp has the word from the remote process.
Try something like this:
int buffer(unsigned long long addr, pid_t child, size_t size, void *buffer)
{
size_t byte = 0;
size_t data;
unsigned long tmp;
// support for word aligned sizes only
if (size % sizeof(long) != 0)
return -1;
long * buffer_int = (long*) buffer;
while (byte < size) {
errno = 0;
tmp = ptrace(PTRACE_PEEKDATA, child, addr + byte);
if (errno)
return -1;
buffer_int[byte / sizeof(long)] = tmp;
byte += sizeof(long);
}
}

Send message through Ring (Circular) Buffer between Threads (in C)

I need to send a message from Main thread to my Created Thread using WinAPI and Ring Buffer.
I defined structures and wrote functions for my Ring buffer.
Ring Buffer - it contains head, tail, size and pointer to the structure Descriptor which has length of Data and data itself. As I need to send 2 parameters to CreateThread function, I created the third structure ThreadParams to keep 2 parameters.
I want to leave this structures how they are now, not changeable.
typedef struct _Descriptor
{
uint32_t dataLen;
void * data;
} Descriptor;
typedef struct _ringBuffer
{
Descriptor *bufferData;
int head;
int tail;
int size;
} ringBuffer;
typedef struct _ThreadParams
{
void * ptr1;
void * ptr2;
} ThreadParams;
There are my realisations of Ring Buffer functions:
void bufferFree(ringBuffer *buffer)
{
free(buffer->bufferData);
}
void ringInitialization(ringBuffer *buffer, int size)
{
buffer->size = size;
buffer->head = 0;
buffer->tail = 0;
buffer->bufferData = (Descriptor*)malloc(sizeof(Descriptor) * size);
}
int pushBack(ringBuffer *buffer, void * data) // fill buffer
{
buffer->bufferData[buffer->tail++] = *(Descriptor*)data;
if (buffer->tail == buffer->size)
{
buffer->tail = 0;
}
return 0;
}
int popFront(ringBuffer *buffer)
{
if (buffer->head != buffer->tail)
{
buffer->head++;
if (buffer->head == buffer->size)
{
buffer->head = 0;
}
}
return 0;
}
My main: I checked that I can send a few bytes (the memory is shared between threads), now I need to send a big message (> BUFF_SIZE) though Ring Buffer what I'm trying to do in while() cycle. Here is the question: how should I do it? My thing doesn't work because I catch an exception in printf() function (memory acces violation).
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <strsafe.h>
#include <stdint.h>
#define RING_SIZE 256
#define BUFFER_SIZE 1024
DWORD WINAPI HandleSendThread(LPVOID params);
uint8_t * getPointer(uint8_t *buffer, uint32_t index)
{
uint8_t * ptr = ((uint8_t*)buffer) + index * BUFFER_SIZE;
return ptr;
}
int main(int argc, char * argv[])
{
//Descriptor * ringData = (Descriptor *)malloc(sizeof(Descriptor) * RING_SIZE);
ringBuffer ring;
ringInitialization(&ring, RING_SIZE);
void * packetBuffer = malloc(BUFFER_SIZE * RING_SIZE);
uint8_t * currentBuffer = getPointer(packetBuffer, 0);
uint8_t * str = "Mr. and Mrs. Dursley, of number four, Privet Drive, were proud to say that they were perfectly normal, thank you very much. They were the last people you'd expect to be involved in anything strange or mysterious, because they just didn't hold with such nonsense. Mr.Dursley was the director of a firm called Grunnings, which made drills.He was a big, beefy man with hardly any neck, although he did have a very large mustache.Mrs.Dursley was thin and blonde and had nearly twice the usual amount of neck, which came in very useful as she spent so much of her time craning over garden fences, spying on the neighbors.The Dursleys had a small son called Dudley and in their opinion there was no finer boy anywhere.";
strcpy(currentBuffer, str);
ring.bufferData[0].data = currentBuffer;
ring.bufferData[0].dataLen = strlen(str);
int currentSize = 0;
int ringSize = RING_SIZE;
while(ring.bufferData[0].data != '\0')
{
for (int i = currentSize; i < ringSize; i + RING_SIZE)
{
pushBack(&ring, currentBuffer);
printf("h = %s, tail = %s, dataBuffer = %s\n", (char*)ring.head, (char*)ring.tail, (char*)ring.bufferData[i].data);
}
currentSize = ringSize;
ringSize = 2 * ringSize;
popFront(&ring);
}
ThreadParams params = { &ring, packetBuffer };
HANDLE MessageThread = 0;
MessageThread = CreateThread(NULL, 0, HandleSendThread, &params, 0, NULL);
if (MessageThread == NULL)
{
ExitProcess(MessageThread);
}
WaitForSingleObject(MessageThread, INFINITE);
CloseHandle(MessageThread);
system("pause");
return 0;
}
And my CreateThread function:
DWORD WINAPI HandleSendThread(LPVOID params)
{
ringBuffer * ring = ((ThreadParams*)params)->ptr1;
void * buffer = ((ThreadParams*)params)->ptr2;
//ring->bufferData[0].dataLen = sizeof(buffer) + sizeof(ring->bufferData[0])*1024;
printf("Shared memory check: ringBuffer data = \"%s\", \nlength = %d\n", (char*)ring->bufferData[0].data, ring->bufferData[0].dataLen);
return 0;
}
Your most immediate problem is the inconsistency between the code in pushBack(), which expects data to point to a Descriptor, and the code in your main function, which passes in a pointer to a string instead.
If you had declared pushBack() properly, i.e.,
void pushBack(ringBuffer *buffer, Descriptor * data)
{
buffer->bufferData[buffer->tail++] = *data;
if (buffer->tail == buffer->size)
{
buffer->tail = 0;
}
}
Then the compiler would have been able to warn you about the discrepancy.
You also have an infinite loop here:
for (int i = currentSize; i < ringSize; i + RING_SIZE)
You probably meant
for (int i = currentSize; i < ringSize; i += RING_SIZE)
... although it still doesn't look to me like it will do anything sensible. Nor do I understand the purpose of the outer loop, which compares a pointer to a character.
Found a solution
int main(int argc, char * argv[])
{
ringBuffer ring;
ringInitialization(&ring, RING_SIZE);
void * packetBuffer = malloc(BUFFER_SIZE * RING_SIZE);
Descriptor temp = { 0 };
uint8_t * currentBuffer = getPointer(packetBuffer, 0);
uint8_t * str = "Mr. and Mrs. Dursley, of number four, Privet Drive, were proud to say that they were perfectly normal, thank you very much. They were the last people you'd expect to be involved in anything strange or mysterious, because they just didn't hold with such nonsense. Mr.Dursley was the director of a firm called Grunnings, which made drills.He was a big, beefy man with hardly any neck, although he did have a very large mustache.Mrs.Dursley was thin and blonde and had nearly twice the usual amount of neck, which came in very useful as she spent so much of her time craning over garden fences, spying on the neighbors.The Dursleys had a small son called Dudley and in their opinion there was no finer boy anywhere.";
strcpy(currentBuffer, str);
temp.dataLen = strlen(str);
temp.data = currentBuffer;
pushBack(&ring, &temp);
ThreadParams params = { &ring, packetBuffer };
HANDLE MessageThread = 0;
MessageThread = CreateThread(NULL, 0, HandleSendThread, &params, 0, NULL);
if (MessageThread == NULL)
{
ExitProcess(MessageThread);
}
WaitForSingleObject(MessageThread, INFINITE);
CloseHandle(MessageThread);
system("pause");
return 0;
}
DWORD WINAPI HandleSendThread(LPVOID params)
{
ringBuffer * ring = ((ThreadParams*)params)->ptr1;
void * buffer = ((ThreadParams*)params)->ptr2;
Descriptor * temp = &ring->bufferData[ring->head];
for (int i = 0; i < temp->dataLen; i++)
{
printf("%c", ((char*)temp->data)[i]);
}
printf("\n");
return 0;
}

Create a safe nullpointer in regards to memory leaks

Look at this:
char (*options)[MAXLEN];
char *ptr;
ptr = strtok(input, " \r\n");
if(!ptr)
continue;
int i = 0;
optionen = malloc(sizeof(*options));
if(!options)
die("malloc");
while(ptr){
if(i > 0){
options = realloc(options, (i+1)*sizeof(*options));
if(!options)
die("malloc");
}
strcpy(options[i], ptr);
if(!options[i])
die("strcpy");
ptr = strtok(NULL, " \r\n");
i++;
}
/*create another entry options[i] that is a nullpointer*/
Purpose is that the exec(3) command requires a nullpointer as the last entry to the *options[] array to work properly.
Problem: How can I add another entry to the array that is a NULL - pointer? I understand that I cannot allocate another options[i] and set it to NULL because some guy on stackoverflow told me to never do that (memory leak).
Note: input is some array that contains some commandline - input(char input[MAXLEN]; ) and die() just calls perror() and then exit()
Whoever told you that you "cannot allocate another options[i] and set it to NULL" was wrong. That is exactly what you do.
However, you have bugs in your code, and the bugs suggest that you don't understand what it means to "allocate another options[i]."
char (*options)[MAXLEN]; /* This is wrong */
char *ptr;
ptr = strtok(input, " \r\n");
if(!ptr)
continue;
int i = 0;
optionen = malloc(sizeof(*options));
if(!options)
die("malloc");
while(ptr){
if(i > 0){
options = realloc(options, (i+1)*sizeof(*options));
if(!options)
die("malloc");
}
strcpy(options[i], ptr); /* This is also wrong */
if(!options[i])
die("strcpy");
ptr = strtok(NULL, " \r\n");
i++;
}
First off, char (*options)[MAXLEN] is the wrong kind of array, and an entry in it cannot be set to NULL. You need instead char **options.
Second off, realloc operation that you are already doing is, in fact, allocating more options[i] slots. But you also need to allocate space for the strings themselves (but not for the NULL). You do that with strdup.
options[i] = strdup(ptr); /* Instead of the "also wrong" line */
(Depending on what input is, you might be able to get away with just using the string pointers strtok returns, but without seeing more of your code I can't be sure that's safe.)
And then, after the loop, you simply set the final options[i] slot to NULL and you're done.
I would structure the loop a bit differently, like this:
char **options;
char *ptr;
size_t i, asize;
ptr = strtok(input, " \t\r\n");
if (!ptr) continue;
options = 0;
i = 0;
asize = 2;
do {
while (i >= asize) {
asize *= 2;
options = xreallocarray(options, asize, sizeof(char *));
}
options[i++] = xstrdup(ptr);
ptr = strtok(0, " \t\r\n");
} while (ptr);
while (i >= asize) {
asize *= 2;
options = xreallocarray(options, asize, sizeof(char *));
}
options[i] = 0;
execve(options[0], options, environ);
die("execve");
The functions xreallocarray and xstrdup are nonstandard, but they should be in your bag of simple functions that you add to every program that you write. Here are their definitions. They use the die() function you already have.
void *
xreallocarray(void *optr, size_t nmemb, size_t size)
{
/* s1*s2 <= SIZE_MAX if both s1 < K and s2 < K where K = sqrt(SIZE_MAX+1) */
const size_t MUL_NO_OVERFLOW = ((size_t)1) << (sizeof(size_t) * 4);
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
nmemb > 0 && SIZE_MAX / nmemb < size) {
errno = ENOMEM;
die("malloc");
}
void *rv = realloc(optr, size * nmemb);
if (!rv)
die("malloc");
return rv;
}
char *
xstrdup(const char *s)
{
size_t n = strlen(s) + 1;
char *rv = malloc(n);
if (!rv)
die("malloc");
memcpy(rv, s, n);
return rv;
}
My suggestion: Create another array as follows and use it in the call to exec.
char** args = malloc((i+1)*sizeof(*args));
for (int j = 0; j < i; ++j )
{
args[j] = options[j];
}
args[i] = NULL;
execvp(..., args);

Resources