I'm going to splice a long string using ast_malloc and ast_realloc functions.When I load this module it will occur segment fault.What's the problem? much thanks!
Asterisk's output like this:
*** glibc detected *** asterisk: double free or corruption (fasttop): 0x00007fd56c000b20 ***
======= Backtrace: =========
/lib64/libc.so.6[0x33b9675f3e]
/lib64/libc.so.6[0x33b9678dd0]
/lib64/libc.so.6[0x33b967bd60]
/lib64/libc.so.6(realloc+0x158)[0x33b967c058]
/usr/lib64/asterisk/modules/app_test2.so(func+0x25)[0x7fd5330d19c5]
/usr/lib64/asterisk/modules/app_test2.so(+0xa92)[0x7fd5330d1a92]
asterisk[0x5175d5]
asterisk(ast_load_resource+0x34)[0x51a924]
asterisk[0x4c0f80]
asterisk(ast_cli_command_full+0x162)[0x4c3ad2]
asterisk(ast_cli_command_multiple_full+0x7c)[0x4c3cfc]
asterisk[0x450b9a]
asterisk[0x5c41cb]
/lib64/libpthread.so.0[0x33b9a07aa1]
/lib64/libc.so.6(clone+0x6d)[0x33b96e8aad]
And the codes shown below:
#include "asterisk.h"
#include "asterisk/module.h"
#include "asterisk/strings.h"
#include "asterisk/json.h"
#include "asterisk/res_odbc.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define AST_MODULE "app_test"
void func(char* test, const char* src)
{
char* new_test = (char*)ast_realloc(test, strlen(test) + 32);
if ( NULL == new_test ){
ast_log(LOG_ERROR, "realloc error\n");
return;
}
test = new_test;
strcat(test, src);
}
static int load_module(void)
{
int i;
char* test;
char* src = "MingYuan";
test = (char*)ast_malloc(16);
for (i = 0; i < 6; i++) {
func(test, src);
}
ast_log(LOG_DEBUG, "\n++++\n%s\n*******\n", test);
ast_free(test);
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
return 0;
}
AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "APP_TEST",
.load = load_module,
.unload = unload_module
);
strlen(test) within func is meaningless as you haven't copied a string into test; this is likely to cause erratic behaviour. My suggestion is, after test = (char*)ast_malloc(16); you probably want something like strcpy(test, ""); or test[0] = '\0';...
As a result of pass-by-value semantics in C, these changes won't be visible to func's caller:
test = new_test;
strcat(test, src);
The caller will have an old, invalidated pointer value somewhere in a pointer variable, which is sure to cause more erratic behaviour when used again later...
My suggestion is that you develop functions so that the caller allocates memory; this won't just fix your problem, but it'll allow you to be much more flexible with how your memory is allocated, much more often.
Related
I am practicing C language.
I wanted to use dynamic allocation to use only the size of the string I input as memory and check whether the input string was properly saved.
So, I wrote the following code using malloc and realloc functions.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void str_copy(char* str_array_f) {
void* tmp;
char buf;
unsigned char arr_size = 1;
unsigned char arr_cur = 0;
while ((buf = getchar())) {
if (buf == '\n') {
break;
}
str_array_f[arr_cur++] = (char)buf;
tmp = realloc(str_array_f, ((arr_size++) * sizeof(char)) + sizeof(char));
if (tmp != 0) {
str_array_f = tmp;
}
else {
printf("memory leak error occur! \n");
break;
}
}
str_array_f[arr_size - 1] = 0x00;
}
void main() {
int contiune = 1;
while (contiune) {
char* str_array = malloc(sizeof(char) + sizeof(char));
printf("Please type something : ");
str_copy(str_array);
printf("'str_array' have this : %s \n", str_array);
printf("-------------------------------------------------\n");
if (str_array[0] == '1') {
contiune = 0;
}
free(str_array);
}
}
And, as a result of the performance,
The following problems have occurred.
Strange values sometimes appear from the 5th character of the intermittently printed value
(To reproduce this issue, it is recommended to remove the while loop and try repeatedly)
In the case of repeatedly receiving a value by using the while loop, an error occurs after 4 repetitions.
If the allocated memory of tmp, which is a void type pointer, is released after line 22(e.g., 'free(tmp);'), when executed, no output and an error occurs immediately.
For the above 3 problems, I am not sure what is the cause and how to fix it.
Please let me know if there is a solution.
And, if there is a bad coding method in my code in terms of efficiency or various aspects, I would appreciate it if you let me know.
*Programming execution environment : Visual studio 2019
to explain what you're doing wrong I'm going to use a minimal example here
void change_x(int x) {
x = 2;
}
int main() {
int x = 1;
change_x(x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
here the integer x is copied when the function is called and changing it won't really change the x in main. similarly you are doing in your code that str_array_f = tmp; it really won't change the str_array but the copied value. and you're trying to free a pointer that was reallocated before.
the fix for the example above is not to pass the value x instead pass the address of x (which is equivalent to pass by reference in other languages)
void change_x(int* x) {
*x = 2;
}
int main() {
int x = 1;
change_x(&x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
and for your code
void str_copy(char** str_array_f) {...} // change the parameter
*str_array_f = tmp; // de reference and use it.
str_copy(&str_array); // call with it's address
And one more thing, don't reallocate more often it's not efficient. instead just just allocate your "array" type with a minimum size and when it's filled reallocate it with the size of 2 times of it (or 1.5 if you like)
I'm trying to use a "fixed memory scheme" and pre-allocate memory & reuse it via alloc, init, free fashion as many times as possible.
free() will called at shutdown only, but I want to test many iterations.
Although I call my alloc function bn_tree_alloc_node_space_heap() & init function bn_tree_init_node_heap(), I can only call free function bn_tree_free_node_space once.
Below is a complete reproducible snippet of my memory management, maint_test.c:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <float.h>
#define BN_TREE_HEAP_SIZE 100
/*variables internal*/
typedef struct bntree_internals;
/*bn_tree_node is single bntree_t leaf*/
typedef struct bn_tree_node {
struct bn_tree_node* left;
struct bn_tree_node* right;
float* dataset;
float distance_to_neighbor;
int visited;
int heap_index;
} bn_tree_node;
/*tree*/
typedef struct {
/*in order to keep track of the bn-tree root*/
bn_tree_node* _root;
/*pointer to internal variables struct*/
struct bntree_internals* _internals;
} bntree_t;
/*bn tree leaf nodes heap*/
bn_tree_node* node_processing_space = NULL;
/*leaf nodes*/
void bn_tree_alloc_node_space_heap(int max_dimensions);
bn_tree_node*
get_pre_allocated_bn_tree_node_heap();
void bn_tree_init_node_heap(bn_tree_node* nodes, int max_dimensions);
void bn_tree_free_node_space(bn_tree_node* nodes);
int main(int argc, char** argv) {
/*PROBLEM:called the alloc,init,free cycle several times, problem,
getting seg fault on 2nd call of free()*/
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
return (EXIT_SUCCESS);
}
void bn_tree_alloc_node_space_heap(int max_dimensions) {
if (NULL == node_processing_space) {
node_processing_space = (bn_tree_node*) calloc(BN_TREE_HEAP_SIZE, sizeof (bn_tree_node));
//TODO: bn_tree_set_k_dimensions (max_dimensions);
int i = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
node_processing_space[i].dataset = (float*) calloc(max_dimensions, sizeof (float));
}
//bn_heap_tail_index = bn_heap_head_index = 0;
}
}
bn_tree_node* get_pre_allocated_bn_tree_node_heap() {
return node_processing_space;
}
void bn_tree_init_node_heap(bn_tree_node* nodes, int max_dimensions) {
int i = 0;
int c = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
/*reset values */
if (NULL != nodes[i].dataset) {
c = 0;
for (; c < max_dimensions; c++) {
nodes[i].dataset[c] = FLT_MIN;
}
}
nodes[i].visited = 0;
nodes[i].distance_to_neighbor = FLT_MAX;
nodes[i].left = NULL;
nodes[i].right = NULL;
nodes[i].heap_index = -1;
}
}
/*PROBLEM is subsequent call to free(), but if I alloc again why cant I free again?*/
void bn_tree_free_node_space(bn_tree_node* nodes) {
int i = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
if (nodes[i].dataset) {
free(nodes[i].dataset);
}
}
free(nodes);
nodes = NULL;
}
Here is the output that I expect/want:
alloc
init
free
alloc
init
free
alloc
init
free
alloc
init
free
But Im getting this output/error:
alloc
init
free
alloc
init
double free or corruption (!prev)
Aborted (core dumped)
How can fix this?
Can't I do alloc,init,free as many times as I want (as long as I called alloc before free) OR I can do only alloc() once, then many init(), free() once?
Thanks a million & please be kind enough to provide concise answers with minimal changes.
The problem is that your bn_tree_free_node_space function takes, as its argument, a copy of the pointer variable - that is, you are passing the pointer by value - thus, the line nodes = NULL; at the end of that function only sets the local variable to NULL and does not change the value of the node_processing_space variable.
To fix this (with minimal changes to your code logic1), you need to pass that function a pointer to the pointer, and dereference that in the function. So, your function should look like this:
void bn_tree_free_node_space(bn_tree_node** nodes) // Argument is pointer-to-pointer
{
int i = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
if ((*nodes)[i].dataset) { // Now we need to use (*nodes) to get the underlying pointer
free((*nodes)[i].dataset); // ... same here
}
}
free(*nodes); /// ... and here
*nodes = NULL;
}
You will, of course, also need to change the function prototype (just before your main) to match the new definition:
void bn_tree_free_node_space(bn_tree_node** nodes); // Must match definition!
Fruther, you will (clearly) need to change the calls to that function to pass the address of the node_processing_space pointer:
bn_tree_free_node_space(&node_processing_space); // Likewise for the other 3 calls!
Feel free to ask for further clarification and/or explanation.
1 EDIT: There are other ways (some may argue better ways) to implement your system, and also other 'minor' issues in your code. However, you did explicitly ask for "concise answers with minimal changes," so I have endeavoured to comply with that request!
It appears that when the realloc function is called with a pointer to non-heap memory, the program dies with
Error(s):
Invalid memory reference (SIGSEGV)
Is it possible for my str_cat function to detect this case and thus avoid calling realloc
Here is a sample program which demonstrates the problem:
//gcc 5.4.0
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int str_cat(char *dest, size_t *dest_size, char *src);
int str_cat(char *dest, size_t *dest_size, char *src) {
// if there is sufficient free space in the dest buffer, append src,
// otherwise keep doubling the allocated size of dest until it is large
// enough to append src
size_t src_len = strlen(src);
size_t dest_len = strlen(dest);
if (src_len < *dest_size - dest_len) {
memcpy(dest+dest_len, src, src_len+1);
return 0;
} else {
char *new_dest = NULL;
size_t new_size = *dest_size * 2;
while (src_len >= new_size - dest_len) {
new_size *= 2;
}
if ((new_dest = realloc(dest, new_size)) != NULL) {
dest = new_dest;
*dest_size = new_size;
memcpy(dest+dest_len, src, src_len+1);
return 0;
} else {
return -1;
}
}
}
int main(void)
{
size_t heap_buf_size=5;
char *heap_buf = malloc(heap_buf_size);
size_t stack_buf_size=5;
char stack_buf[stack_buf_size];
*heap_buf = '\0';
if (str_cat(heap_buf, &heap_buf_size, "foo")) return -1;
printf("1. heap_buf %s\n", heap_buf);
printf("1. heap_buf_size %zu\n", heap_buf_size);
if (str_cat(heap_buf, &heap_buf_size, "bar")) return -1;
printf("2. heap_buf %s\n", heap_buf);
printf("2. heap_buf_size %zu\n", heap_buf_size);
stack_buf[0] = '\0';
if (str_cat(stack_buf, &stack_buf_size, "foo")) return -1;
printf("3. stack_buf %s\n", stack_buf);
printf("3. stack_buf_size %zu\n", stack_buf_size);
/* If the line below is uncommented, the program dies
with Invalid memory reference (SIGSEGV) */
// if (str_cat(stack_buf, &stack_buf_size, "bar")) return -1;
printf("4. stack_buf %s\n", stack_buf);
printf("4. stack_buf_size %zu\n", stack_buf_size);
return 0;
}
No, it's not possible to catch or handle SIGSEGV on most systems in a meaningful way. This is because at the point when the program causes unmapped memory access it probably have damaged its stack or data or code already, so can't be trusted and is beyond saving. You're better off crashing immediately if you detect such situation so you can analyze dumped core in debugger.
To track invalid access valgrind is a nice tool, try valgrind ./a.out, will run slow but track many things, including misuses of malloc/realloc/free.
Edit: (since OP edited the question) No, it's not possible to detect if pointer is a correct argument for realloc/free, unless you track all (de)allocations (say using malloc hooks).
As TLPI exercise 6-3 required, I made an implementation of setenv() and unsetenv() using putenv(), getenv() and via modifing environ variable directly.
Code:
// setenv() / unsetenv() impl
// TLPI exercise 6-3
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define ENV_SEP '='
extern char **environ;
// setenv() impl using putenv() & getenv()
int setenv_impl(const char * name , const char * value , int overwrite ) {
if(!overwrite && getenv(name)) { // exists & don't overwrite
return 0;
} else {
// construct the new variable
char *env_var = malloc(strlen(name) + strlen(value) + 2);
strcpy(env_var, name);
env_var[strlen(name)] = ENV_SEP;
strcpy(env_var+(strlen(name)+1), value);
int result = putenv(env_var);
if(result==0) {
return 0;
} else {
errno = result;
return -1;
}
}
}
// unsetenv() impl via modifing environ directly,
int unsetenv_impl(const char * name ) {
char **ep, **sp;
size_t len;
len = strlen(name);
for(ep = environ; *ep != NULL;) {
if(strncmp(*ep, name, len)==0 && (*ep)[len] == ENV_SEP) {
// shift all successive elements back 1 step,
for(sp=ep; *sp != NULL; sp++) {
*sp = *(sp+1);
}
} else {
ep++;
}
}
return 0;
}
// setenv_impl() test
int setenv_impl_test() {
char *key = "name";
setenv_impl(key,"Eric", 1);
printf("%s\n", getenv(key));
setenv_impl(key,"Eric2", 0);
printf("%s\n", getenv(key));
setenv_impl(key,"Eric3", 1);
printf("%s\n", getenv(key));
return 0;
}
// unsetenv_impl() test
int unsetenv_impl_test() {
char *key = "name";
setenv_impl(key,"Eric", 1);
printf("%s\n", getenv(key));
unsetenv_impl(key);
char *val = getenv(key);
printf("%s\n", val==NULL?"NULL":getenv(key));
return 0;
}
int main(int argc, void *argv[]) {
// setenv_impl_test();
unsetenv_impl_test();
return 0;
}
In my setevn_impl(), I use malloc() to allocate memory for new environment variable.
But I don't know how the memory of process's default environment allocated.
My question is:
In my unsetenv_impl() implementation, is it necesary / proper to free the memory of removed environment string by free()?
If I don't free it, will it be a problem, or it won't take much memory thus could be ignored?
Tip:
putenv() won't duplicate the string, it just make global variable environ point to the string that pass to it.
In your case it is not necessary if you don't plan to set your environment variables very frequently leading to exhaust of your memory resources.
But it would be great if you always deallocate resources after you are done with using them, be it file handles/memory/mutexs. By doing so you will not make that sort of mistake when building servers.
Some servers are expected to run 24x7. In those cases, any leak of any sort means that your server will eventually run out of that resource and hang/crash in some way. A short utility program, ya a leak isn't that bad. Any server, any leak is death. Do yourself a favor. Clean up after yourself. It's a good habit
I have been thinking in this for a while but I'm not sure if it's "safe" and possible.
Imagine something like this:
void genLeaks(void)
{
char* charLeakAddr;
charLeakAddr = (char*)malloc(sizeof(char) * 10);
strcpy(charLeakAddr, "Hello World");
}
As I understand this will create a memory leak because charLeakAddr is not released ( free (charLeakAddr); ).
Now in main :
int main(void)
{
genLeaks();
???????
return 0;
}
In the place marked with ??????? is there a way to create some kind of function that frees the memory allocated by charLeakAddr?
Thanks for your time.
Sorry but, how can I do to make the code good looking in the post :S ?
Thanks for your answers.
Somehow I produced this code and it seems to work ( I tested it in GCC with Code::Blocks in both Linux and Windows) Please take a look at it. Is it correct? or it is just crazy to try something like it?
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#define LEAKS_PATH_FILE "leaks.txt"
#define WIN_ERASE_LEAKS_FILE_COMMAND "erase leaks.txt"
#define UNIX_ERASE_LEAKS_FILE_COMMAND "rm leaks.txt"
#define __ASM__LEAK__DELETER__DEBUG
#define __UNIX__DEBUG__
unsigned int LEAKS = 0;
void regLeakAddr(void* memPtr, const char* fileName)
{
FILE* arch;
#ifdef __ASM__LEAK__DELETER__DEBUG
printf("\nMemory Leak : 0x%x\n", (void*)memPtr);
#endif
arch = fopen(fileName, "a");
if(arch)
{
fprintf(arch, "%d", (void*)memPtr);
fprintf(arch, "%c", '\n');
fclose(arch);
LEAKS++;
}
else
printf("ERROR IN FILE leaks.txt\n");
}
void assemblyDeleter(int numAddr)
{
#ifdef __ASM__LEAK__DELETER__DEBUG
printf("\nOnassemblyDeleter : 0x%x\n\n", numAddr);
#ifdef __UNIX__DEBUG__
getchar();
#else
system("pause");
#endif
#endif
char* deleter;
int* ptr = &numAddr;
printf("\n======> 0x%x\n\n", *ptr);
printf("\n======> 0x%x\n\n", deleter);
if((char*)*ptr > deleter)
{
printf("(ptr > deleter) : Offset : 0x%x\n", ((char*)*ptr - deleter));
deleter += ((char*)*ptr - deleter);
}
else
{
printf("(ptr < deleter) : Offset : 0x%x\n", (deleter - (char*)*ptr));
deleter += ((char*)*ptr - deleter);
}
printf("deleter =========> 0x%x\n", deleter);
#ifdef __ASM__LEAK__DELETER__DEBUG
puts(deleter);
#endif
free(deleter);
#ifdef __ASM__LEAK__DELETER__DEBUG
puts(deleter);
#endif
deleter = NULL;
ptr = NULL;
}
void freeMemory(void)
{
if(LEAKS == 0)
{
#ifdef __ASM__LEAK__DELETER__DEBUG
printf("NO LEAKS\n");
#endif
return;
}
FILE* arch;
int addr;
int i;
arch = fopen(LEAKS_PATH_FILE, "r");
if(arch == NULL)
{
#ifdef __ASM__LEAK__DELETER__DEBUG
printf("Error on file...\n");
#endif
return;
}
for(i = 0; i<LEAKS; i++)
{
fscanf(arch, "%d", &addr);
assemblyDeleter(addr);
}
fclose(arch);
#ifdef __UNIX__DEBUG__
system(UNIX_ERASE_LEAKS_FILE_COMMAND);
#else
system(WIN_ERASE_LEAKS_FILE_COMMAND);
#endif
}
void genLeakTrick(char** msg)
{
*msg = (char*)malloc(sizeof(char) * 17);
strcpy(*msg, "Hello World again");
printf("\n%s\n", *msg);
}
void genLeaks(void)
{
char* charLeakAddr;
charLeakAddr = (char*)malloc(sizeof(char) * 10);
strcpy(charLeakAddr, "Hello World");
printf("\n%s\n", charLeakAddr);
//free(charLeakAddr);
regLeakAddr(charLeakAddr, LEAKS_PATH_FILE);
char* charLeakAddr2;
genLeakTrick(&charLeakAddr2);
//free(charLeakAddr2);
regLeakAddr(charLeakAddr2, LEAKS_PATH_FILE);
}
int main(void)
{
genLeaks();
freeMemory();
return 0;
}
No, there is no way to free that memory. It's permanently lost (unless you can somehow find the pointer that was originally returned by malloc).
You can always just free it in genLeak since it's not being used for anything after that. If you return the pointer though, someone else is going to have to free it after it's used.
That's why in C library documentation whenever a pointer is returned, they tell you who the pointer is owned by and if you have to free it or not.
No way, until the genleak() return type is void. Modifying the return type and if the function returns a reference of charLeakAddr, it would be possible.
char* genleak()
{
char* charLeakAddr;
charLeakAddr = (char*)malloc(sizeof(char) * 10);
strcpy(charLeakAddr, "Hello World");
return charLeakAddr ;
}
int main()
{
genleak(); // Now also not possible, since the return value is not collected.
char* temp = genleak();
free temp; // Deallocating the resources acquired using malloc
return 0;
}
Edit:
In the posted snippet, charLeakAddr goes out of scope up on return of function call genleak(). Thus, making the resources stay there on the free way making no process to access the leaked sources. How about adding a global variable ?
char* globalVar = NULL ;
void genleak()
{
char* charLeakAddr;
charLeakAddr = (char*)malloc(sizeof(char) * 10);
// ....
globalVar = charLeakAddr ;
}
int main()
{
// .....
genleak();
free globalVar ;
}
And in genleak(), assign the value of where charLeakAddr is pointing to it. And then, the program can perform a free operation on it.
Yes, there's a way, that's called garbage collector. You can read this and this for some heads up. Basically if all your program is compiled/linked with the garbage collector, you might be able to do things like
gc.collect()
to claim back all leaked memory.