C - Conditional jump or move depends on uninitialised value(s) - c

I'm getting this error from Valgrind when I try to run my program :
==23152== Conditional jump or move depends on uninitialised value(s)
==23152== at 0x4C2D8D0: strcmp (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==23152== by 0x40096C: str_lower_cmp (functions.c:41)
==23152== by 0x400BB8: list_sort (list_sort.c:34)
==23152== by 0x400CC7: get_wdir_content (working_dir.c:27)
==23152== by 0x400C27: main (main.c:18)
==23152== Uninitialised value was created by a heap allocation
==23152== at 0x4C2C27B: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==23152== by 0x400D4C: xmalloc (xfunctions.c:35)
==23152== by 0x400886: lower_string (functions.c:20)
==23152== by 0x400945: str_lower_cmp (functions.c:39)
==23152== by 0x400BB8: list_sort (list_sort.c:34)
==23152== by 0x400CC7: get_wdir_content (working_dir.c:27)
==23152== by 0x400C27: main (main.c:18)
==23152==
==23152== Conditional jump or move depends on uninitialised value(s)
==23152== at 0x400BBB: list_sort (list_sort.c:34)
==23152== by 0x400CC7: get_wdir_content (working_dir.c:27)
==23152== by 0x400C27: main (main.c:18)
==23152== Uninitialised value was created by a heap allocation
==23152== at 0x4C2C27B: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==23152== by 0x400D4C: xmalloc (xfunctions.c:35)
==23152== by 0x400886: lower_string (functions.c:20)
==23152== by 0x400945: str_lower_cmp (functions.c:39)
==23152== by 0x400BB8: list_sort (list_sort.c:34)
==23152== by 0x400CC7: get_wdir_content (working_dir.c:27)
==23152== by 0x400C27: main (main.c:18)
==23152==
Though I can't say what is wrong, I'm guessing it comes from list_sort.c :
t_llist *list_sort(t_llist *list)
{
struct s_node *tmp;
tmp = list->head;
while (tmp != NULL)
{
if (tmp->next != NULL)
{
if (!tmp->name || !tmp->next->name)
printf("Reached.\n");
if (str_lower_cmp(tmp->name, tmp->next->name) > 0)
{
data_swap(tmp, tmp->next);
tmp = list->head;
}
else
tmp = tmp->next;
}
else
return (list);
}
return (list);
}
Does that mean that at some point, the tmp->name or tmp->next->name value is uninitialised ?
EDIT (the functions.c code)
char *lower_string(char *s)
{
char *res;
int i;
i = 0;
res = xmalloc(sizeof(*res) * strlen(s) + 1);
while (s[i])
{
if (s[i] >= 'A' && s[i] <= 'Z')
res[i] = s[i] + 32;
else
res[i] = s[i];
i++;
}
s[i] = '\0';
return (res);
}
int str_lower_cmp(char *s1, char *s2)
{
char *tmp1;
char *tmp2;
int res;
tmp1 = lower_string(s1);
tmp2 = lower_string(s2);
res = strcmp(tmp1, tmp2);
free(tmp1);
free(tmp2);
return (res);
}

Initially valgrind is telling you that you're running strcmp with a memory address allocated with malloc, coming from function lower_string, but that has no initial value assigned.
This would mean an undefined behavior, meaning that, depending on your code, can be very dangerous because can lead to unexpected results.
I would suggest using calloc in lower_string.
Edit: your're setting s[i] to 0 instead of res[i] (the pointer you've allocated and returning). On the other hand, I would suggest using calloc and checking res!=NULL

Your error is here in lower_string You're not terminating the string you're allocating:
char *lower_string(char *s)
{
char *res;
int i;
i = 0;
res = xmalloc(sizeof(*res) * strlen(s) + 1);
while (s[i])
{
if (s[i] >= 'A' && s[i] <= 'Z')
res[i] = s[i] + 32;
else
res[i] = s[i];
i++;
}
s[i] = '\0'; // THIS IS WRONG
return (res);
}
The marked line should be this:
res[i] = '\0'; // THIS IS RIGHT
And note, this would be caught if you properly passed the input string as a const parameter:
char *lower_string(const char *s) // MAKE PARAM CONST
Doing so would have failed to compile because your s[i] = '\0' assignment would have violated the const-condition. General rule, unless you need to modifying something passed as a a by-address parameter, make it const

when the "char *s" passed to lower_string is an empty string you'd have a crashing program too. Calling calloc as jcm said would help fix that problem

Related

Error when coding my own implementation of realloc()

I am contacting you because I need to code the functions realloc / strlen (signed and unsigned) / memcpy.
I have recoded these functions but it's doens't working and now I have valgrind errors like Conditional jump or move depends on uninitialised value(s).
Can you help me to fix the problem?
Here are the functions:
void *my_realloc(void *ptr, size_t size)
{
unsigned char *old_ptr = (unsigned char *)ptr;
void *new_ptr = NULL;
size_t old_size = 0;
if (!ptr) {
return malloc(size);
}
if (size == 0) {
free(ptr);
return NULL;
}
old_size = my_strlen_unsigned(old_ptr) + 1;
new_ptr = malloc(size);
if (!new_ptr) {
return NULL;
}
my_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
free(ptr);
return new_ptr;
}
void *my_memcpy(void *restrict dest, const void *restrict src, size_t n)
{
if (!dest || !src) return NULL;
unsigned char *d = dest;
const unsigned char *s = src;
size_t i = 0;
while (i < n && i < my_strlen_unsigned(s)) {
*d = *s;
d++;
s++;
i++;
}
return dest;
}
size_t my_strlen_unsigned(const unsigned char *s)
{
size_t count = 0;
if (s != NULL) {
while (*s != 0) {
count++;
s++;
}
}
return count;
}
size_t my_strlen(const char *s)
{
size_t count = 0;
if (s != NULL) {
while (*s != 0) {
count++;
s++;
}
}
return count;
}
Currently I test these functions via the following functions:
char *my_str_clean(char *str)
{
char *ptr = str;
char *new_str = malloc(1);
size_t i = 0;
if (!new_str)
return (NULL);
while (*ptr) {
if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
new_str = my_realloc(new_str, (sizeof(char) * (i + 2)));
new_str[i] = *ptr;
i++;
}
ptr++;
}
new_str[i] = '\0';
free(str);
return (new_str);
}
int main(int argc, char **argv, char **env)
{
char *test = malloc(15);
test[0] = 'l';
test[1] = 's';
test[2] = ' ';
test[3] = ' ';
test[4] = ' ';
test[5] = ' ';
test[6] = ' ';
test[7] = ' ';
test[8] = ' ';
test[9] = '-';
test[10] = 'l';
test[11] = ' ';
test[12] = '-';
test[13] = 'a';
test[14] = '\0';
char *clean = NULL;
clean = my_str_clean(test);
printf("%s\n", clean);
free(clean);
}
Here is the valgrid report :
==28637== Memcheck, a memory error detector
==28637== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==28637== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==28637== Command: ./mysh
==28637==
==28637== Conditional jump or move depends on uninitialised value(s)
==28637== at 0x109BB2: my_strlen_unsigned (my_strlen_unsigned.c:14)
==28637== by 0x109B2B: my_realloc (my_realloc.c:35)
==28637== by 0x1096B5: my_str_clean (my_str_clean.c:19)
==28637== by 0x10954A: main (minishell.c:55)
==28637== Uninitialised value was created by a heap allocation
==28637== at 0x4841888: malloc (vg_replace_malloc.c:381)
==28637== by 0x109660: my_str_clean (my_str_clean.c:13)
==28637== by 0x10954A: main (minishell.c:55)
==28637==
==28637== Conditional jump or move depends on uninitialised value(s)
==28637== at 0x109BB2: my_strlen_unsigned (my_strlen_unsigned.c:14)
==28637== by 0x109ABC: my_memcpy (my_memcpy.c:16)
==28637== by 0x109B73: my_realloc (my_realloc.c:40)
==28637== by 0x1096B5: my_str_clean (my_str_clean.c:19)
==28637== by 0x10954A: main (minishell.c:55)
==28637== Uninitialised value was created by a heap allocation
==28637== at 0x4841888: malloc (vg_replace_malloc.c:381)
==28637== by 0x109660: my_str_clean (my_str_clean.c:13)
==28637== by 0x10954A: main (minishell.c:55)
==28637==
==28637== Conditional jump or move depends on uninitialised value(s)
==28637== at 0x1097A5: my_strlen (my_strlen.c:18)
==28637== by 0x10962C: my_put_str (my_put_str.c:21)
==28637== by 0x10955F: main (minishell.c:56)
==28637== Uninitialised value was created by a heap allocation
==28637== at 0x4841888: malloc (vg_replace_malloc.c:381)
==28637== by 0x109B3F: my_realloc (my_realloc.c:36)
==28637== by 0x1096B5: my_str_clean (my_str_clean.c:19)
==28637== by 0x10954A: main (minishell.c:55)
==28637==
l==28637==
==28637== HEAP SUMMARY:
==28637== in use at exit: 0 bytes in 0 blocks
==28637== total heap usage: 8 allocs, 8 frees, 43 bytes allocated
==28637==
==28637== All heap blocks were freed -- no leaks are possible
==28637==
==28637== For lists of detected and suppressed errors, rerun with: -s
==28637== ERROR SUMMARY: 18 errors from 3 contexts (suppressed: 0 from 0)
Invalid assumption:
void *my_realloc(void *ptr, size_t size)
{
unsigned char *old_ptr = (unsigned char *)ptr;
You're breaking the realloc() specification here. realloc() doesn't assume that the ptr would always be pointing to a string. It is type-agnostic.
And the cast is redundant. There's an implicit conversion from void * to any other pointer type.
Undefined behaviour:
old_size = my_strlen_unsigned(old_ptr) + 1;
The standard, or your my_strlen_unsigned version of it, works with strings, i.e. the pointer is assumed to be pointing to an array of char terminated by a null-byte. An array of ints will not be terminated with a null-byte. Your my_realloc() thus invokes undefined behaviour.
Possible fix:
You can't determine the old size of the block pointed to by ptr and reimplement realloc(), without at least reimplementing your own malloc() and free(), portably. But you can take the old size as the third argument. (But the standard realloc() only takes two. So would this be a conforming implementation?)
Here's glibc's implementation of it: malloc().c
Here's musl C implementation of it: malloc().c
And here's my toy example that attempts to emulate the standard realloc() to some extent:
/**
* #brief The my_realloc() function shall deallocate the old object pointed to
* by ptr and return a pointer to a new object that has the size specified by new_size.
*
* #param ptr - A pointer to a block of memory to resize.
* #param old_size - The size of the block pointed to by ptr.
* #param new_size - The size to resize by.
*
* If ptr is a null pointer, my_realloc() shall be equivalent to
* malloc() for the specified new_size.
*
* If ptr does not match a pointer returned earlier by calloc(),
* malloc(), or realloc() or if the space has previously been
* deallocated by a call to free() or realloc(), the behavior is undefined.
*
* #return Upon successful completion, my_realloc() shall return a pointer to the moved allocated space.
* If size and ptr both evaluate to 0, my_realloc() shall return a
* NULL pointer with errno set to [EINVAL].
* If there is not enough available memory, my_realloc() shall return a
* NULL pointer and set errno to [ENOMEM].
*
* If my_realloc() returns a NULL pointer, the memory referenced by ptr shall not be changed.
*
* #warning my_realloc() may return NULL to indicate an error. For that reason, a different pointer variable
* must be used to hold it's return value. Otherwise, you risk overwriting the original ptr with NULL and
* losing your only reference to the original block of memory.
*/
void *my_realloc (void *ptr, size_t new_size, size_t old size)
{
if (!ptr) {
return malloc (new_size);
}
if (!new_size) {
errno = EINVAL;
return 0;
}
if (new_size <= old_size) {
return ptr;
}
/* As a last resort, allocate a new chunk and copy to it.
*/
void *new = 0;
if (new_size > old_size) {
new = malloc (new_size);
if (!new) {
return 0;
}
memcpy (new, ptr, old_size);
free (ptr);
}
return new;
}
You can also find a sample implementation in chapter 8 of K&R.
Side-notes:
char *new_str = malloc(1);
new_str = my_realloc(..);
You risk losing access to the original memory allocated through malloc() here. If my_realloc() returned NULL, new_str would be assigned it's result and you would cause a memory leak in your program.
Furthermore, the memory returned by malloc() is uninitialized. And your code invokes undefined behaviour by calling my_strlen() underneath my_realloc() on a pointer that is uninitialized. Hence the warnings.
Your are assuming that data in the chunk is a correctly terminated C string, and using my_strlen_unsigned() to calculate the old size. You cannot do this since you cannot know what kind of data is stored in a chunk. The only real solution is to also remember the chunk size somehow, and pass it as parameter to your function.
If you cannot do this, there might be some other ways to get around the problem. For example, the malloc_usable_size() function is a GNU extension available in glibc (GNU libc) that can be used to query the size of an existing chunk, so you can use that instead of your my_strlen_unsigned(). Note however that if you use dynamic linking (the default when compiling) this will make your program non-portable and will only work on systems using glibc. You may wish to link your program statically in this case.
Assuming that the other functions in your code (such as my_memcpy()) are correctly implemented, a correct implementation of my_realloc() would be as follows:
void *my_realloc(void *ptr, size_t size)
{
void *new_ptr;
size_t old_size;
if (!ptr)
return malloc(size);
if (size == 0) {
free(ptr);
return NULL;
}
new_ptr = ptr;
old_size = malloc_usable_size(ptr);
if (size != old_size) {
new_ptr = malloc(size);
if (!new_ptr)
return NULL;
my_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
free(ptr);
}
return new_ptr;
}

C programming problem in dynamic memory allocation

The problem should be simple, but I have spent hours on this and cannot see what is wrong in my logic. The output works as it should, but Valgrind prints memory issues that should be fixed. I have added the origdest = (char*)realloc(origdest, strlen(origdest) + i * sizeof(char)); code to the while loop, my question is why doesn't this dynamically adjust the memory? The exact error given by Valgrind is
==9== Invalid write of size 1
==9== at 0x1087E2: mystrcat (mystrcat.c:18)
==9== by 0x10883C: main (mystrcat.c:34)
==9== Address 0x522d046 is 6 bytes inside a block of size 7 free'd
==9== at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9== by 0x1087C2: mystrcat (mystrcat.c:17)
==9== by 0x10883C: main (mystrcat.c:34)
==9== Block was alloc'd at
==9== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9== by 0x108811: main (mystrcat.c:31)
char *mystrcat(char *dest, const char *src)
{
char *origdest = dest;
while(*dest) {
dest++;
}
int i = 1;
while (*src) {
origdest = (char*)realloc(origdest, strlen(origdest) + i * sizeof(char));
*dest++ = *src++; // Copies character and increases/moves pointer
i++;
}
*dest = 0;
return origdest;
}
int main(void)
{
char *str = malloc(7);
strcpy(str, "Mydogs");
str = mystrcat(str, "arecool");
printf("%s\n", str);
free(str);
}
This statement:
Address 0x522d046 is 6 bytes inside a block of size 7 free'd is saying that the realloc() called following these statements results in the old pointer pointing to freed memory.
after this segment:
char *origdest = dest;
while(*dest) {
dest++;
}
EDIT to address comment "what is specifically wrong with the code and what could be changed to make it work?"
The explanation of my first observation above is that once the pointer to allocated memory is moved, as you have done, the memory allocation tables no longer have an accurate location of that memory, making that memory un-freeable.
Your stated goal here is to create a version of strcat(), so using realloc() is a reasonable approach, but to use it safely allocate the new memory into a temporary buffer first, then if allocation fails, the original memory location still exists, and can be freed.
One other small change that makes a big difference is how i is initialized. If 1 is used, it places the beginning of the second string one extra position further in memory, leaving a \0 character just after the first string, effectively making it the end of the resultant string. i.e. you would never see the appended part of the string:
In memory it would look like this:
|M|y|d|o|g|s|\0|a|r|e|c|o|o|l|
Then over-flow your buffer when attempting to place another NULL terminator at the and of the concatenated buffer, resulting in undefined behavior.
The following adaptation of your code illustrates these, along with some other simplifications:
char *mystrcat(char *dest, const char *src)
{
char *temp = NULL;
int i = 0;//changed from i = 1 as first location to
//copy to is len1, not len1 + 1
//Note, starting at len1 + 1 would leave a NULL character
//after "Mydogs", effectively ending the string
//the following are simplifications for use in realloc()
int len1 = strlen(dest);
int len2 = strlen(src);
//call once rather than in a loop. It is more efficient.
temp = realloc(dest, len1+len2+1);//do not cast return of realloc
if(!temp)
{
//handle error
return NULL;
}
dest = temp;
while(*src)
{
dest[len1 + i] = *src;
i++;
src++;
}
dest[len1 + i] = 0;//add null terminator
return dest;
}
int main(void)
{
char *temp = NULL;
char *str = malloc(7);
if(str)//always a good idea to test pointer before using
{
strcpy(str, "Mydogs");
temp = mystrcat(str, "arecool");
if(!temp)
{
free(str);
printf("memory allocation error, leaving early");
return 0;
}
str = temp;
printf("%s\n", str);
free(str);
}
return 0;
}
Why it is not correct to cast the return of c-m-realloc() in C.
Here you move to the end of the original string:
while(*dest)
dest++;
Here you allocate some new memory, but dest still points to the end of the original string. So you are overwriting memory after the end of the original string. Since you are reallocating, the original string may not even exist anymore at the previous location you are writing to, because realloc can move the data to a completely new location.
while (*src)
{
origdest = (char*)realloc(origdest, strlen(origdest) + i * sizeof(char));
*dest++ = *src++; // Copies character and increases/moves pointer
i++;
}

Memory leak in a join string function

I'm trying to make a function that join 2 strings (str1 & str2) into a new one (str3) seperated by a char seperator. Unfortunately I have a memory leak from this function and I don't really know why since I free str3 at the end.
Example: str_join_string("ABC","DEF",'|') ---> "ABC|DEF"
Here's the code:
char *str_join_string(const char *str1, const char *str2, char separator) {
char *str3;
size_t len = str_length(str1)+ str_length(str2)+1;
size_t i = 0;
size_t j = 0;
str3 = (char * )calloc(len, sizeof(char));
if(str3 == NULL){
printf("Impossible d'allouer la mémoire");
return NULL;
}
while(str1[i] != '\0' && str1 != NULL){
str3[i] = str1[i];
i++;
}
str3[i] = separator;
i+=1;
while(str2[j] != '\0' && str2 != NULL){
str3[i+j] = str2[j];
j++;
}
str3[len] = '\0';
return str3;
}
I will add that I can't use any function like strcat() or anything that comes from string.h.
What Valgrind shows:
==4300== Searching for pointers to 3 not-freed blocks
==4300== Checked 131,560 bytes
==4300==
==4300== 4 bytes in 1 blocks are definitely lost in loss record 1 of 3
==4300== at 0x4C31B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4300== by 0x13E3B2: str_join_string (stringslib.c:238)
==4300== by 0x13E545: str_join_array (stringslib.c:283)
==4300== by 0x137065: JoinArrayTest_OneEmpty_Test::TestBody() (stringslib_test.cc:779)
==4300== by 0x1652A9: HandleSehExceptionsInMethodIfSupported<testing::Test, void> (gtest.cc:2611)
==4300== by 0x1652A9: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (gtest.cc:2647)
==4300== by 0x15A9DE: testing::Test::Run() [clone .part.658] (gtest.cc:2686)
==4300== by 0x15AC61: Run (gtest.cc:2677)
==4300== by 0x15AC61: testing::TestInfo::Run() [clone .part.659] (gtest.cc:2863)
==4300== by 0x15B350: Run (gtest.cc:2837)
==4300== by 0x15B350: testing::TestSuite::Run() [clone .part.660] (gtest.cc:3017)
==4300== by 0x15BAF4: Run (gtest.cc:2997)
==4300== by 0x15BAF4: testing::internal::UnitTestImpl::RunAllTests() (gtest.cc:5709)
==4300== by 0x165769: HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool> (gtest.cc:2611)
==4300== by 0x165769: bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (gtest.cc:2647)
==4300== by 0x15AD82: testing::UnitTest::Run() (gtest.cc:5292)
==4300== by 0x11C08E: RUN_ALL_TESTS (gtest.h:2485)
==4300== by 0x11C08E: main (stringslib_test.cc:799)
==4300==
I hope that you can help me because I'm really lost right now.
------EDIT------
Yes I completely forgot to add the caller which is where I free the memory:
TEST(JoinStringTest, Simple) {
char *buf = str_join_string("ABC", "XYZ", '|');
ASSERT_TRUE(buf != NULL);
EXPECT_EQ(buf[0], 'A');
EXPECT_EQ(buf[1], 'B');
EXPECT_EQ(buf[2], 'C');
EXPECT_EQ(buf[3], '|');
EXPECT_EQ(buf[4], 'X');
EXPECT_EQ(buf[5], 'Y');
EXPECT_EQ(buf[6], 'Z');
EXPECT_EQ(buf[7], '\0');
free(buf);
}
For starters the function invokes undefined behavior because there is not enough memory allocated for the result string.
Instead of
size_t len = str_length(str1)+ str_length(str2)+1;
you have to write
size_t len = str_length(str1)+ str_length(str2)+2;
Moreover this statement
str3[len] = '\0';
also tries to write to the memory outside the allocated array.
It seems you mean
str3[i + j] = '\0';
Though you could remove this statement because you are using the function calloc that sets the allocated memory with zeroes. On the other hand, using calloc is inefficient in the function context.
And statements after the return statement
//...
return str3;
free(str3);
str3 = NULL;
are never executed.
Pay attention to that conditions like in this for loop
while(str1[i] != '\0' && str1 != NULL){
do not make a sense. At least the operands of the logical AND operator shall be exchanged like
while( str1 != NULL && str1[i] != '\0' ){
Though in any case the condition str1 != NULL is redundant or you could check the condition before the loop in an if statement.
Here is a demonstrative program that shows how the function can be defined (without using standard string functions) and called.
#include <stdio.h>
#include <stdlib.h>
size_t str_length( const char *s )
{
size_t n = 0;
while ( *s++ ) ++n;
return n;
}
char * str_join_string( const char *s1, const char *s2, char separator )
{
size_t n = str_length( s1 ) + str_length( s2 ) + sizeof( separator ) + 1;
char *s3 = malloc( n );
if ( s3 )
{
char *p = s3;
for ( ; *s1; ++s1 ) *p++ = *s1;
*p++ = separator;
for ( ; *s2; ++s2 ) *p++ = *s2;
*p = '\0';
}
return s3;
}
int main(void)
{
char *s = str_join_string( "ABC", "DEF", '|' );
if ( s ) puts( s );
free( s );
return 0;
}
The program output is
ABC|DEF
It is the user of the function shall provide arguments not equal to NULL.
return str3;
free(str3);
Looks at this snippet, do you think free() is ever going to get called?
Maybe because you return your function first and THEN free your buffer!?
https://learn.microsoft.com/en-us/cpp/c-language/return-statement-c?view=vs-2019
A return statement ends the execution of a function, and returns
control to the calling function

Valgrind : Conditional jump or move depends on uninitialised value(s) occurs even I initializing the memory

I am very struggle to solve this problem.
After I initializing a allocated memory, valgrind said that "conditional jump or move depends on uninitialised value".
So, here is my code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
int st_a;
int st_b;
char st_c:
char st_d:
} TEST_ST;
int inner_func(int *a, TEST_ST *st_a)
{
//memset(st_a, 0, sizeof(TEST_ST));
if(a[0] > 0)
{
printf("inner_func: bigger than zero\n");
}
else
{
printf("inner_func: else\n");
}
st_a->st_a = 1;
}
int main()
{
int *a;
int *b;
TEST_ST *st_a;
a = (int *)malloc(sizeof(int) * 10);
b = (int *)malloc(sizeof(int) * 10);
memset(a, 0, sizeof(int) * 10);
//memset(b, 0, sizeof(int) * 10);
st_a = (TEST_ST *)malloc(sizeof(TEST_ST));
a[0] = 1;
if(a[9] > 0)
{
printf("Bigger than zero\n");
}
else
{
printf("Smaller than zero or equal\n");
}
inner_func(b, st_a);
free(st_a);
free(b);
free(a);
return 0;
}
And here is a valgrind logs.
I don't understand why valgrind said like the first line in the picture.
Anybody can help me?
The value you're reading was not in fact initialized.
The line in question is in the function inner_func:
if(a[0] > 0)
The a is as parameter to the function, which was called like this:
inner_func(b, st_a);
So a in the function is the same as b in main. b points to memory returned by malloc, but that memory is never initialized.
Uncomment the line where you call memset on b.
Run valgrind like this and post results i'll edit with the answer but this should pinpoint where is the error:
valgrind --tool=memcheck --track-origins=yes <program_path>
You have assigned "char st_c:" and "char st_d:" change it to ';' instead of ':'

Function to split string sometimes gives segmentation fault

I have the following function to split a string. Most of the time it works fine, but sometimes it randomly causes a segmentation fault.
char** splitString(char* string, char* delim){
int count = 0;
char** split = NULL;
char* temp = strtok(string, delim);
while(temp){
split = realloc(split, sizeof(char*) * ++count);
split[count - 1] = temp;
temp = strtok(NULL, " ");
}
int i = 0;
while(split[i]){
printf("%s\n", split[i]);
i++;
}
split[count - 1][strlen(split[count - 1]) - 1] = '\0';
return split;
}
You have a number of subtle issues, not the least of which your function will segfault if you pass a string literal. You need to make a copy of the string you will be splitting as strtok modifies the string. If you pass a string literal (stored in read-only memory), your compiler has no way of warning unless you have declared string as const char *string;
To avoid these problems, simply make a copy of the string you will tokeninze. That way, regardless how the string you pass to the function was declared, you avoid the problem altogether.
You should also pass a pointer to size_t as a parameter to your function in order to make the number of token available back in the calling function. That way you do not have to leave a sentinel NULL as the final pointer in the pointer to pointer to char you return. Just pass a pointer and update it to reflect the number of tokens parsed in your function.
Putting those pieces together, and cleaning things up a bit, you could use the following to do what you are attempting to do:
char **splitstr (const char *str, char *delim, size_t *n)
{
char *cpy = strdup (str), *p = cpy; /* copy of str & pointer */
char **split = NULL; /* pointer to pointer to char */
*n = 0; /* zero 'n' */
for (p = strtok (p, delim); p; p = strtok (NULL, delim)) {
void *tmp = realloc (split, sizeof *split * (*n + 1));
if (!tmp) { /* validate realloc succeeded */
fprintf (stderr, "splitstr() error: memory exhausted.\n");
break;
}
split = tmp; /* assign tmp to split */
split[(*n)++] = strdup (p); /* allocate/copy to split[n] */
}
free (cpy); /* free cpy */
return split; /* return split */
}
Adding a short example program, you could do the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **splitstr (const char *str, char *delim, size_t *n)
{
char *cpy = strdup (str), *p = cpy; /* copy of str & pointer */
char **split = NULL; /* pointer to pointer to char */
*n = 0; /* zero 'n' */
for (p = strtok (p, delim); p; p = strtok (NULL, delim)) {
void *tmp = realloc (split, sizeof *split * (*n + 1));
if (!tmp) { /* validate realloc succeeded */
fprintf (stderr, "splitstr() error: memory exhausted.\n");
break;
}
split = tmp; /* assign tmp to split */
split[(*n)++] = strdup (p); /* allocate/copy to split[n] */
}
free (cpy); /* free cpy */
return split; /* return split */
}
int main (void) {
size_t n = 0; /* number of strings */
char *s = "My dog has fleas.", /* string to split */
*delim = " .\n", /* delims */
**strings = splitstr (s, delim, &n); /* split s */
for (size_t i = 0; i < n; i++) { /* output results */
printf ("strings[%zu] : %s\n", i, strings[i]);
free (strings[i]); /* free string */
}
free (strings); /* free pointers */
return 0;
}
Example Use/Output
$ ./bin/splitstrtok
strings[0] : My
strings[1] : dog
strings[2] : has
strings[3] : fleas
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to write beyond/outside the bounds of your allocated block of memory, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/splitstrtok
==14471== Memcheck, a memory error detector
==14471== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==14471== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==14471== Command: ./bin/splitstrtok
==14471==
strings[0] : My
strings[1] : dog
strings[2] : has
strings[3] : fleas
==14471==
==14471== HEAP SUMMARY:
==14471== in use at exit: 0 bytes in 0 blocks
==14471== total heap usage: 9 allocs, 9 frees, 115 bytes allocated
==14471==
==14471== All heap blocks were freed -- no leaks are possible
==14471==
==14471== For counts of detected and suppressed errors, rerun with: -v
==14471== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
split[count - 1][strlen(split[count - 1]) - 1] = '\0';
should look like
split[count - 1] = NULL;
You don't have anything allocated there so that you can access it and put '\0'.
After that put that line before while(split[i]) so that the while can stop when it reaches NULL.
The function strtok is not reentrant, use strtok_r() function this is a reentrant version strtok().

Resources