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
Related
I'm currently creating a program that captures user's keypresses and stores them in a string. I wanted the string that stores the keypresses to be dynamic, but i came across a problem.
My current code looks something like this:
#include <stdio.h>
#include <stdlib.h>
typedef struct Foo {
const char* str;
int size;
} Foo;
int main(void)
{
int i;
Foo foo;
foo.str = NULL;
foo.size = 0;
for (;;) {
for (i = 8; i <= 190; i++) {
if (GetAsyncKeyState(i) == -32767) { // if key is pressed
foo.str = (char*)realloc(foo.str, (foo.size + 1) * sizeof(char)); // Access violation reading location xxx
sprintf(foo.str, "%s%c", foo.str, (char)i);
foo.size++;
}
}
}
return 0;
}
Any help would be appreciated, as I don't have any ideas anymore. :(
Should I maybe also allocate the Foo object dynamically?
First, in order to handle things nicely, you need to define
typedef struct Foo {
char* str;
int size
} Foo;
Otherwise, Foo is really annoying to mutate properly - you invoke undefined behaviour by modifying foo->str after the realloc call in any way.
The seg fault is actually caused by sprintf(foo.str, "%s%c", foo.str, (char)i);, not the call to realloc. foo.str is, in general, not null-terminated.
In fact, you're duplicating work by calling sprintf at all. realloc already copies all the characters previously in f.str, so all you have to do is add a single character via
f.str[size] = (char) i;
Edit to respond to comment:
If we wanted to append to strings (or rather, two Foos) together, we could do that as follows:
void appendFoos(Foo* const first, const Foo* const second) {
first->str = realloc(first->str, (first->size + second->size) * (sizeof(char)));
memcpy(first->str + first->size, second->str, second->size);
first->size += second->size;
}
The appendFoos function modifies first by appending second onto it.
Throughout this code, we leave Foos as non-null terminated. However, to convert to a string, you must add a final null character after reading all other characters.
const char *str - you declare the pointer to const char. You cant write to the referenced object as it invokes UB
You use sprintf just to add the char. It makes no sense.
You do not need a pointer in the structure.
You need to set compiler options to compile **as C language" not C++
I would do it a bit different way:
typedef struct Foo {
size_t size;
char str[1];
} Foo;
Foo *addCharToFoo(Foo *f, char ch);
{
if(f)
{
f = realloc(f, sizeof(*f) + f -> size);
}
else
{
f = realloc(f, sizeof(*f) + 1);
if(f) f-> size = 0
}
if(f) //check if realloc did not fail
{
f -> str[f -> size++] = ch;
f -> str[f -> size] = 0;
}
return f;
}
and in the main
int main(void)
{
int i;
Foo *foo = NULL, *tmp;
for (;;)
{
for (i = 8; i <= 190; i++)
{
if (GetAsyncKeyState(i) == -32767) { // if key is pressed
if((tmp = addCharToFoo(f, i))
{
foo = tmp;
}
else
/* do something - realloc failed*/
}
}
}
return 0;
}
sprintf(foo.str, "%s%c", foo.str, (char)i); is ill-formed: the first argument cannot be const char *. You should see a compiler error message.
After fixing this (make str be char *), then the behaviour is undefined because the source memory read by the %s overlaps with the destination.
Instead you would need to use some other method to append the character that doesn't involve overlapping read and writes (e.g. use the [ ] operator to write the character and don't forget about null termination).
I'm struggling with one of my training tasks for getting in touch with a new language. Unfortunately, this time the new language is an old one, it is C. My programming Task for this is to generate Langford-Strings, which should not be the main problem.
My first attempt in C, with a recursive approach works like a charm:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int grade = 0;
const char* blank = "_";
const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
void generate(int position, char* string) {
if (!string) {
string = calloc(grade*2+1, sizeof(char));
for (int i = 0; i < grade*2; i++) {
string = strcat(string, blank);
}
}
if (!strstr(string, blank)) {
printf("%s\n", string);
return;
}
if (position < strlen(string)) {
if (string[position] != *blank) {
char* nstring = calloc(grade*2+1, sizeof(char));
strcpy(nstring, string);
generate(position+1, nstring);
free(nstring);
return;
} else {
for (int i = 0; i<strlen(string); i++) {
if (strchr(string, alphabet[i])){
continue;
}
int index = strcspn(alphabet, &alphabet[i])+1;
if (position+index+1<strlen(string)) {
if (string[position]==*blank) {
if (string[position+index+1]==*blank) {
char* nstring = calloc(grade*2+1, sizeof(char));
strncat(nstring, string, position);
strncat(nstring, &alphabet[i], 1);
strncat(nstring, &string[position+1], index);
strncat(nstring, &alphabet[i], 1);
strcat(nstring, &string[position+2+index]);
if (position<strlen(nstring)) {
generate(position+1, nstring);
}
free(nstring);
}
}
}
}
}
}
}
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Missing parameter of langford strings grade!\n");
return 1;
}
grade = strtol(argv[1], NULL, 10);
if (grade % 4 != 0) {
if ((grade+1) % 4 != 0) {
printf("Grade must be multiple of 4 or one less\n");
return 1;
}
}
generate(0, NULL);
return 0;
}
That works great, giving me exactly the results I expected.
But when I try to do it threaded (old-style threaded, spawning a new thread on each level of the recursion), it not only ends with a seqfault every time. It does end in an seqfault in a not predictable time. That means, that it runs indefinitly, printing out doubled and trippled results and always a random number of results, before seqfaulting.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>
size_t grade = 0;
const char* blank = "_";
const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
struct thread {
pthread_t* thread;
size_t position;
struct thread* threads[26];
char* string;
};
void* alloc_thread_data() {
struct thread* ret = calloc(1, sizeof(struct thread));
ret->thread = calloc(1, sizeof(pthread_t));
ret->position = 0;
ret->string = calloc((grade*2)+1, sizeof(char));
return (void*)ret;
}
void free_thread_data(struct thread* dst) {
free(dst->string);
free(dst->thread);
free(dst);
}
void assemble_string(char* dst, char* src, size_t pos, size_t index) {
strncat(dst, src, pos);
strncat(dst, &alphabet[index-1], 1);
strncat(dst, &src[pos+1], index);
strncat(dst, &alphabet[index-1], 1);
strncat(dst, &src[pos+2+index], (grade*2)-pos+index+2);
}
void* generate(void* data) {
struct thread* args = (struct thread*)data;
if (args->string && strlen(args->string)==0) {
for (size_t i = 0; i<grade*2; i++) {
strcat(args->string, blank);
}
}
if (args->string && !strstr(args->string, blank)) {
printf("%s\n", args->string);
return NULL;
}
if (args->string && args->position<strlen(args->string)) {
size_t sub = 0;
if (args->string[args->position]!=*blank) {
args->threads[sub] = alloc_thread_data();
strcpy(args->threads[sub]->string, args->string);
args->threads[sub]->position = args->position+1;
pthread_create(args->threads[sub]->thread, NULL, generate, (void*)args->threads[sub]);
sub++;
} else {
for (size_t i = 0; i<grade*2; i++) {
if (strchr(args->string, alphabet[i])){
continue;
}
int index = strcspn(alphabet, &alphabet[i])+1;
if (args->string[args->position] == *blank) {
if (args->string[args->position+index+1] == *blank) {
args->threads[sub] = alloc_thread_data();
assemble_string(args->threads[sub]->string, args->string, args->position, index);
args->threads[sub]->position = args->position+1;
pthread_create(args->threads[sub]->thread, NULL, generate, (void*)args->threads[sub]);
sub++;
}
}
}
}
for (size_t i = 0; i<sub; i++) {
if (args->threads[i]->thread!=NULL) {
if(pthread_kill(*args->threads[i]->thread, 0)==0) {
pthread_join(*args->threads[i]->thread, NULL);
}
free_thread_data(args->threads[i]);
}
}
}
return NULL;
}
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Missing parameter of langford strings grade!\n");
return 1;
}
grade = strtol(argv[1], NULL, 10);
if (grade % 4 != 0) {
if ((grade+1) % 4 != 0) {
printf("Grade must be multiple of 4 or one less\n");
return 2;
}
}
struct thread* args = alloc_thread_data();
pthread_create(args->thread, NULL, generate, (void*)args);
if(pthread_kill(*args->thread, 0)==0) {
pthread_join(*args->thread, NULL);
}
free_thread_data(args);
}
So, as written before, I managed to get around C programming for my whole work-life and do this just for fun - so I do not expect my code to be somewhat comprehensive. Please help me finding out, what is wrong with the threaded approach (and if you see any well-known-codesmell in the first one as well of course). Any hints welcome.
In addition to the bad allocation that #EugeneSh. pointed out, this looks like a problem:
pthread_create(args->threads[sub]->thread, NULL, generate,
(void*)&args->threads[sub]);
Note the difference from this other call that also appears:
pthread_create(args->threads[sub]->thread, NULL, generate,
(void*)args->threads[sub]);
[newlines inserted and indentation normalized for clarity and ease of reading].
args->threads[sub] is a struct thread*. You want to pass that pointer itself to pthread_create(), as in the second case, not its address, as in the first case.
Overall, I'm inclined to agree with #MikeRobinson that yours is an inappropriate use of threads. It is never useful performance-wise to have more schedulable threads in your process than you have cores, and you scale up to many thousands of total threads very quickly. I doubt very much that the result will outperform your single-threaded solution -- the costs of the context switching and cache thrashing that surely result will likely swamp whatever speedup you get from parallel execution on the 4 - 12 cores you probably have.
Added:
Additionally, it is very important to check the values returned by your function calls for error codes, unless you don't care and don't need to care whether the calls succeed. In particular, you should check
the return values of your malloc() / calloc() calls -- these return NULL in the event of unsuccessful allocation, and with as many total allocations as you perform, it is plausible that some of these fail. Using the resulting NULL pointer could easily lead to a segfault
the return values of your pthread_create() calls -- these return a value different from 0 in the event of failure. It is not safe to afterward rely on pthread_kill() to determine whether the thread was created successfully, for a failed pthread_create() leaves the thread handle's contents undefined. Any subsequent evaluation that depends on the value of the handle therefore exhibits undefined behavior.
I'm also a little suspicious of all your strncat()ing, for this is a notorious source of string overruns. These are ok if the target strings have enough capacity, but it's difficult for me to tell whether they always do in your case.
May I cordially suggest that it makes absolutely no sense (to me, at least ...) to "spawn a new thread" here?
The only reason to "spawn a thread" is when you wish to perform two thereafter, independent activities, which this algorithm quite clearly does not.
The immediate reason for the segfault is that the various threads are all attempting to manipulate the same data without regard to one another, and without waiting for one another. But, IMHO, the root cause of the problem is ... that this entire scenario is nonsense. "Recursion" and "multi-threading" are not at all the same thing. If your objective here was to learn about threading, I'm afraid that you've just learned far more (the very-hard way) than you ever wished to know . . .
I'm not proficient in C programming so please excuse me if this isn't a strong question. In the following code, I can only allocate memory to samplesVecafter obtaining the value of nsamplepts, but I need to return the vector samplesVec to the main for further use (not yet coded). However, I'm getting the following error:
Error in Terminal Window:
ImportSweeps(3497,0x7fff7b129310) malloc: * error for object 0x7fdaa0c03af8: pointer being freed was not allocated
* set a breakpoint in malloc_error_break to debug
Abort trap: 6
I'm using Mac OS X Mavericks with the gcc compiler. Thanks for any help.
*EDITED!!! AFTER VALUABLE INPUTS FROM COMMENTATORS, THE FOLLOWING REPRESENTS A SOLUTION TO THE ORIGINAL PROBLEM (WHICH IS NO LONGER AVAILABLE) *
The following code modification seemed to solve my original questions. Thanks for the valuable inputs everyone!
/* Header Files */
#define LIBAIFF_NOCOMPAT 1 // do not use LibAiff 2 API compatibility
#include <libaiff/libaiff.h>
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <math.h>
/* Function Declarations */
void FileSearch(char*, char*, char*, char*, char*);
int32_t *ImportSweeps(char*);
/* Main */
int main()
{
char flag1[2] = "N";
char binname[20] = "bin1"; // dummy assignment
char buildfilename[40] = "SweepR";
char skeletonpath[100] = "/Users/.../Folder name/";
int k, len;
/* Find the sweep to be imported in the directory given by filepath */
FileSearch(skeletonpath, binname, buildfilename, skeletonpath, flag1);
if (strcmp(flag1,"Y")) {
printf("No file found. End of program.\n");
} else {
len = (int) strlen(skeletonpath);
char *filepath = malloc(len);
for (k = 0; k < len; k++) {
filepath[k] = skeletonpath[k];
}
printf("File found! Filepath: %s\n", filepath);
// Proceed to import sweep
int32_t *sweepRfile = ImportSweeps(filepath);
if (sweepRfile) {
printf("Success!\n");
// Do other things with sweepRfile
free(sweepRfile);
}
free(filepath);
}
return 0;
}
/* Sub-Routines */
void FileSearch(char *dir, char *binname, char *buildfilename, char* filepath, char* flag1)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if((dp = opendir(dir)) == NULL) {
fprintf(stderr,"Cannot open directory: %s\n", dir);
return;
}
chdir(dir);
while((entry = readdir(dp)) != NULL) {
lstat(entry->d_name, &statbuf);
if(S_ISDIR(statbuf.st_mode)) {
/* Found a directory, but ignore . and .. */
if(strcmp(".",entry->d_name) == 0 || strcmp("..",entry->d_name) == 0)
continue;
strcpy(binname,entry->d_name);
strcpy(buildfilename,"SweepR");
/* Recurse at a new indent level */
FileSearch(entry->d_name, binname, buildfilename, filepath, flag1);
}
else {
sprintf(buildfilename, "%s%s.aiff", buildfilename, binname);
if (strcmp(entry->d_name,buildfilename)) {
strcpy(buildfilename,"SweepR");
} else {
sprintf(filepath, "%s%s/%s", filepath, binname, buildfilename);
strcpy(flag1,"Y");
break;
}
}
}
chdir("..");
closedir(dp);
}
int32_t *ImportSweeps(char *filepath)
{
char *filepathread = filepath;
/* Initialize files for importing */
AIFF_Ref fileref;
/* Intialize files for getting information about AIFF file */
uint64_t nSamples;
int32_t *samples = NULL;
int32_t *samplesVec = NULL;
int channels, bitsPerSample, segmentSize, ghost, nsamplepts;
double samplingRate;
/* Import Routine */
fileref = AIFF_OpenFile(filepathread, F_RDONLY) ;
if(fileref)
{
// File opened successfully. Proceed.
ghost = AIFF_GetAudioFormat(fileref, &nSamples, &channels, &samplingRate, &bitsPerSample, &segmentSize);
if (ghost < 1)
{
printf("Error getting audio format.\n");
AIFF_CloseFile(fileref); return (int32_t) 0;
}
nsamplepts = ((int) nSamples)*channels;
samples = malloc(nsamplepts * sizeof(int32_t));
samplesVec = malloc(nsamplepts * sizeof(int32_t));
ghost = AIFF_ReadSamples32Bit(fileref, samples, nsamplepts);
if (ghost) {
for (int k = 0; k < nsamplepts; k++) {
samplesVec[k] = *(samples+k);
}
}
free(samples);
AIFF_CloseFile(fileref);
}
return samplesVec;
}
So... as far as I can see... :-)
samplesVec, the return value of ImportSweeps is not initialized, if fileref is false. Automatic (== local) variables have no guarantees on its value if samplesVec are not explicitly initialized - in other words samplesVec could carry any address. If samplesVec is not NULL on luck (which on the other hand might be often the case), you try free a not allocated junk of memory, or by very bad luck an somewhere else allocated one.
If I'm correct with my guess you can easy fix this with:
int32_t *samples;
int32_t *samplesVec = NULL;
It is a good idea anyway to initialize any variable as soon as possible with some meaningful error or dummy value, if you not use it in the very next line. As pointers are horrible beasts, I always NULL them if I don't initialize them with a useful value on declaration.
Edit: Several minor small changes for a readable approximation to English. :-)
If AIFF_OpenFile fails, ImportSweeps returns an undefined value because samplesVec wasn't initialized. If that value is non-NULL, main will try to free it. You can either initialize samplesVec = NULL, or you can reorganize the code as
fileref = AIFF_OpenFile(filepathread, F_RDONLY) ;
if(!fileref) {
{
// print error message here
return NULL;
}
// File opened successfully. Proceed.
...
There are people who will insist a functon that should only have one exit -- they are poorly informed and voicing a faulty dogma handed down from others who are likewise uninformed and dogmatic. The check for error and return above is known as a guard clause. The alternate style, of indenting every time a test succeeds, yields the arrow anti-pattern that is harder to read, harder to modify, and more error prone. See http://blog.codinghorror.com/flattening-arrow-code/ and http://c2.com/cgi/wiki?ArrowAntiPattern for some discussion.
I have problem with my array of char*-
char *original_file_name_list[500];
while(dp=readdir(dir)) != NULL) {
original_file_name = dp->d_name;
original_file_name_list[counter] = original_file_name;
printf("%s\n",original_file_name_list[0]);
printf("%d\n",counter);
counter++;
}
The problem is, that it prints all files fine. It should print only first file, right?
And if I try printf("%s\n",original_file_name_list[1]); It doesn't work , which means that it is writing only in 1st string. Any idea why?
edit: There is no syntax error due to compiler.
You're not copying the string at all - also your file_name_list array hasn't enough space for a list of filenames - just for a list of pointers. But dp->d_name is just a pointer to a char* - you can't know for how long the memory behind the pointer is valid. Because of that you have to make a copy for yourself.
#include <string.h>
#include <dirent.h>
int main(int argc, char** argv){
char original_file_name_list[50][50];
size_t counter = 0;
while(dp=readdir(dir)) != NULL) // does work fine (ordinary reading files from dir)
{
size_t len = strlen(dp->d_name);
if(len >= 50) len = 49;
strncpy(original_file_name_list[counter], dp->d_name, len);
original_file_name_list[counter][len] = '\0';
printf("%d\n",counter);
counter++;
}
printf("%s\n",original_file_name_list[1]); // <- will work if you have at least 2 files in your directory
return 0;
}
I'm not sure about purpose of counter2 (I have replaced it with counter) but I can propose the following code with strdup() call to store the file names:
char *original_file_name_list[500] = {0}; // it is better to init it here
while(dp=readdir(dir)) != NULL) {
original_file_name_list[counter] = strdup(dp->d_name); // strdup() is ok to use
// here, see the comments
printf("%s\n%d\n",original_file_name_list[counter], counter);
counter++;
}
/* some useful code */
/* don't forget to free the items of list (allocated by strdup(..) )*/
for (int i = 0; i < 500; ++i) {
free(original_file_name_list[i]);
}
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.