strcat makes crash program (0xc0000005) - c

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

Related

Why does calling a function in a different order cause a segmentation fault in C?

I am working on a program in C that processes an array of character pointers. I have several functions that operate on this array, including longest_name(), unique(), and others. When I call the longest_name() function after unique() in the main() function, the program runs fine. But when I switch the order and call longest_name() before unique(), I get a segmentation fault.
I know that the pointer char *name in the longest_name() function should be allocated by malloc to avoid the segmentation fault. However, I want to understand why the program is working in one scenario but not in the other.
My suspicion is that the issue may be related to memory allocation or pointer manipulation, but I am not sure where to start looking. Can anyone explain why this is happening and how to fix it?
#include <stdio.h>
#include<string.h>
#include<ctype.h>
#include<stdlib.h>
void display_name(char **stu, int indx);
void vowel_count(char **stu, int indx);
void longest_name(char **stu);
void unique(char **stu);
int main()
{
char *name[10] = {"Aakash", "Anirudha", "Vikas", "Vinay", "Rakesh", "Thomas", "Jerry", "Alekha", "Daksh", "Peter"};
display_name(name, 4);
vowel_count(name,9);
//longest_name(name); //not run
unique(name);
longest_name(name); //run
return 0;
}
void unique(char **stu){
int len = 0, found = 0;
printf("Name whose first and last character is \'a\' :- ");
for(int i = 0; i < 10; i++){
len = strlen(stu[i]);
if(stu[i][0] == 'a' && stu[i][0] == 'a'){
found = 1;
printf("%s\n", stu[i]);
}
}
if(found == 0){
printf("None\n");
}
}
void longest_name(char **stu){
int len = 0, mx = 0;
char *name;
printf("\n Address is %p \n",name);
for(int i = 0; i < 10; i++){
if(mx < strlen(stu[i])){
mx = strlen(stu[i]);
strcpy(name, stu[i]);
}
}
printf("Longest name is \"%s\" with length %d\n", name, mx);
}
void vowel_count(char **stu, int indx){
indx--;
for(int i = 0; i < 10; i++){
if(i == indx){
int len = strlen(stu[i]), cnt = 0;
char name[len];
strcpy(name, stu[i]);
for(int j = 0; j < len; j++){
char c = tolower(name[j]);
if(c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
cnt++;
}
printf("Number of vowels in \"%s\" are :- %d\n", name, cnt);
break;
}
}
}
void display_name(char **stu, int indx){
indx--;
for(int i = 0; i < 10; i++){
if(i == indx)
printf("%s\n",stu[i]);
}
}
I tried running the program on a different machine as I thought the issue might be related to the compiler. However, the behavior was the same on the other machine as well.
As mentioned in the comments, your variable name in your longest_name function is uninitialised.
You have declared it like this:
char *name;
and have not made it point anywhere.
Many compilers will manually initialise the pointer to zero, such as having written:
char *name = NULL;
... but this is not guaranteed, so it is good practice to always initialise your pointers to NULL if you do not wish to make them point anywhere yet. Nonetheless, it is important to note that initialising to NULL just means the pointer definitely does not point anywhere.
In your case, it looks like your name pointer might have been initialised to some random value - such as whetever was at that location on the stack previously. If the latter is the case, it would explain why your program works sometimes, and at other times it does not. As mentioned by a comment, the order with which you call the functions will determine what is on the stack (beneath the stack pointer) by the time the second function is called. Thus, it is perfectly plausible that when you call your unique function first and THEN your longest_name function, by sheer luck your name variable in your longest_name function is initialised to some "valid" memory location, which means you are able to write data to it.
What is happening is described as undefined behaviour. Essentially, this means that your program can sometimes perform as you expected, and sometimes do something completely different. Undefined behaviour happens when something in the program has not been written correctly, but that does not necessarily always make the program crash instantly. However, if you write a program correctly, you can avoid UB (undefined behaviour) completely.
Thus, you should never do something like:
char *whatever = "It was a sunny day.";
char *str;
strcpy(str, whatever);
... because you have not made the pointer str point anywhere valid, and you cannot copy data to a memory location that does not exist or one that cannot be accessed by your program.
In your case, your longest_name function should allocate memory and make the name pointer point to this allocated memory, before copying anything to it, such as:
name = malloc((strlen(stu[i]) + 1)*sizeof(char));
strcpy(name, stu[i]);
... remembering to free the memory after using it.
Remember, a string stored as a char* always needs to include an extra byte for the null terminator character '\0' or simply 0 in ASCII. This is where you have gone wrong in your vowel_count function, your declaration of name should be:
char name[len + 1];
Note also that by declaring name like that you are declaring a variable-length array (VLA), which can be tricky. If you need memory with a dynamic size (determined at runtime) it is usually better to use dynamic memory allocation (using malloc, and free for deallocation).
Furthermore, in your longest_name function, you don't need to allocate any extra memory, all you need is to make your name pointer point to the longest string, and print that out, such as:
void longest_name(char **stu){
size_t len = 0, mx = 0; // strlen returns a number of type size_t
char *name = NULL; // initialise to NULL since not pointing anywhere
printf("\n Address is %p\n", name); // this will either be zero or undefined
for(unsigned int i = 0; i < 10; i++){ // use unsigned as you start from zero
if(mx < (len = strlen(stu[i]))){ // assign len to only call strlen once
mx = len;
name = stu[i]; // simply make name point to the longest string
}
}
printf("Longest name is \"%s\" with length %zu\n", name, mx);
}
In conclusion, your program runs sometimes and at other times it crashes, becuase your name variable sometimes ends up pointing somewhere "valid", and sometimes it doesn't. You can fix this by always ensuring your pointers point somewhere valid before using them.

An error caused by "a WRITE memory access"

tbh I thought it wouldn't be hard to learn C seeing as I already know several other languages, but I'm having trouble with my code, and I can't seem to figure out how to fix these errors. I specialize in Python, so this is much different because of all the specifications for types, pointers, etc. Anyway, here's the code below, sorry, I would paste the error, but it won't allow me to copy paste. I was using some print functions and found the error to be coming from line 9, "*returnStr += *str";. Thanks in advance for any help.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
char *multiplyString(const char *str, int num){
char *returnStr = "";
for (int i = 0; i < num; i++){
*returnStr += *str;
}
return returnStr;
}
int main(void){
bool asking = true;
int height;
const char *symbol = "#";
while (asking == true){
height = get_int("How tall should the pyramid be? pick a number between 1 and 8: ");
if (8 >= height && height >= 1){
asking = false;
}
}
for (int i=1; i<=height; i++){
printf("%s %s\n", strcat(multiplyString(" ", height-i), multiplyString(symbol, i)), multiplyString(symbol, i));
}
}
Change multiplyString() to the following
char *multiplyString(const char *str, int num) {
// + 1 for null-terminator
char *returnStr = calloc(sizeof(*returnStr), strlen(str)*num + 1);
for (int i = 0; i < num; i++) {
strcat(returnStr, str);
}
return returnStr;
}
You were attempting to modify a string literal, which is forbidden in C. Secondly, += is not string concatenation in C; rather, it was trying to perform integer addition on the first character of returnStr.
To fix this, you dynamically allocate the proper amount of memory using calloc() (which also initializes the memory to 0, which is necessary for strcat()). Then, in each iteration, append the string using strcat() to the end of the new string.
Remember to free the strings returned by this function later in the program, as they are dynamically allocated.
Two problems:
First of all, returnStr is pointing to a string literal, which is really an array of read only characters. In this case an array of only a single character, being the string terminator '\0'
Secondly, *returnStr += *str; makes no sense. It's the same as returnStr[0] = returnStr[0] + str[0]. And since the destination (returnStr[0]) is a string literal, attempting to write to it leads to undefined behavior
If you want to create a new string containing num copies of str, then you need to create a new string containing at least num * strlen(str) + 1 characters, the +1 for the terminator. Then you need to use strcat to concatenate into that new string.
Also if you allocate memory dynamically (with e.g. malloc) then you need to make sure that the first element is initialized to the string terminator.

Double free error in string operations

Getting double free for below code, if a long string passed.
I tried all sorts of things. If I remove the free(s) line it goes away.
Not sure why it is happening.
void format_str(char *str1,int l,int o) {
char *s = malloc(strlen(str1)+1);
char *s1=s, *b = str1;
int i=0;
while(*str1!='\0') {
i++;
*s1++=*str1++;
if(i>=l) {
if(*str1!=',') {
continue;
}
*s1++=*str1++;
*s1++='\n';
for(i=0;i<o;i++) {
*s1++=' ';
}
i = 0;
}
}
*s1 = '\0';
strcpy(b,s);
free(s);
}
You probably aren't allocating enough space in s for the amount of data you're copying. I don't know what your logic is really doing, but I see stuff like
*s1++=*str1++;
*s1++='\n';
where you're copying more than one character into s (via s1) for a single character from str1.
And for the love of all that is computable, use better variable names!
You are almost certainly corrupting the heap. For example:
int main()
{
char original[1000] = "some,,,string,,, to,,,,format,,,,,";
printf( "original starts out %u characters long\n", strlen(original));
format_str( original, 6, 6);
printf( "original is now %u characters long\n", strlen(original));
return 0;
}
would require that the buffer allocated by malloc() be much larger than strlen(str1)+1 in size. Specifically, it would have to be at least 63 bytes long (as the function is coded in the question, the allocation has a size of 35 bytes).
If you need more specific help, you should describe what you're trying to do (such as what are the parameters l and o for?).
I'll try to reformat your code and guess-rename the variables for sake of mental health.
void format_str(char *str, int minlen, int indent)
{
char *tmpstr = malloc( strlen(str) + 1 ); // here is the problem
char *wrkstr = tmpstr, *savestr = str;
int count = 0;
while ( *str != '\0' ) {
count++;
*wrkstr++ = *str++;
if ( count >= minlen ) {
if ( *str != ',' ) {
continue;
}
*wrkstr++ = *str++;
*wrkstr++ = '\n';
for ( count = 0; count < indent; count++ ) {
*wrkstr ++= ' ';
}
count = 0;
}
}
*wrkstr = '\0';
strcpy(savestr,tmpstr);
free(tmpstr);
}
As others have pointed out you are not allocating sufficient space for the temporary string.
There are two other problems in your code (one of them is a major problem).
You probably should validate your arguments by checking that str is not NULL and maybe also that minlen and indent are not negative. This is not crucial however as a NULL str will just segfault (same behavior of standard library string functions) and values below 1 for minlen and/or indent just behave as if they were 0.
The major problem is with how much space you have in str. You blindly grow the string during formatting and then copy it back to the same memory. This is a buffer overflow waiting to happen (with potentially severe security implications, especially if str happens to point to the stack).
To fix it:
You should allocate sufficient space.
You should either return the allocated string and stipulate that the caller is responsible for freeing it (like strdup does) or add a parameter that specifies the space available in str and then avoid any work if it's not enought to store the formatted string.
The use case is a good example for the need of having a the possibility to do a dry-run.
I'd propose you modify your code like so:
ssize_t format_str(const char * input, int p1, int p2, char * output);
1 the target buffer shall be provided by the function caller via the parameter òutput passed to the function
2 the function shall return the number of characters written into the target buffer (negative values might indicated any sort of errors)
3 if the value passed as output is NULL the function does not copy anything but just parses the data referenced by input and determines how many characters would be written into the target buffer and returns this value.
To then use the conversion function one shall call it twice, like so:
char * input = "some,,test , data,,, ...";
int p1 = <some value>, p2 = <some other value>;
ssize_t ssizeOutput = format_str(input, p1, p2, NULL)
if (0 > ssizeOutput)
{
exit(EXIT_FAILURE);
}
else if (0 < ssizeOutput)
{
char * output = calloc(ssizeOutput, sizeof(*output));
if (!output)
{
exit(EXIT_FAILURE);
}
ssizeOutput = format_str(input, p1, p2, output);
if (0 > ssizeOutput)
{
exit(EXIT_FAILURE);
}
}
As others have pointed out, the heap memory is most likely getting corrupted because the code writes beyond the end of the allocated memory.
To verify whether memory is getting corrupted or not is simple. At beginning of function save the length of str1, let's name it 'len_before'. Before calling free(), get the string length again and let's name it 'len_after'.
if (len_after > len_before) then we have a fatal error.
A relatively simple fix would be to pass in the max length that str1 can grow up to,
malloc that much memory and stop before exceeding the max length, i.e. truncate it with a null but remain within the limit.
int len_before, len_after;
len_before = strlen(str1) + 1;
.
. /* Rest of the code. */
.
len_after = strlen(str1) + 1;
if (len_after > len_before) {
printf("fatal error: buffer overflow by %d bytes.\n", len_after - len_before);
exit(1);
}
free(s);

Allocating an array of an unknown size

Context: I'm trying to do is to make a program which would take text as input and store it in a character array. Then I would print each element of the array as a decimal. E.g. "Hello World" would be converted to 72, 101, etc.. I would use this as a quick ASCII2DEC converter. I know there are online converters but I'm trying to make this one on my own.
Problem: how can I allocate an array whose size is unknown at compile-time and make it the exact same size as the text I enter? So when I enter "Hello World" it would dynamically make an array with the exact size required to store just "Hello World". I have searched the web but couldn't find anything that I could make use of.
I see that you're using C. You could do something like this:
#define INC_SIZE 10
char *buf = (char*) malloc(INC_SIZE),*temp;
int size = INC_SIZE,len = 0;
char c;
while ((c = getchar()) != '\n') { // I assume you want to read a line of input
if (len == size) {
size += INC_SIZE;
temp = (char*) realloc(buf,size);
if (temp == NULL) {
// not enough memory probably, handle it yourself
}
buf = temp;
}
buf[len++] = c;
}
// done, note that the character array has no '\0' terminator and the length is represented by `len` variable
Typically, on environments like a PC where there are no great memory constraints, I would just dynamically allocate, (language-dependent) an array/string/whatever of, say, 64K and keep an index/pointer/whatever to the current end point plus one - ie. the next index/location to place any new data.
if you use cpp language, you can use the string to store the input characters,and access the character by operator[] , like the following codes:
std::string input;
cin >> input;
I'm going to guess you mean C, as that's one of the commonest compiled languages where you would have this problem.
Variables that you declare in a function are stored on the stack. This is nice and efficient, gets cleaned up when your function exits, etc. The only problem is that the size of the stack slot for each function is fixed and cannot change while the function is running.
The second place you can allocate memory is the heap. This is a free-for-all that you can allocate and deallocate memory from at runtime. You allocate with malloc(), and when finished, you call free() on it (this is important to avoid memory leaks).
With heap allocations you must know the size at allocation time, but it's better than having it stored in fixed stack space that you cannot grow if needed.
This is a simple and stupid function to decode a string to its ASCII codes using a dynamically-allocated buffer:
char* str_to_ascii_codes(char* str)
{
size_t i;
size_t str_length = strlen(str);
char* ascii_codes = malloc(str_length*4+1);
for(i = 0; i<str_length; i++)
snprintf(ascii_codes+i*4, 5, "%03d ", str[i]);
return ascii_codes;
}
Edit: You mentioned in a comment wanting to get the buffer just right. I cut corners with the above example by making each entry in the string a known length, and not trimming the result's extra space character. This is a smarter version that fixes both of those issues:
char* str_to_ascii_codes(char* str)
{
size_t i;
int written;
size_t str_length = strlen(str), ascii_codes_length = 0;
char* ascii_codes = malloc(str_length*4+1);
for(i = 0; i<str_length; i++)
{
snprintf(ascii_codes+ascii_codes_length, 5, "%d %n", str[i], &written);
ascii_codes_length = ascii_codes_length + written;
}
/* This is intentionally one byte short, to trim the trailing space char */
ascii_codes = realloc(ascii_codes, ascii_codes_length);
/* Add new end-of-string marker */
ascii_codes[ascii_codes_length-1] = '\0';
return ascii_codes;
}

Seg fault on my own version of getline

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'.

Resources