Seg fault on my own version of getline - c

I'm trying to make a simple version of getline. It should read a line in from stdin, reallocating the size of the buffer as necessary. It should also return the number of characters read. It takes a char ** so that the reallocated buffer can be later freed. Why am I getting a segfault?
Heres my version:
int get_input_line(char **buff, int start_size) {
char c;
int stop = 0, length = 0, k = start_size;
while(!stop) {
if(length > k) {
k += 50;
buff = (char *)(realloc(buff, start_size + 1));
}
c = getchar();
if(c == '\n'){
stop = 1;
}
buff[length] = c;
length++;
}
return length;
}
And here's the call:
char *buff = (char *)(malloc(50 + 1));
get_input_line(&buff, 50);
printf("%s", buff);

You probably meant:
*buff = (realloc(*buff, new_size));
^ ^
And
(*buff)[length] = c;
You're also missing the 0 terminator.
EDIT
As nos points out, length > k should be length >= k .

You're not detecting EOF reliably. You need to save the result of getchar() in an int and not a char. And you should not try to store EOF in your buffer.
You're not checking your memory allocations.
You're not null terminating the output string, so the printf() in main() may crash.
You're confusing someone (maybe me, maybe the compiler, maybe yourself) by allocating 51 bytes and telling the function that it only has 50 bytes to play with.
And, most particularly, you need to be using *buff at most points inside the function, including, in particular, when adding a character:
(*buff)[length++] = c;
You really should be paying more attention to all those compiler warnings. If your compiler isn't giving you any, get a better compiler (or turn on the warning flags - but you should be being shrieked at by the compiler in its default mode).
Also, you are miscalling realloc() on three grounds. One is the *buff issue. The second is that you want the size to be k, not start_size + 1. The other is that you are assigning the result to the input parameter. This is a 'no-no' because if the allocation fails, you've lost your pointer to the previously (and still) allocated data. Always use the idiom:
void *new_data = realloc(old_data, new_size);
if (new_data == 0)
...deal with out of memory error...
else
{
old_data = new_data;
old_size = new_size;
}
Applied to your code, that means:
char *new_buff = (char *)realloc(*buff, k); // NOT start_size+1!!!
if (new_buff == 0)
...deal with out of memory error...
else
*buff = new_buff;
There are those who argue against the cast on malloc() and realloc() and calloc(); there are those who prefer the casts present. There are arguments on both sides, of differing degrees of validity. I prefer the cast - I respect those who prefer no cast. We reach our different conclusions for different reasons.
I have not studied the code for other 'off-by-one' errors. I suspect that there may be several of those, too.

The line
buff = (char *)(realloc(buff, start_size + 1));
should be
*buff = (char *)(realloc(*buff, k + 1));
Also
buf[length] = c
should be
*buf[length] = c
Moreover, I think you forgot to store a final '\0'.

Related

Error on realloc : corrupted size vs. prev_size

I'm coding in C.
The goal of my algorithm is to double each char c we find in an array str.
I have to run a few tests, for the first test I call doubleChar("~/fichier.txt", '~') and it works fine, but my second test is doubleChar("une commande # commentaire", '#') and I get the error from the title.
When i tried to debug it, the error was on the realloc line.
In debugger I get this error:
Program Received signal SIGABRT Stack trace is available in the 'Call Stack' tab
Any idea why?
Here's my code:
char * doubleChar(const char *str, char c){
assert(str!=NULL);
char *newString=malloc(sizeof(str) * sizeof(char));
int a = 0;
while(str[a] != '\0'){
newString[a]=str[a];
a++;
}
newString[a]='\0';
int i = 0;
while(newString[i] != '\0'){
if(newString[i] == c){
newString = (char *)realloc(newString, stringLength(newString)*sizeof(char) + sizeof(char));
for(int j=stringLength(newString)+1; j>i; j--){
newString[j]=newString[j-1];
}
i++; //we add 1 to i so we don't find the char we just added
}
i++;
}
return newString;
}
char *newString=malloc(sizeof(str) * sizeof(char));
Since str is a const char *, the sizeof(str) is how many bytes a const char * takes on your platform. That's not what you want. What you want to pass to malloc is the length of the string you want to store, making sure to leave an extra byte for the terminating zero. In this case, that's (strlen(str) + 1).
newString = (char *)realloc(newString, stringLength(newString)*sizeof(char) + sizeof(char));
This is probably also wrong. You need (strlen(newString) + 2) to leave one byte for the newly-added character and one byte for the zero terminator. It's hard to be sure though because I can't tell what your stringLength function does.
Also, sizeof(char) is, by definition, one. The sizeof function returns the size in characters.
It's much easier to understand strlen(newString) + 2 than stringLength(newString)*sizeof(char) + sizeof(char) because strlen is a standard function and sizeof(char) is verbose and ugly. So I'd strongly suggest changing this to strlen(newString) + 2.

Do I have to initialize a char* after malloc?

I have a program that reads chars into a dynamic string buffer. We do not know the size of the string, and it is a requirement that we do not simply set a fixed-size, "large-enough" buffer.
The relevant function works like this:
char* read_field(FILE* data)
{
int size = 8;
char *field = malloc(size);
if (field == NULL)
exit(1);
char *tmp = NULL;
int idx = 0;
int ch = EOF;
while (ch) {
ch = fgetc(data);
// Double size if full
if (size <= idx) {
size *= 2;
tmp = realloc(field, size);
if (!tmp)
exit(1);
field = tmp;
}
field[idx++] = ch;
// Relevant termination in my use case
if (ch == ';' || ch == '\n')
ch = 0;
}
printf("field: %s\n"); // value correct, but sometimes valgrind error
return field; // field is free'd by the caller
}
Now the program seems to work, but when running it through Valgrind I get the errors Uninitialised value was created by a heap allocation and Conditional jump or move depends on uninitialised value(s). These error appears arbitrarily (sometimes) when I call functions like printf or strlen, as seen in the code above.
This problem is sorted if I use calloc instead of malloc / realloc, but then the reallocation process becomes messier.
Is the Valgrind error something that could be ignored if the program works fine? What are the implications of not initializing the memory to zero? If this can't be ignored, what's the best design to sort it out?
You should put a string terminator at the end of the string.
PS:
If you want to clear some memory use memset, it's faster than a for cycle
use calloc , its much better than malloc and memset.
Example
char *string = calloc( 100 , sizeof(char*));
// Calloc automatically fills the memory blocks
// Its much faster than malloc and memset
// In addition , only in C you don't need typecast for memory allocators

strcat makes crash program (0xc0000005)

I need to draw a line of characters as long I want. So I wrote a function for that purpose:
void fDrawLine(int length)
{
int i;
char * compLine = (char *) malloc(WINDOW_WIDTH + 2);
for(i = 0; i < length; i++)
strcat(compLine, "-");
fDrawSpacedMessage(compLine, -1, TRUE);
}
WINDOW_WIDTH defined as 80, fDrawSpacedMessage is another function to print texts centered etc.
It's building perfectly, no errors, no warnings. But in runtime, everything works but if fDrawLine executes, the program crashes and gives the error code 0xc0000005. I know it's about memory allocation but I already initialize the compLine string.
I've tried a couple of things; I thought another function caused it so I isolated fDrawLine, but crashing continued. Changing initialize with compLine[0] = 0;, compLine[WINDOW_WIDTH] = {0}; did not help.
It works well with my other machine, which runs Ubuntu, with latest gcc, but when using Code::Blocks (MinGW) on Windows it keeps crashing.
What's wrong with this code?
Don't declare compLine as a pointer, since you don't need that, and actually you have a memory leak in your function, first declare compLine this way
char compLine[1 + WINDOW_WIDTH] = {0}; // strings need an extra byte at the end to mark the end.
then use memset to set the '-' character like this
memset(compLine, '-', length);
of course, check that length <= WINDOW_WIDTH.
This is your function fixed, so you can try it
void fDrawLine(int length)
{
char compLine[1 + WINDOW_WIDTH] = {0}; // initialized so that last byte is '\0'.
if (length > WINDOW_WIDTH)
length = WINDOW_WIDTH;
memset(compLine, '-', length);
fDrawSpacedMessage(compLine, -1, TRUE);
}
besides using strcat that way is a bad idea, you can do it this
char *compLine = malloc(1 + length); // the last extra '\0' byte.
if (compLine == NULL) // malloc returns NULL on failure to allocate memory
return; // so we must abort this function in that case.
for(i = 0; i < length; i++)
compLine[i] = '-';
compLine[length] = '\0';
fDrawSpacedMessage(compLine, -1, TRUE);
free(compLine);
you can also use memset in this case, and it is actually better.
The allocated memory starts containing garbage. Set it to an empty string, for example like this:
compLine[0] = '\0';
There are a few problems with your code below
void fDrawLine(int length)
{
int i;
char * compLine = (char *) malloc(WINDOW_WIDTH + 2);
for(i = 0; i < length; i++)
strcat(compLine, "-");
fDrawSpacedMessage(compLine, -1, TRUE);
}
First, the length parameter should at least be unsigned int as a negative length makes no sense. Ideally, you should use size_t. The same goes for i.
Next, you are not protecting yourself against invalid values for length. The implicit contract is that 0 <= length <= WINDOW_WIDTH - make it explicit.
Your use of dynamically allocated memory leads to a memory leak as you don't release it after calling fDrawSpacedMessage().
Finally, strcat is overkill to append a single character.
Putting all that together, here's an alternative implementation.
void fDrawLine(size_t length)
{
size_t actual_length = length <= WINDOW_WIDTH ? length : WINDOW_WIDTH;
char compLine[WINDOW_WIDTH+2];
memset(compLine, '-', actual_length);
compline[actual_length] = '\0';
fDrawSpacedMessage(compLine, -1, TRUE);
}
I've left compline at WINDOW_WIDTH+2 as I'm guessing fDrawSpacedMessage adds a newline.
If it still crashes, the problem is in fDrawSpacedMessage

receive a segmentation error when printing out words of array

The following is a piece of code where the user enters unknown amounts of words until 'E' is entered, whereupon the program should stop and print out all of the entered words. However, when run this program produces a segmentation error. Did I access some memory I shouldn't have?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CAPACITY 10
#define NUM_OF_WORDS 10
int main(void)
{
int num_words = 10;
char *word= malloc(CAPACITY*sizeof(char));
char **w=(char **) malloc(num_words*sizeof(char));
int i;
for(i = 0 ; scanf("%s", word)==1; ++i)
{
if(*word == 'E')
break;
if( i == num_words-1)
w = (char **)realloc(w, (num_words *=2) * sizeof(char));
w[i] =(char *) malloc(strlen(word)+1 * sizeof(char));
strcpy(w[i], word);
}
int x = 0;
for(x = 0 ; x<num_words ; x++)
printf("%s", w[x]);
return 0;
}
Your initial allocation code reads:
char *word = malloc(CAPACITY*sizeof(char));
char **w = (char **) malloc(num_words*sizeof(char));
Both these allocate 10 bytes of memory. Your second one should read:
char **w = (char **) malloc(num_words*sizeof(char *));
or:
char **w = malloc(num_words*sizeof(*w));
These both allocate enough memory for 10 pointers (which might be eight times as much memory as your original code). The second is arguably better style; the first is indubitably the classic style. In C, the cast on malloc() is not necessary; in C++, it is.
This may not be the whole problem; it is almost certainly a contributory factor.
Also, you aren't checking your memory allocations; that is not advisable. You should always check them.
This code:
if (i == num_words-1)
w = (char **)realloc(w, (num_words *=2) * sizeof(char));
is playing with fire on two accounts (plus a repeat of the previously diagnosed problem):
The assignment within the argument list is...not generally reckoned to be a good idea. I wouldn't write code with that in place, and I'd send back code I was asked to review that contained it. It isn't technically wrong; it will work. But it does not make life easier for the maintenance programmers who come after.
You should never reallocate a pointer such as w and assign the new space to the same pointer. If the memory allocation fails, you'll get back a null pointer, so you've lost the only pointer to the previous data, which is still allocated. That's a memory leak. Also, if the memory allocation fails, you have to undo the assignment within the argument list because the allocated space is still at the original size. I think you'd be better off using:
if (i == num_words - 1)
{
size_t new_size = (num_words * 2);
char **new_data = realloc(w, new_size * sizeof(*new_data));
if (new_data == 0)
...handle error; w is still valid, and num_words is still correct too...
num_words = new_size;
w = new_data;
}
Your variable num_words holds the current maximum size of the w array, but that isn't the same as the number of words actually in the array.
When you loop through the w array you are looping through too many items - some elements of the w do not have a valid string in them - trying to print them will cause a segfault.

Weird Side Effect with Malloc()

So i'm writing a C program for a simple shell. Not too difficult but I've run into one really odd problem that I can't explain. I'm attempting to create a two dimensional array within a struct to represent a command and its arguments. For the command "ls -l" for example i'd like to have the first element "ls" and the second "-l". Seems to work fine other than that malloc changes the "ls" to "ms". The same thing for other commands, the first character is incremented; not before the malloc and immediately afterward.
This is the hunk of code in question....
printf ("PRE MALLOC: %c\n", ret_val->args[0][0]);
printf ("[0] %p\n", ret_val->args[0]);
ret_val->args[1] = (char*) malloc ((3) * sizeof (char));
printf ("[0] %p\n", ret_val->args[0]);
printf ("[1] %p\n", ret_val->args[1]);
printf ("POST MALLOC: %c\n", ret_val->args[0][0]);
All I'm attempting to accomplish is to allocate a 3 ( -l + the null ) character array to hold the "-l" in args[1]. These aren't really going to be hardcoded either but I figured it makes the point better.
The output produced it this...
PRE MALLOC: l
[0] 80613b0
[0] 80613b0
[1] 80613b8
POST MALLOC: m
So the two addresses don't overlap or anything strange but the first character of the first array is incremented? I'm sorry if I'm overlooking something stupid. But I can't think of any reason why this would happen.
Any ideas?
Thanks,
Here is much more code, for some context.
int lcv;
int numargs;
int numchars;
int offset;
int bool;
TCommand* ret_val;
char** tmp;
ret_val = (TCommand*) malloc ( sizeof (TCommand) );
ret_val->cmd = NULL;
ret_val->args = NULL;
/* CMD */
lcv = 0;
numargs = 0;
numchars = 0;
offset = 0;
/* Remove initial whitespace */
while (input[offset] == ' ')
{
++offset;
}
lcv = offset;
/* Loop through command string */
while ( input[lcv] != ' ' && input[lcv] != 0 && input[lcv] != '&')
{
++numchars;
++lcv;
}
ret_val->cmd = (char*) malloc ( (numchars+1) * sizeof(char));
/* Copy to command string */
memcpy (ret_val->cmd, &(input[offset]), (numchars * sizeof (char)));
ret_val->cmd[numchars] = 0;
offset += numchars;
/* Copy command string into first argument */
ret_val->args = (char**) malloc ( sizeof (char*));
memcpy (ret_val->args[numargs++],ret_val->cmd, (numchars+1) * sizeof(char));
bool = 1;
while ( bool )
{
/* Remove initial whitespace */
while (input[offset] == ' ')
{
++offset;
}
lcv = offset;
if ( input[lcv] == 0 )
{
bool = 0;
}
else
{
++numargs;
tmp = (char**) realloc (ret_val->args, numargs * sizeof (char*));
ret_val->args = tmp;
numchars = 0;
while ( input[lcv] != ' ' && input[lcv] != 0 &&
input[lcv] != '&')
{
++numchars;
++lcv;
}
printf ("PRE MALLOC: %c\n", ret_val->args[0][0]);
printf ("[0] %p\n", ret_val->args[0]);
ret_val->args[1] = (char*) malloc ((2) * sizeof (char));
printf ("[0] %p\n", ret_val->args[0]);
printf ("[1] %p\n", ret_val->args[1]);
printf ("POST MALLOC: %c\n", ret_val->args[0][0]);
fflush(stdout);
memcpy (ret_val->args[numargs-1],&(input[offset]),numchars * sizeof (char));
ret_val->args[numargs-1][numchars] = 0;
offset += numchars;
}
}
This code:
/* Copy command string into first argument */
ret_val->args = (char**) malloc ( 2 * sizeof (char*));
memcpy (ret_val->args[numargs++],ret_val->cmd, (numchars+1) * sizeof(char));
Copies through the uninitialised pointer ret_val->args[0]. Try:
/* Copy command string into first argument */
ret_val->args = malloc(2 * sizeof ret_val->args[0]);
ret_val->args[numargs] = malloc(numchars + 1);
memcpy(ret_val->args[numargs++], ret_val->cmd, numchars + 1);
(Note that sizeof(char) is defined to be 1 by the language).
The problem isn't in the fragment you show; it is somewhere in the code you don't show.
So, you need to build your example from the ground up - showing the structure declarations, the memory management and copying you do for the structures.
I guess we need to see how args[0] is allocated, and how it is given a value.
I can make a few suggestions, though they won't fix the allocation problem:
It is not necessary to cast the result of malloc(), and while one has limited ability to change a type in an already-written C program, there is no reason to deliberately lower the level of abstraction by open-coding duplicate type information. Old texts and old code examples often cast malloc() because C did not always have a void * type, and even after it arrived people wrote in the old style by habit and for code portability to old compilers. But you are unlikely these days to ever need to compile for the PDP-11.
sizeof(char) is, by C99, 1, so it's OK to just say malloc(3), and it reads slightly easier because it's shorter. And if you know it's "-l", you may as well just use "-l".
This sounds like a classic example of heap corruption.
What you are seeing is most probably the result of the malloc() call overwriting the string that args[0] points to with its return value. This can happen if the args array does not actually have enough space for the second argument. By writing into args[1] you are overwriting the contents of the string that is pointed to by argv[0] and that I suspect you allocate with an strdup() call or similar immediately after allocating the args array.
Since the heap grows upwards, and assuming no intervening free() calls, by overstepping the bounds of a dynamically allocated array there is an increased chance of messing with the area allocated immediately afterwards.
I'll hazard a guess and recommend that you check the allocated size for the args array by printing out the arguments for the realloc() call and checking its return value.
EDIT:
Apparently not in this case. It seems that the argv[0] pointer could actually be uninitialised, without causing a segmentation fault. Interesting...

Resources