A CLI program of mine compiles and runs fine on windows. Compiles fine on linux, but causes a segmentation fault when running.
I turned to stackoverflow for help, and found a few questions similar to what I was going to ask that suggested valgrind, which I just happen to have installed (woo!).
So I ran my program through valgrind, and got a depressingly large amount of output, but I shall start with the first error message:
==11951== Command: ./vt
==11951==
Loading...
Load default database? (y/n)y
Opened input file vtdb.~sv, reading contents...
==11951== Invalid write of size 1
==11951== at 0x400FA9: readnumberfromfile (in /home/rob/Documents/programming/c/vocabtest/vt)
==11951== by 0x400C21: getrecordsfromfile (in /home/rob/Documents/programming/c/vocabtest/vt)
==11951== by 0x401FFD: main (in /home/rob/Documents/programming/c/vocabtest/vt)
==11951== Address 0x53b05bb is 0 bytes after a block of size 11 alloc'd
==11951== at 0x4C28FAC: malloc (vg_replace_malloc.c:236)
==11951== by 0x400EAC: readnumberfromfile (in /home/rob/Documents/programming/c/vocabtest/vt)
==11951== by 0x400C21: getrecordsfromfile (in /home/rob/Documents/programming/c/vocabtest/vt)
==11951== by 0x401FFD: main (in /home/rob/Documents/programming/c/vocabtest/vt)
==11951==
...finished.
1180 entries read from vtdb.~sv.
The problem seems to be in readnumberfromfile, and I've looked through it, and I can't seem to find what's wrong with it!
Can anyone shed some light?
int readnumberfromfile (int maxvalue,char separator)
{
int number, i=0;
char ch;
char * buff = (char *)malloc(11);//allocate enough space for an 10-digit number and a terminating null
if (!buff) {printf("Memory allocation failed!\n");return 0;}//return 0 and print error if alloc failed
if (!maxvalue) maxvalue=MAXINTVALUE;
ch=getc(inputfile);
while (!isdigit(ch))
{
if (ch == separator||ch=='\n'||ch==EOF) {fprintf(stderr,"Format error in file\n");return 0;}//if no number found(reached separator before digit), print error and return 0
ch = getc(inputfile);//cycle forward until you reach a digit
}
while (i<11 && ch!=separator && ch!='\n')//stop when you reach '~', end of line, or when number too long
{
buff[i++]=ch;
ch = getc(inputfile); //copy number from file to buff, one char at a time
}
buff[i] = '\0';//terminate string
number = atoi(buff)<=maxvalue ? atoi(buff) : maxvalue;//convert string to number and make sure it's in range
free(buff);
return number;
}
This is called from getrecordsfromfile if that's of any use:
void getrecordsfromfile(char * inputfilename,char separator)
{
int counter = 0;
struct vocab * newvocab;
struct listinfo * newvocablist;
if (!(inputfile = fopen(inputfilename, "r")))
{
printf("Unable to read input file. File does not exist or is in use.\n");
}
else
{
printf("Opened input file %s, reading contents...\n",inputfilename);
while (!feof(inputfile))
{
newvocab = (struct vocab *)malloc(sizeof(struct vocab));
if (!newvocab)
{
printf("Memory allocation failed!\n");
return;
}
else
{
newvocab->question=readtextfromfile(MAXTEXTLENGTH,separator);
newvocab->answer=readtextfromfile(MAXTEXTLENGTH,separator);
newvocab->info=readtextfromfile(MAXTEXTLENGTH,separator);
newvocab->hint=readtextfromfile(MAXTEXTLENGTH,separator);
newvocab->right=readnumberfromfile(1,separator);
newvocab->counter=readnumberfromfile(0,separator);
newvocab->known=readnumberfromfile(3,separator);
switch (newvocab->known)
{
case 0: newvocablist = &n2l;break;
case 1: newvocablist = &norm;break;
case 2: newvocablist = &known;break;
case 3: newvocablist = &old;break;
}
addtolist(newvocab,newvocablist);
if (newvocab->question==NULL||newvocab->answer==NULL)
{
printf("Removing empty vocab record created from faulty input file...\n");
removefromlist(newvocab,newvocablist,1);
}
else counter++;
}
}
fclose(inputfile);
printf("...finished.\n%i entries read from %s.\n\n",counter,inputfilename);
}
return;
}
Full source can be gitted from https://github.com/megamasha/Vocab-Tester
A couple of notes: I am trying to help myself, I have done my research, looked at similar questions and found out about valgrind myself.
I am still a relative beginner though, and while I appreciate solutions (WHAT to do to fix it), yet more useful is knowledge (HOW to fix or avoid it myself next time). I am here (and very keen) to learn.
buff[i] = '\0';//terminate string
in here i == 11, since you allocated only 11 chars, and while condition ends when i=11.
so, you access a memory you did not allocate.
the behavior for this situation is not defined.
you can solve this by allocating one extra character on your malloc.
int number, i=0;
...
while (i<11 ...
You are reading up to eleven digits for i = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 and 10. And then trying to stick the \0 in twelfth slot buff[11].
It's called an "off by one error".
So the fix depends on what you want to change. If you want to accept 11 characters, change the malloc of buff. If you want to only accept 10, then change the while condition.
Invalid write of size 1
You're probably writing a char
Address 0x53b05bb is 0 bytes after a block of size 11 alloc'd
You've only just overflowed something of size 11
Both in readnumberfromfile
This is suspiciously related (by the sizes):
char * buff = (char *)malloc(11);
This will be done with i = 11 after the loop, which is past the end of the allocation:
buff[i] = '\0'
As wormsparty says you can get valgrind to be more helpful, by getting debug symbols in your binary.
For later, if you compile with -g, valgrind will show you exactly at which line the segfault happened.
Related
Update:
Removing the print statement (line 52) entirely with all else the same as in the code below gave me 0 errors. The errors exist within my functions when printing (using philo[i]) and in this print statement (added for debugging), but do not exist if I run the entire program with no prints. Any ideas what I'm doing wrong???
Thank you for the help so far. I have made a couple of changes based on comments received so far.
***** Original (Edited) Question *****
I can't figure out why I have been getting this error, "Uninitialised value was created by a stack allocation". I started with a completed program that works fine, but gives me a ton of uninitialized value errors. I have traced the problem down to a few lines of code by excluding all functions and adding additional print statements. This code is to solve the dining philosopher's problem using threads (homework), so I don't want to post too much. My code is now:
#include <all needed header files>
#define NUM_PHIL 5
#define MIN_EAT_TIME 10
pthread_t philosopher[NUM_PHIL]; // array to hold IDs for philosopher threads
pthread_mutex_t chopstick[NUM_PHIL]; // array to hold IDs for chopstick mutexes (locks)
// function definitions here:
int philosopherFun(int *philo);
// All others have been bypassed at the time of my current problem
int main(int argc, char *argv[]) {
int phil[NUM_PHIL]; // Philosopher numbers ("names")
for(int i = 0; i < NUM_PHIL; i++) {
phil[i] = i + 1;
}
for(int i = 0; i < NUM_PHIL; i++) {
// Print statement causes valgrind error to exist (as does calling a function using phil)
printf("Value phil[%d] = %d\n", i, phil[i]);
}
// Initilize mutexes for chopsticks, report error as needed
for(int i = 0; i < NUM_PHIL; i++) {
if(pthread_mutex_init( stuff here) < 0) {
// error reporting
// print statement uses i
}
}
for(int i = 0; i < NUM_PHIL; i++) {
if(pthread_create(&philosopher[i], NULL, (void*) philosopherFun, (void*) &phil[i] ) != 0) {
// error reporting
// print statement uses phil[i]
}
}
/* Code omitted as this is Homework */
// Join threads created for philosophers (using pthread_join)
// error reporting
// print statement uses phil[i]
// Destroy chopstick mutexes when done. (using pthread_mutex_destroy)
// error reporting
// print statement uses i
printf("The philosophers have all finished eating and its time for bed. Goodnight...\n");
return 0;
}
int philosopherFun(int *philo) {
return 0;
}
Program output:
Value phil[0] = 1
Value phil[1] = 2
Value phil[2] = 3
Value phil[3] = 4
Value phil[4] = 5
The philosophers have all finished eating and its time for bed. Goodnight...
My Valgrind errors are:
==46556== HEAP SUMMARY:
==46556== in use at exit: 0 bytes in 0 blocks
==46556== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==46556==
==46556== All heap blocks were freed -- no leaks are possible
==46556==
==46556== ERROR SUMMARY: 10 errors from 1 contexts (suppressed: 0 from 0)
==46556==
==46556== 10 errors in context 1 of 1:
==46556== Conditional jump or move depends on uninitialised value(s)
==46556== at 0x7FFF205A395F: ??? (in /dev/ttys000)
==46556== by 0x7FFF2046FFFA: ??? (in /dev/ttys000)
==46556== by 0x7FFF20478CF0: ??? (in /dev/ttys000)
==46556== by 0x7FFF2049D8B8: ??? (in /dev/ttys000)
==46556== by 0x7FFF20475EF5: ??? (in /dev/ttys000)
==46556== by 0x7FFF20474061: ??? (in /dev/ttys000)
==46556== by 0x1000038CD: main (philo.c:52)
==46556== Uninitialised value was created by a stack allocation
==46556== at 0x7FFF20475FDF: ??? (in /dev/ttys000)
==46556==
==46556== ERROR SUMMARY: 10 errors from 1 contexts (suppressed: 0 from 0)
line 52 is my print statement:
printf("Value phil[%d] = %d\n", i, phil[i]);
which I believe accounts for 5 errors (phil[0] - phil[4]) and I call philosopherFun in my pthread_create on line 68 (this line of code included), again accounting for 5 errors (1 for each thread). This function is currently only returning on the first line, so none of the rest of my program is involved (though I started with 50-250 errors depending on my parameters and which functions I excluded).
I feel like my int array (int phil[]) is causing the problem, but it is immediately initialized, so I'm not sure how this is happening. Please help!
Thanks,
Kristine
Edited... Added some comments from omitted code- Threads are joined and mutexes destroyed
I also tried making int phil[NUM_PHIL] global by declaring it outside of my main function, but this made no change to the errors returned by Valgrind. defining phil[5] = {1, 2, 3, 4, 5}; as a global also didn't help.
I believe this is where your problem comes from:
int phil[NUM_PHIL];
and here
if(pthread_create(&philosopher[i], NULL, (void*) philosopherFun, (void*) &phil[i] ) != 0) {
more code ...
}
You're referencing a stack-allocated object (you're getting a pointer to the elements of the philo array). When the main function returns, the memory will be deleted and the other threads will still be holding on to garbage. To be sure this is the problem, move your philo array to a global scope, or allocate the array on the heap instead.
My AF-XDP userspace program is based on this tutorial: https://github.com/xdp-project/xdp-tutorial/tree/master/advanced03-AF_XDP
I am currently trying to parse ~360.000 RTP-packets per second (checking for continuous sequence numbers) but I loose around 25 per second (this means that for 25 packets the statement previous_rtp_sqnz_nmbr + 1 == current_rtp_sqnz_nmbr doesn't hold true).
So I tried to increase the number of allocated packets NUM_FRAMES from 228.000 to 328.000. With default FRAME_SIZE of XSK_UMEM__DEFAULT_FRAME_SIZE = 4096 this results in 1281Mbyte being allocated (no problem because I have 32GB of RAM) but for whatever reason, this function call:
static struct xsk_umem_info *configure_xsk_umem(void *buffer, uint64_t size)
{
printf("Try to allocate %lu\n", size);
struct xsk_umem_info *umem;
int ret;
umem = calloc(1, sizeof(*umem));
if (!umem)
return NULL;
ret = xsk_umem__create(&umem->umem, buffer, size, &umem->fq, &umem->cq,
NULL);
if (ret) {
errno = -ret;
return NULL;
}
umem->buffer = buffer;
return umem;
}
fails with
Try to allocate 1343488000
ERROR: Can't create umem "Cannot allocate memory"
I don't know why? But because I know that my RTP-packets are not larger than 1500bytes, I set FRAME_SIZE 3072 so I am now at around 960Mbyte (which works without an error).
However, I am now loosing half of the received packets (this means that for 180.000 packets the previous sequence number doesn't line up with the current sequence number).
Because of this I ask the question: What is the relationship between FRAME_SIZE and the actual size of a packet? Because obviously it can not be the same.
Edit: I am using 5.4.0-4-amd64 #1 SMP Debian 5.4.19-1 (2020-02-13) x86_64 GNU/Linux and just copied the libbpf-repository from here into my code-base: https://github.com/libbpf/libbpf
So I don't know whether the error mentioned here: https://github.com/xdp-project/xdp-tutorial/issues/76 is still valid?
Code Purpose
The code is supposed to simulate CPU scheduling algorithms. At present only FCFS (First Come First Served) and SJF (Shortest Job First have been written)
Problem
When running the code I receive the following error when using the FCFS 'pathway'
*** Error in `./test2': double free or corruption (!prev): 0x000055f54ecc7830 ***
From what I have found online this relates to memory overflow issues, although running this on my personal PC I receive no errors for the pathway. I have checked my loops and array decelerations yet cannot find the issue.
I believe my issue may relate to the following for loops
//compare values in at to find earliest arrival time. Basically loops through dataset values and sorts them into the arrival order
for(i=0; i<processes; i++)
{
for(j=0; j<processes; j++)
{
if(at[i]<at[j]) //if the value of i is smaller than j (basically gets the smallest value in array)
{
temp=at[i]; //temp int equals arrival time of i
at[i]=at[j]; //arrival time i changes to value of arrival time j
at[j]=temp; //arrival time j becomes original value of arrival time i (basically switching the values of i and j)
temp=bt[i]; //temp becomes value of burst time i
bt[i]=bt[j]; //burst time i becomes values of burst time j
bt[j]=temp; //burst time j becomes the original value of burst time i (basically switching the values of i and j)
temp=pid[i]; //t changes to value of pid i
pid[i]=pid[j]; //pid i becomes value of pid j
pid[j]=temp; //pid j becomes value of t (basically switching the values of i and j)
}
}
}
The main dataset I have been testing is dataset 3 which contains the following data
PID AT BT
0 3 4
1 1 5
2 2 20
3 0 25
4 6 14
5 8 6
All files relating to this code can be located at the following link (Apologies I know links are not preferred but its the easiest way to share the datasets with correct formatting)
Full code and file location
Full Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char filename[100];
char *buffer = NULL;
int schedtoken;
char entries[10];
int fcfs()
{
int pid[10],at[10],bt[10],st[10],ft[10],tat[10],wt[10],i=0,j=0,processes=6,temp,n1,n2,n3;
int totwt=0,tottat=0;
char c1,c2,c3,fcfsselection;
printf("\n\n\nPlease select the dataset you would like to use\n\n");
printf(" 1. Dataset1\n 2. Dataset2\n 3. Dataset3\n 4. Quit\n\nSelection:\n");
scanf(" %c",&fcfsselection);
// Get data set user wants and amend filename based on selection
switch(fcfsselection)
{
case '1':
printf("\nYou have selected Dataset1\n");
strcpy(filename, "datasets/dataset1.txt");
break;
case '2':
printf("\nYou have selected Dataset2\n");
strcpy(filename, "datasets/dataset2.txt");
break;
case '3':
printf("\nYou have selected Dataset3\n");
strcpy(filename, "datasets/dataset3.txt");
break;
case '4':
printf("\nThank you for using this tool!");
break;
default:
printf("\nERROR!: Incorrect selection - Returning to Menu\n");
fcfs();
}
//Import dataset file, store the first line of char's (crashes if only checking for ints) and the rest of the ints
FILE *fp;
fp=fopen(filename,"r");
if (fp == NULL)
{
printf("Cannot open file at %s, try again.", filename);
fcfs();
}
else
{
fscanf(fp,"%s%s%s",&c1,&c2,&c3);
while(fscanf(fp,"%d%d%d",&n1,&n2,&n3)!=EOF)
{
pid[i]=n1;
at[i]=n2;
bt[i]=n3;
i++;
}
}
fclose(fp);
//compare values in arr time to find earliest arrival time. Basically loops through dataset values and sorts them into the arrival order
for(i=0; i<processes; i++)
{
for(j=0; j<processes; j++)
{
if(at[i]<at[j]) //if the value of i is smaller than j (basically gets the smallest value in array)
{
temp=at[i]; //temp int equals arrival time of i
at[i]=at[j]; //arrival time i changes to value of arrival time j
at[j]=temp; //arrival time j becomes original value of arrival time i (basically switching the values of i and j)
temp=bt[i]; //temp becomes value of burst time i
bt[i]=bt[j]; //burst time i becomes values of burst time j
bt[j]=temp; //burst time j becomes the original value of burst time i (basically switching the values of i and j)
temp=pid[i]; //t changes to value of pid i
pid[i]=pid[j]; //pid i becomes value of pid j
pid[j]=temp; //pid j becomes value of t (basically switching the values of i and j)
}
}
}
//complete calculations
for(i=0; i<processes; i++)
{
if(i==0)
st[i]=at[i]; //if i equals 0 (basically the beggining of the sim) then the start time equals the arrival time of the first entry (so 0)
else
st[i]=ft[i-1]; //otherwise the start value equals the finish value of the last entry run -1
wt[i]=st[i]-at[i]; //wait time equals the start time of the process minus the arrival time
ft[i]=st[i]+bt[i]; //finish time equals start time plus run time
tat[i]=ft[i]-at[i]; // turn around time equals finish time minus arrival time
}
tottat=0;
//print results
printf("\nPID\t AT\t BT\t WT\t ST\t TAT\t CT");
for(i=0; i<processes; i++)
{
printf("\n%3d\t%3d\t%3d\t%3d\t%3d\t%3d\t%3d",pid[i],at[i],bt[i],wt[i],st[i],tat[i],ft[i]);
totwt+=wt[i];
tottat+=tat[i];
}
printf("\n\nAverage Waiting Time:%f",(float)totwt/processes);
printf("\nAverage Turn Around Time:%f",(float)tottat/processes);
fclose(fp);
//Open new file to print output
FILE *f = fopen("datasets/output.txt", "w");
if (f == NULL)
{
printf("Error opening file!\n");
exit(1);
}
//Print output to file
fprintf(f, "PID\t AT\t BT\t WT\t ST\t TAT\t CT");
for(i=0; i<processes; i++)
{
fprintf(f,"\n%3d\t%3d\t%3d\t%3d\t%3d\t%3d\t%3d",pid[i],at[i],bt[i],wt[i],st[i],tat[i],ft[i]);
}
fprintf(f,"\n\nAverage Waiting Time:%f",(float)totwt/processes);
fprintf(f,"\nAverage Turn Around Time:%f",(float)tottat/processes);
printf("\n\nThe results for the dataset simulated are stored in: datasets/output.txt");
fclose(f);
return 0;
}
char* getfile(char *filename)
{
int string_size, read_size;
FILE *file = fopen(filename, "r");
if (file)
{
// Seek the last byte of the file
fseek(file, 0, SEEK_END);
// Offset from the first to the last byte, or in other words, filesize
string_size = ftell(file);
// go back to the start of the file
rewind(file);
// Allocate a string that can hold it all
buffer = (char*) malloc(sizeof(char) * (string_size + 1) );
// Read it all in one operation
read_size = fread(buffer, sizeof(char), string_size, file);
// fread doesn't set it so put a \0 in the last position
// and buffer is now officially a string
buffer[string_size] = '\0';
if (string_size != read_size)
{
// Something went wrong, throw away the memory and set
// the buffer to NULL
free(buffer);
buffer = NULL;
}
// Always remember to close the file.
fclose(file);
}
// printf("%s",buffer);
return buffer;
}
void schedintro(int schedtoken)
{
//take the value of schedtoken, change the value of filename to relavent file path and use getfile to open and then print the file. Call relavent scheduling function to do calculations
if(schedtoken==1)
{
strcpy(filename, "headers/fcfsheader.txt");
getfile(filename);
printf("%s",buffer);
fcfs();
}
else if(schedtoken==2)
{
strcpy(filename, "headers/sjfheader.txt");
getfile(filename);
printf("%s",buffer);
}
else if(schedtoken==3)
{
strcpy(filename, "headers/rrheader.txt");
getfile(filename);
printf("%s",buffer);
}
}
int schedselect()
{
//function to display what algorithms can be selected. User input
// obtained based on these options and user filtered based on switch
// cases to relevant function path
char selection;
// print users options and take their input for switch
printf("%s",buffer);
printf("\n\n\nPlease select the Scheduling Algorithm you would like to use\n\n");
printf(" 1. First Come First Served (FCFS)\n 2. Shortest Job First (SJF)\n 3. Round Robin (RR)\n 4. Quit\n\nSelection:\n");
scanf("%c",&selection);
// direct user to specific algorithm function path
switch(selection)
{
case '1':
//printf("\nYou have selected First Come First Served (FCFS)\n");
schedintro(schedtoken=1);
break;
case '2':
//printf("\nYou have selected Shortest Job First (SJF)\n");
schedintro(schedtoken=2);
break;
case '3':
//printf("\nYou have selected Round Robin (RR)\n");
schedintro(schedtoken=3);
break;
case '4':
printf("\nThank you for using this tool!");
break;
default:
printf("\nERROR!: Incorrect selection - Returning to Menu\n");
schedselect();
}
//printf("%d", schedoption);
return schedtoken;
}
int main()
{
strcpy(filename, "headers/introheader.txt");
getfile(filename);
schedselect();
return 0;
}
In fcfs() you close two times the same file
fclose(fp);
//compare values in arr time to find earliest arrival time. Basically loops through dataset values and sorts them into the arrival order
and
fclose(fp);
//Open new file to print output
remove the second fclose
without the second fclose nothing is signaled by valgrind for -Dataset3_ :
valgrind ./a.out
==22836== Memcheck, a memory error detector
==22836== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==22836== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==22836== Command: ./a.out
==22836==
(null)
Please select the Scheduling Algorithm you would like to use
1. First Come First Served (FCFS)
2. Shortest Job First (SJF)
3. Round Robin (RR)
4. Quit
Selection:
1
(null)
Please select the dataset you would like to use
1. Dataset1
2. Dataset2
3. Dataset3
4. Quit
Selection:
3
You have selected Dataset3
PID AT BT WT ST TAT CT
3 0 25 0 0 25 25
1 1 5 24 25 29 30
2 2 20 28 30 48 50
0 3 4 47 50 51 54
4 6 14 48 54 62 68
5 8 6 60 68 66 74
Average Waiting Time:34.500000
Average Turn Around Time:46.833332
The results for the dataset simulated are stored in: datasets/output.txt==22836==
==22836== HEAP SUMMARY:
==22836== in use at exit: 0 bytes in 0 blocks
==22836== total heap usage: 4 allocs, 4 frees, 2,272 bytes allocated
==22836==
==22836== All heap blocks were freed -- no leaks are possible
==22836==
==22836== For counts of detected and suppressed errors, rerun with: -v
==22836== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 6)
Besides the double free issue pointed out by bruno, your code has an out-of-bounds write bug.
char c1,c2,c3,fcfsselection;
...
fscanf(fp,"%s%s%s",&c1,&c2,&c3); // out-of-bounds write.
Here is a live test of your code, which reports this bug.
I have wrote a small example problem to learn about memory allocation and freeing this memory (to protect from memory leaks):
#include <stdlib.h>
long* foo(char str[]) {
long *nums;
nums = calloc(2,sizeof(long));
//something is happening depending on what's inside the char[]
nums[0] = 57347534;
nums[1] = 84757;
return nums;
}
int main() {
char str1[400] = "This is a test";
long* retValue = foo(str1);
//some lines of checking content of "retValue"
char str2[400] = "This is another test";
retValue = foo(str2);
//some more lines of checking content of "retValue"
char str3[400] = "This is a final test";
retValue = foo(str3);
//again some more lines of checking content of "retValue"
free(retValue);
}
So in my main function, I am using three char arrays which I will pass to my function. This function has a num pointer of long values where am callocing two of them. Then I am just calculating some numbers according to the content in str[] and return nums.
My questions about this are:
How do I free the memory I used for nums? Because I cannot free it before I use it for return.
Is it right to free the retValue in the last line there?
Am I right that I do not need to free my char arrays, because they are not dynamic?
Thanks for your answers, that would help me more safely use pointers!
You need to call free before every new assignment to retValue (if the previous assignment came from malloc, calloc or realloc). Otherwise you will have memory leaks.
Every allocation must be matched by a free, plain and simple.
A good way to answer questions about memory allocation and use is to use a memory checker - I'll use Valgrind:
gcc-8 -std=c11 -fPIC -g -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds 50627661.c -o 5062766
50627661.c: In function ‘foo’:
50627661.c:3:16: warning: unused parameter ‘str’ [-Wunused-parameter]
long* foo(char str[]) {
~~~~~^~~~~
valgrind -q --leak-check=full ./50627661
==14785== HEAP SUMMARY:
==14785== in use at exit: 32 bytes in 2 blocks
==14785== total heap usage: 3 allocs, 1 frees, 48 bytes allocated
==14785==
==14785== 16 bytes in 1 blocks are definitely lost in loss record 1 of 2
==14785== at 0x4C2EBA5: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14785== by 0x10867F: foo (50627661.c:5)
==14785== by 0x1086F6: main (50627661.c:18)
==14785==
==14785== 16 bytes in 1 blocks are definitely lost in loss record 2 of 2
==14785== at 0x4C2EBA5: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14785== by 0x10867F: foo (50627661.c:5)
==14785== by 0x108758: main (50627661.c:24)
This shows that of the three allocations we made, we only freed one of them - the other two leaked.
If you do not want to clutter your calling code with free, the alternative is to pass to the callee an array mangaged by the caller:
long foo(char str[], long *nums, int size) {
if (size < 2) { // passed array must be at least 2 long
return -1;
}
//something is happening depending on whats inside the char[]
nums[0] = 57347534;
nums[1] = 84757;
return 2; // returns used size (or -1 for error)
}
int main() {
long retValue[2];
char str1[400] = "This is a test";
if (-1 == foo(str1, retValue, 2)) {
// process error condition
}
//some lines of checking content of "retValue"
char str2[400] = "This is another test";
if (-1 == foo(str2, retValue, 2)) {
// process error condition
}
//some more lines of checking content of "retValue"
char str3[400] = "This is a final test";
if (-1 == foo(str3, retValue, 2)) {
// process error condition
}
//again some more lines of checking content of "retValue"
//free(retValue); no need for free since nothing was allocated...
return 0;
}
This program is running with root privileges on my machine and I need to perform a Stack overflow attack on the following code and get root privileges:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/sha.h>
void sha256(char *string, char outputBuffer[65])
{
unsigned char hash[SHA256_DIGEST_LENGTH];
int i = 0;
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, string, strlen(string));
SHA256_Final(hash, &sha256);
for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
{
sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
}
outputBuffer[64] = 0;
}
int password_check(char *userpass)
{
char text[20] = "thisisasalt";
unsigned int password_match = 0;
char output[65] = { 0, };
// >>> hashlib.sha256("Hello, world!").hexdigest()
char pass[] = "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3";
text[0] = 'a';
text[1] = 't';
text[2] = 'j';
text[3] = '5';
text[4] = '3';
text[5] = 'k';
text[6] = '$';
text[7] = 'g';
text[8] = 'f';
text[9] = '[';
text[10] = ']';
text[11] = '\0';
strcat(text, userpass);
sha256(text, output);
if (strcmp(output, pass) == 0)
{
password_match = 1;
}
return (password_match == 1);
}
int main(int argc, char **argv)
{
if (argc < 3)
{
printf("Usage: %s <pass> <command>\n", argv[0]);
exit(1);
}
if (strlen((const char *) argv[1]) > 10)
{
printf("Error: pasword too long\n");
exit(1);
}
if (password_check(argv[1]))
{
printf("Running command as root: %s\n", argv[2]);
setuid(0);
setgid(0);
system(argv[2]);
}
else
{
printf("Authentication failed! This activity will be logged!\n");
}
return 0;
}
So I try to analyse the program with IDA and I see the text segment going from the lower addresses to the higher addresses, higher than that I see the data and then the bss and finally external commands.
Now as far as I know the stack should be just above that, but I'm not certain how to view it, how exactly am I supposed to view the stack in order to know what I'm writing on? (Do I even need it or am I completely clueless?)
Second question is considering the length of the input, how do i get around this check in the code:
if (strlen((const char *) argv[1]) > 10)
{
printf("Error: pasword too long\n");
exit(1);
}
Can I somehow give the string to the program by reference? If so how do I do it? (Again, hoping I'm not completely clueless)
Now as far as I know the stack should be just above that, but I'm not certain how to view it, how exactly am I supposed to view the stack in order to know what I'm writing on? (Do I even need it or am I completely clueless?)
The stack location varies all the time - you need to look at the value of the ESP/RSP register, its value is the current address of the top of the stack. Typically, variable addressing will be based on EBP rather then ESP, but they both will point to the same general area of memory.
During analysis, IDA sets up a stack frame for each function, which acts much like a struct - you can define variables with types and names in it. This frame is summarized at the top of the function:
Double-clicking it or any local variable in the function body will open a more detailed window. That's as good as you can get without actually running your program in a debugger.
You can see that text is right next to password_match, and judging from the addresses, there are 0x14 bytes allocated for text, as one would expect. However, this is not guaranteed and the compiler can freely shuffle the variables around, pad them or optimize them into registers.
Second question is considering the length of the input, how do i get around this check in the code:
if (strlen((const char *) argv[1]) > 10)
{
printf("Error: pasword too long\n");
exit(1);
}
You don't need to get around this check, it's already broken enough. There's an off-by-one error.
Stop reading here if you want to figure out the overflow yourself.
The valid range of indices for text spans from text[0] through text[19]. In the code, user input is written to the memory area starting at text[11]. The maximum input length allowed by the strlen check is 10 symbols + the NULL terminator. Unfortunately, that means text[19] contains the 9th user-entered symbol, and the 10th symbol + the terminator overflow into adjacent memory space. Under certain circumstances, that allows you to overwrite the least significant byte of password_match with an arbitrary value, and the second least significant byte with a 0. Your function accepts the password if password_match equals 1, which means the 10th character in your password needs to be '\x01' (note that this is not the same character as '1').
Here are two screenshots from IDA running as a debugger. text is highlighted in yellow, password_match is in green.
The password I entered was 123456789\x01.
Stack before user entered password is strcat'd into text.
Stack after strcat. Notice that password_match changed.