Segfault when a large parameter is passed - c

My program is supposed to print out every nth character in a string of text. The first parameter defines the "n" or the increment between characters to report on. The second parameter is optional, but if specified, defines how many characters to work on.
#include <stdio.h>
#include <stdlib.h>
#define MAX_STR_LEN 100
int main(int argc, char **argv) {
char string[MAX_STR_LEN];
char *testval="This is a test, this is only a test. For the next sixty seconds, this will be a test of the emergency broadcasting system.";
if (argc<2) {
printf("Expected at least one argument.\n");
return 1;
}
int inc=atoi(argv[1]);
if (inc <=0) {
printf("Expected first argument to be a positive number.\n");
return 1;
}
int max=MAX_STR_LEN;
if (argc==3) {
max=atoi(argv[2]);
if (max<0) max=MAX_STR_LEN;
}
int i=0;
while(i < max) {
string[i]=testval[i]; // gdb says this is where the seg fault occurs
i++;
}
string[i]=0;
i=0;
while(string[i]!=0) {
printf("The %d char is %c\n",i,string[i]);
i = i + inc;
}
return 0;
}
Running "./prog3 5 40" works fine, but running "./prog3 5 150" causes a seg fault.
./prog3 5 150
8
Segmentation Fault

This will work better. you need to make the string buffer large enough to hold all the data you put into it. The segmentation fault you had comes from writing to a memory address not allowed to be used by the program.
#include <stdio.h>
#include <stdlib.h>
#define MAX_STR_LEN 100
int main(int argc, char **argv) {
char *testval="This is a test, this is only a test. For the next sixty seconds, this will be a test of the emergency broadcasting system.";
if (argc<2) {
printf("Expected at least one argument.\n");
return 1;
}
int inc=atoi(argv[1]);
if (inc <=0) {
printf("Expected first argument to be a positive number.\n");
return 1;
}
int max=MAX_STR_LEN;
if (argc==3) {
max=atoi(argv[2]);
if (max<0) max=MAX_STR_LEN;
}
char string[max];
int i=0;
while(i < max) {
string[i]=testval[i];
i++;
}
string[i]=0;
i=0;
while(string[i]!=0) {
printf("The %d char is %c\n",i,string[i]);
i = i + inc;
}
return 0;
}

Related

Stack smashing detected in C - why does this happen?

I have the following function, which, given a string, should find the most recurrent couple of letters in it and store the result in a different string.
For example - for the string "ababa", the most recurrent couple would be "ba", and for "excxexd" it would be "ex". This is the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
void printError(){
printf("Error: please check your input\n");
}
bool isLexicographicallyPreceding(char couple1[], char couple2[])
{
if (strcmp(couple1, couple2)>=0) return true;
return false;
}
void coupleDetector(int length, char word[], char result[])
{
char couples[length-1][2];
for (int i=0; i<length-1; i++)
{
char couple[2] = {word[i], word[i+1]};
strcpy(couples[i], couple);
}
char element[]="";
int count=0;
for (int j=0; j<length-1; j++)
{
char tempElement[2];
strcpy(tempElement,couples[j]);
int tempCount=0;
for (int p=0; p<length-1; p++)
{
if (couples[p]==tempElement) tempCount++;
}
if (tempCount>count)
{
strcpy(element, tempElement);
count=tempCount;
}
if (tempCount==count)
{
if (isLexicographicallyPreceding(tempElement,element) == true) strcpy(element, tempElement);
}
}
strcpy(result,element);
}
int main() {
//Supposed to print "ba" but instead presents "stack smashing detected".
int length=5;
char arr[] = "ababa";
char mostCommonCouple[2];
coupleDetector(length,arr,mostCommonCouple);
printf("%s", mostCommonCouple);
return 0;
}
The code compiles without errors, but for some reason does not work as intended but prints out "stack smashing detected". Why would that be? Advices would be very helpful.
Thanks.
In trying out your program, I found a few of your character arrays undersized. Character arrays (strings) need to be sized large enough to also include the null terminator value in the array. So in many locations, having a two-character array size is not sufficient and was the cause of the stack smashing. With that in mind, following is a refactored version of your program.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
void printError()
{
printf("Error: please check your input\n");
}
bool isLexicographicallyPreceding(char couple1[], char couple2[])
{
if (strcmp(couple1, couple2)>=0) return true;
return false;
}
void coupleDetector(int length, char word[], char result[])
{
char couples[length-1][3];
for (int i=0; i<length-1; i++)
{
char couple[3] = {word[i], word[i+1], '\0'};
strcpy(couples[i], couple);
}
char element[3]; /* Define the character array */
strcpy(element, ""); /* Then initialize it if need be */
int count=0;
for (int j=0; j<length-1; j++)
{
char tempElement[3];
strcpy(tempElement,couples[j]);
int tempCount=0;
for (int p=0; p<length-1; p++)
{
if (couples[p]==tempElement) tempCount++;
}
if (tempCount>count)
{
strcpy(element, tempElement);
count=tempCount;
}
if (tempCount==count)
{
if (isLexicographicallyPreceding(tempElement,element)) strcpy(element, tempElement);
}
}
strcpy(result,element);
}
int main()
{
//Supposed to print "ba" but instead presents "stack smashing detected".
int length=5;
char arr[] = "ababa";
char mostCommonCouple[3]; /* Notice size requirement to also contain the '\0' terminator */
coupleDetector(length,arr,mostCommonCouple);
printf("%s\n", mostCommonCouple);
return 0;
}
Here are some key points.
Viewing the code, most sizes for arrays was enlarged by one to accommodate storage of the null terminator.
Work fields such as "element" need to be defined to their proper size so that subsequent usage won't also result in stack smashing.
Testing out the refactored code resulted in the following terminal output.
#Vera:~/C_Programs/Console/Recurrent/bin/Release$ ./Recurrent
ba
So to reiterate, be cognizant that character arrays normally need to be defined to be large enough to contain the largest expected string plus one for the null terminator.
Give that a try and see if it meets the spirit of your project.

Segmentation fault using strcmp and char *arr[]

I am working on creating a shell and I haven't use C for a while. I have the shell initizing properly but when I try to compare the user input to an array of strings I have I get a segmentation fault. I was planning on adding casce statements in a the for loop to initiate each of the processes once they are called by the user. I haven't included those since I have been trying to figure out how to get the user input to match with a value in my string array. Under debug I was only receiving the first character of the builtins[j] value which kind of makes since since it is a pointer right. However I am stuck and could use some ideas for why this isn't returning 0 when I input "exit". Thanks
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
//This code is for creating a basic shell
void init_shell(int num, char *prompt[]){
char s1[] = "-p";
int result;
if(num>1){
result = strcmp(s1, prompt[1]);
if(result==0){
printf("%s>$", prompt[2]);
}else{
printf("308sh>$");
}
}
//printf("%s\n %s\n %s\n %d\n", prompt[0], prompt[1], prompt[2], result);
else{
printf("308sh>$");
}
}
//The infinite loop for accepting user input until it closes
int main(int argc, char *argv[]){
const char *builtins[7];
builtins[0] = "exit\n";
builtins[1] = "pid\n";
builtins[2] = "ppid\n";
builtins[3] = "cd\n";
builtins[4] = "pwd\n";
builtins[5] = "set\n";
builtins[6] = "get\n";
char usr_in[]="";
char cmp[]="";
while(1==1){
init_shell(argc, argv);//intial prompt for the shell
fgets(usr_in,100,stdin);
//Check for builtin Commands
int cmds_size = 7;
int j=0;
int res;
for(j; j<cmds_size; j++){
res=strcmp(usr_in, hold);
if(res==0){
printf("Execucting\n");
}
else{
printf("no command\n");
}
}
}
return(0);
}
The issue here is that you're writing the user's input to a buffer that isn't big enough to hold anything other than a null terminator.
char user_in[] = "";
The above line tells the C compiler that you need just enough space to store [ '\0' ], which is a single byte. The C compiler doesn't know that you may later write a 100-byte string to that buffer.
When you write to the buffer, the user's input overflows and will overwrite other values in your stack. Since the other values in your stack are pointers, what'll happen is you'll run into seg-faults, since you're writing character values into those bytes, but interpreting them as char pointers.
You are correctly limiting the size of the allowed input from the user to 100 characters, but you should make sure that your buffer is big enough to hold the value you're reading in:
char user_in[101];
for(int i = 0; i < sizeof(user_in) / sizeof(user_in[0]); i++) {
user_in[i] = 0; // Since this is allocated on the stack *in main*, this
// shouldn't be necessary
}
Here's one example of how you can rewrite your main method:
#include <stdio.h>
#include <string.h>
typedef enum { false, true } bool; // If you don't have this
// defined already
int main(int argc, char *argv[]) {
const char *builtins[7];
builtins[0] = "exit\n";
builtins[1] = "pid\n";
builtins[2] = "ppid\n";
builtins[3] = "cd\n";
builtins[4] = "pwd\n";
builtins[5] = "set\n";
builtins[6] = "get\n";
char user_in[101];
for(int i = 0; i < sizeof(user_in) / sizeof(user_in[0]); i++) {
user_in[i] = 0;
}
while(1) {
printf("Enter a command: ");
fgets(user_in, 100, stdin);
bool found = false;
for(int i = 0; i < sizeof(builtins) / sizeof(builtins[0]); i++) {
if (!strcmp(user_in, builtins[i])) {
printf("Found command %s", builtins[i]);
found = true;
break;
}
}
if (!found) {
printf("Didn't find command\n");
}
}
return 0;
}
Also, regarding your function init_shell: you're checking to see if argc is greater than 1, but that only guarantees that argv[1] is defined; it doesn't guarantee that argv[2] is defined. (Remember, argc is the size of the argv array, where the first element is the name of the program being executed). You want to make sure that argc is at least 3 before checking for the prompt flag in the way you are.
It may be overkill for your use-case, but consider using the getopt function for getting a custom prompt value from the user. See http://man7.org/linux/man-pages/man3/getopt.3.html for documentation regarding that method.

Getting seg fault when assigning random float value in a loop from rand()

I cannot figure out why my code works only one way and it is driving me crazy because it just should work.Whole code so far:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <time.h>
#define MAGIC_CONSTANT 73.
int Check_Parameters(int argc, char **argv){
int i=0;
if(argc > 3){
printf("You have entered more than 2 arguments!\n");
}
do{
if(isdigit(argv[2][i]) == 0){
printf("Second argument should be number!\n");
return 0;
}
i++;
}while(argv[2][i] != 0);
return 1;
}
int Write_Sequence(char **argv,int Handle){
char *length;
float *values,help;
unsigned int i,j,index;
index = atoi(argv[2]);
for(i=0; i<index; i++){
*length = (char)(rand()%11+10);
if(write(Handle,length,1) == -1){
printf("Something went wrong during Index writing\n%s\n",strerror(errno));
return 0;
}
printf("Value is: %u\n",*(unsigned int *)length);
values = malloc(*length*sizeof(float));
if(values == NULL){
printf("Memory allocation for values failed\n");
return 0;
};
for(j=0; j<*length; j++){
help = (rand()%200)/MAGIC_CONSTANT;
values[j] = (float)(rand()%200)/MAGIC_CONSTANT;
printf("%lf\n",values[j]);
}
if(write(Handle,(char*)values,sizeof(float)*(*length)) == -1){
printf("Something went wrong during value writing\n%s\n",strerror(errno));
return 0;
};
free(values);
}
return 1;
}
int main(int argc, char **argv){
int Handle;
char *citaj;
srand(time(NULL));
Check_Parameters(argc,argv);
Handle = open(argv[1], O_RDWR | O_CREAT);
Write_Sequence(argv,Handle);
close(Handle);
return 0;
}
I want to fill my allocated memory with floats. The thing is that this code does not work:
for(j=0; j<*length; j++){
values[j] = (rand()%200)/MAGIC_CONSTANT;
}
But this one does without any problem:
for(j=0; j<*length; j++){
help = (rand()%200)/MAGIC_CONSTANT;
values[j] = help;
}
MAGIC_CONSTANT = 73.
The thing is it only works when I first assign it to my help variable and then to the memory and whenever I assign it right away it gives me segmentation fault. What is wrong with that rand() function?
Thanks!
You declare length as a char *, but you never give it a value. You then try to dereference the pointer by assigning to *length. That invokes undefined behavior. In your case, this manifests as a segfault in your code without help but appears to work when you do use help.
In this code, you don't need length to be a pointer. Instead, declare it as a char and assign to it directly. Then when calling write, take its address.
int Write_Sequence(char **argv,int Handle){
// declare as char
char length;
float *values,help;
unsigned int i,j,index;
index = atoi(argv[2]);
for(i=0; i<index; i++){
// assign directly to length
length = (char)(rand()%11+10);
// pass address of length to write
if(write(Handle,&length,1) == -1){
printf("Something went wrong during Index writing\n%s\n",strerror(errno));
return 0;
}
...
The statement:
*length = (char)(rand()%11+10);
You are attempting to assign the type-casted output of rand() into an uninitialized pointer. I am not sure what you intended to use this for, but if your compiler is set properly, this should throw warnings. (uninitialized pointer)
Other things to consider, 1) for how its used, length does not need to be a pointer. 2) when using rand(), it is recommended that srand() be called first to seed the pseudo randomizer rand(). Once you do these few things, your code at least starts to work. There are other things you can do to optimize, but a code segment with these changes is below.
#define MAGIC_CONSTANT 73
int main(void)
{
char length = 0;
float *values,help;
unsigned int i,j;
srand(clock());
length = (char)(rand()%11+10);
values = malloc(length*sizeof(float));
for(j=0; j<length; j++)
{
values[j] = (rand()%200)/MAGIC_CONSTANT;
}
return 0;
}

Shell-like program C

The code below is supposed to work as a shell. It has the previous and next option, a history like feature, the exit and the execute command.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BUFFER_SIZE 256
#define HISTORY_LENGTH 128
int executeCommand(char*cmd)
{
return(!strcmp(cmd,"e\n"));
}
int exitCommand(char*cmd)
{
return (!strcmp(cmd,"exit\n"));
}
int previousCommand(char*cmd)
{
return (!strcmp(cmd,"p\n"));
}
int nextCommand(char*cmd)
{
return (!strcmp(cmd,"n\n"));
}
void execute(char *line)
{
line[strlen(line)-1]='\0';
char **arguments;
char* temp;
int i=0;
arguments=(char**)malloc(sizeof(char)*10);
temp=strtok(line," ");
arguments[i]=malloc(strlen(temp)*sizeof(char));
if(arguments[i]!=NULL)
{
strcpy(arguments[i],temp);
i++;
}
else
{
printf("Out of memory");
}
while(temp!=NULL)
{
temp=strtok(NULL," ");
if(temp==NULL){
arguments[i]=NULL;
}
else{
arguments[i]=malloc(strlen(temp)*sizeof(char));
if(arguments[i]!=NULL)
{
strcpy(arguments[i],temp);
i++;
}
}
}
printf("%s ",arguments[0]);
printf("%s ",arguments[1]);
printf("%s ",arguments[2]);
execvp(arguments[0],arguments);
}
int main(int argc, char*argV[]) {
int i;
char *cmd=(char*)malloc(sizeof(char)*BUFFER_SIZE);
char **history=NULL;
int historylength=0;
int currentCommand=0;
history=(char**)malloc(sizeof(char)*BUFFER_SIZE);
do{
fgets(cmd,BUFFER_SIZE-1,stdin);
if(exitCommand(cmd))
break;
else
if(previousCommand(cmd))
{
if(currentCommand>0)
printf("%s",history[--currentCommand]);
else if(currentCommand==0)
{
currentCommand=historylength;
printf("%s",history[--currentCommand]);
}
}
else
if(nextCommand(cmd))
{
if(currentCommand<historylength)
printf("%s",history[currentCommand++]);
}
else
if(executeCommand(cmd))
{
execute(history[--currentCommand]);
}
else
{
history[historylength]=malloc(strlen(cmd)*sizeof(char));
if(history[historylength]!=NULL)
{
strcpy(history[historylength],cmd);
currentCommand=++historylength;
}
else
{
printf("Out of memory");
break;
}
}
} while(1);
free(cmd);
for(i=0;i<historylength;i++)
free(history[i]);
free(history);
return 0;
}
I want to make this work for the function cat. I type in the e cat main.c and I expect it to execute the cat command but it's not doing anything, what am I doing wrong here? I'm not a pro at this so I appreciate all the help.
This is incorrect:
arguments=(char**)malloc(sizeof(char)*10);
as arguments is a char**, so the code needs to allocate sizeof(char*). Change to:
arguments = malloc(10 * sizeof(*arguments));
Same mistake for history also. Additionally, see Do I cast the result of malloc?
There is one char less than required being allocated for the char* as strcpy() writes a terminating null character. Change:
arguments[i]=malloc(strlen(temp)*sizeof(char));
to:
arguments[i] = malloc(strlen(temp) + 1);
sizeof(char) is guaranteed to be 1 and can be omitted from the size calculation.
Prevent i from exceeding the bounds of the memory allocated for arguments. As the code currently stands there is no protection to prevent i from exceeding 9 (0 to 9 are the valid values for i as arguments was allocated 10 elements).

Wrong result when copying an array of strings

I have the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* user;
char* passwd;
int nr;
void test()
{
int i=0;
for(i=0;i<argc;i++)
printf("Hello %s \n",user);
}
int main(int argc,char*argv[])
{
int i;
nr=argc;
for (i=0; i<argc; i++)
{
user=strdup(argv[i]);
}
test();
return 0;
}
The result is the argv[argc] on all the positions. How can I fix this? I wwant to have that test() outside the loop.
**
EDIT
**
After the ANSWERS here this is my new code, which is not working. Can anyone say why?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* user;
void test(int n)
{
int i=0;
for(i=0;i<n;i++)
printf("%s \n",user[i]);
}
int main(int argc,char*argv[])
{
user = (char*) malloc(argc*sizeof(char));
int i;
for (i=0;i<argc;i++)
{
user[i]=argv[i];
}
test(argc);
return 0;
}
You are assigning to both password and user at each iteration of the for loop. The final values you see are from the last iteration. Also, there is memory leak due to overwriting the pointers from previous strdup calls. In fact, you do not need a loop:
int main(int argc,char*argv[])
{
if(argc == 3) {
user=strdup(argv[1]);
passwd=strdup(argv[2]);
} else {
// error: usage
}
test();
return 0;
}
If you want to have multiple user/password combinations:
char *user[256], *passwd[256];
void test(int n) {
int i;
for(i=0;i<n;i++)
printf("Hello %s \n",user[i]);
}
int main(int argc,char*argv[])
{
int i;
for(i = 0; i < argc && i < 256; i+=2) {
user[i]=strdup(argv[i]);
passwd[i]=strdup(argv[i+1]);
}
test(argc);
return 0;
}
Because you overwrite the pointers user and passwd in every iteration. Hence, you'll only see the last string.
If you can tell your aim of the program, a better answer can be provided. Because I am not sure whether you want to read one user and passwd Or an array of users and passwds.
After you edit, I see you want to read an array of strings:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char** user;
// or char *user[100]; /* If you want a fix length array of pointers. Now, you dont have to malloc. /*
char* passwd;
int nr;
void test(int argc)
{
int i=0;
for(i=0;i<argc;i++)
printf("Hello %s \n",user[i]);
}
int main(int argc,char*argv[])
{
int i;
nr=argc;
user = malloc(argc*sizeof(char*));
for (i=0; i<argc; i++)
{
user[i]=strdup(argv[i]);
}
test(argc);
return 0;
}
Of course; in test() you don't use i other than a loop variable and in main() you keep overwriting the previous value of user and passwd. In effect, what you do is:
user = strdup(argv[0]); /* Note: argv[0] is the program name. */
passwd = strdup(argv[0]);
user = strdup(argv[1]);
passwd = strdup(argv[1]);
user = strdup(argv[2]);
passwd = strdup(argv[2]);
user = strdup(argv[3]);
passwd = strdup(argv[3]);
printf("%s %s \n", user, passwd);
With this information, can you fix your program?
$ cat trash.c
#include <stdio.h>
#include <string.h>
void test(FILE* stream, char* usr, char* pass) {
fprintf( stream, "%s#%s\n", usr, pass);
}
int main(int argc, char** argv) {
int i = 1;
if (argc % 2) {
while(argv[i]) {
test(stdout, argv[i], argv[i + 1]);
i += 2;
}
}
return 0;
}
$ clang trash.c
$ ./a.out user1 pass1 user2 pass2
user1#pass1
user2#pass2
$
also if you call strdup() don't forget to free memory, because strdup called malloc().

Resources