Saving an integer into an array of strings using sprintf() - c

I'm having trouble storing an integer into an array of strings using sprintf(). I am trying to create a new argv list to pass into my child process. I have 'curr' storing the correct value since I've tested in in GDB. My code is as follows:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h> /* for pid_t */
int main(int argc, char *argv[]){
static char *argv2[] = {"./datagen", "10", "outputfile", "SIGUSR1"};
pid_t pid = fork();
int curr = getpid();
sprintf(argv2[4], "%s", curr);
if(pid == 0)
{
printf("You are in the child process.\n");
}
else{
printf("You are in the parent process. Process ID is %d\n", getpid());
}
return;
}
After exhaustively searching around for a clear answer, I have yet to find anything. Ideally, the 4th slot of argv2 will store the process id as a string. However, I am getting a segmentation fault 11. If anyone could shed some light on this issue I would be eternally grateful.
Thank you!!

You can't do that
static char *argv2[] = {"./datagen", "10", "outputfile", "SIGUSR1"};
is a declaration of an array of char pointers, which are pointing to string literals, and further more to four string literals only, you can't extend it nor modifiy the strings.
What you need is
char argv2[10][100] = {"./datagen", "10", "outputfile", "SIGUSR1"};
assuming that you want 10 strings of maximum length 100, which you can obviously change.
Also, the format specifier for integers is "%d" so you have another mistake, having said all that you can now
sprintf(argv2[4], "%d", curr);
and I would suggest the snprintf() function, since it will avoid buffer overflow problems,
snprintf(argv2[4], sizeof(argv[4]), "%d", curr);
chux comment is correct if you want to have control on whether the specified length of the string was enough, you should check the return value of snprintf(), in case there wasn't sufficient space to write all the source string into the destination it will be truncated, if snprintf() returns a value larger or equal to the requested maximum, it means that the string was truncated, so a simple check like
if (snprintf(argv2[4], sizeof(argv[4]), "%d", curr) >= sizeof(argv[4]))
doSomething_TheString_Was_Truncated();
although, for 100 characters and the "%d" that will not happen, but I firmly believe that people must write safe code as a habit, rather than only checking possible problems, check for every thing that can conceptually go wrong, no matter how unlikely. Because sometimes there will be situations where an unexpected thing will happen.
Note: as chux pointed out again, snprintf() will return a negative in case of an error, you can check for that separately, to check if there was an error.

Simple steps:
1) Determine array size information from existing argv, argc arguments.
int i, len=0, lenKeep=21;//initialize large enough to contain pid integer
for(i=0;i<argc;i++)
{
len = strlen(argv[i])
if(lenKeep<len)lenKeep = len;
}
2) use that size information to create a new string array, argv2, with additional elements if necessary. (argv2 will be an array of strings, create sufficient space.)
char argv2[argc+1][lenKeep+1];
//argc+1 allows for additional array element
//lenKeep+1 provides space for all existing content
3) add new information to the string array in the normal way.
sprintf(argv2[argc], "%d", curr); //argv2 array contains argc + 1 elements
//so now argc is a valid index value

sprintf(argv2[4], "%s", curr); breaks the array, which has only 4 elements.
Even if it has more elements, you are writing to a string literal which is Undefined Behaviour.

Try using %d instead of %s, it should anyways be saved as a C string.

Related

How to make a conditional loop from "Yes, no" question by comparing String with pointer initiation? [C Programming]

int main(int argc, char* argv[]) {
char* string; //local variable for a character
string = (char*)malloc(sizeof(char));
char* yes = "yes";
printf("Do you want to play (y/n)?");
scanf_s("%s", string);
if (strcmp(yes, string) == 0) {
printf("Hello welcome...");
}
Above is my code. Essentially, I want to make a loop asking for the user to input yes, y to continue; n, no to discontinue. But I simplify it for the sake of simple codes. The program just output the question, I press enter yes and enter then it stops.
I cant figure out a way to do it using array syntax( char string[] way, although array and malloc are basically the same) so I use pointer and malloc instead.
I'm going mad because this is bugging me so much. The practice assignment only asks to input character 'y' 'n' using %c but i want to do it the %s.
Really appreciate any help, im really stuck now. Thank you so much
Your code has two significant problems. First, if you want to input a string of characters, your malloc call needs to allocate space for more than one character; you should allocate the maximum number of characters you think the user's input will contain plus one - strings in C have a zero (nul) character at the end, to mark the end of the string).
Second, when you use the scanf_s function to read in a string (using the %s format specifier, as you have done), then you need to add additional parameters (the size of the target string buffer) after each string argument.
Here's a modified version of your code with these (and a few other) corrections:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char* string = malloc(5); //local variable for a character string (up to 5 chars)
char* yes = "yes";
printf("Do you want to play (y/n)?");
scanf_s("%s", string, 5);
if (strcmp(yes, string) == 0) {
printf("Hello welcome...");
}
free(string); // Don't forget to release the memory!
return 0; // Conventionally, return 0 for success or non-zero on error
}
Note 1: Each and every call to malloc (or calloc) should be paired with a call to free to release the allocated memory, or you will end up with memory leaks.
Note 2: Please read this post: Do I cast the result of malloc?
Note 3: Although most (all?) C compilers will not insist on it, it is good practice to explicitly add a return 0; (for success) statement at the end of the main function.
Please feel free to ask for any further clarification and/or explanation.

C: buffer overflow, changing passed variables

I'm doing some exercises regarding buffer overflows and I am currently stumped as how to proceed further with one of them. This is the program code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
void reverb(char *msg, unsigned int len) {
unsigned char length = (unsigned char) len;
char buffer[250] = "Printed: ";
strcat(buffer + 9, msg);
if ((length > 75) || (length < 15)) {
fprintf(stderr, "Error: invalid string length");
exit(1);
}
else {
fprintf(stdout, "%s\n", buffer);
}
}
int main(int argc, char **argv) {
//argument check
if (argc != 2) {
fprintf(stderr, "Invalid arguments!\n");
return 1;
}
reverb(argv[1], strlen(argv[1]));
return 0;
}
So basically as obvious as it is, this program should just re-print the argument you gave to it. I obviously have to exploit one of the functions used, and I suspect the main culprit here is strcat. However, I'm faced with the issue of the length variable when I want to get my stack smashing done.
To be able to cause a segfault and successfully find a point for the overflow to happen, I need to pass an argument with a length of around 255+ (not sure on the current number right now but it's somewhere around that), which is not doable with my 75 char limit. Using gdb and setting a break point right after strcat, I am able to find the buffer's location in the memory (kinda easy, just the area filled with 0x41 since I spammed it with A's). length's location was kinda trickier, but here's the issue - it's located BEFORE the buffer, meaning I couldn't even overwrite it if I wanted. But, I somehow still need to overwrite it to get into the else branch, I think. And I've been stuck at that point, not seeing a way to proceed properly.
If the string is long - say 256 chars, the Length variable will be wrong. Use the suggestions in the comments to catch it.
The crash: the string copy will then copy all 256 chars onto the end of the string in buffer, which only can hold 250 bytes.
Move the strcpy() into the else.
Additional notes:
I ran this in my debugger (VS2015)
When passing in the 256 byte string and overrunning “buffer”, the variable “length” got clobbered, and its value changes from 0 to 69 (some character in my string). This caused the 75 char limit to fail and the buffer to be printed.

CS50 IDE: printf returns extra characters

I am having problems with the printf function in the CS50 IDE. When I am using printf to print out a string (salt in this code), extra characters are being output that were not present in the original argument (argv).
Posted below is my code. Any help would be appreciated. Thank you.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
// ensuring that only 1 command-line argument is inputted
if (argc != 2)
{
return 1;
}
char salt[2];
for (int i = 0; i < 2; i++)
{
char c = argv[1][i];
salt[i] = c;
}
printf("the first 2 characters of the argument is %s\n", salt);
}
You are missing a string terminator in salt.
Somehow the computer needs to know where your string ends in memory. It does so by reading until it encounters a NUL byte, which is a byte with value zero.
Your array salt has exactly 2 bytes of space, and after them, random garbage exists which just happens to be next in memory after your array. Since you don't have a string terminator, the computer will read this garbage as well until it encounters a NUL byte.
All you need to do is include such a byte in your array, like so:
char salt[3] = {0};
This will make salt one byte longer, and the {0} is a shorthand for {0, 0, 0} which will initialize the contents of the array with all zerores. (Alternatively, you could use char salt[3]; and later manually set the last byte to zero using salt[2] = 0;.)
In your case, salt is at least one element shy of being a string, unless the argv[1] is only one element, it does not contain a null-terminator.
You need to allocate space to hold the null-terminator and actually put one there to be able to use salt as string, as expected for the argument to %s conversion specifier in case of printf().
Otherwise, the string related functions and operations, which essentially rely on the fact that there will be a null terminator to mark the end of the char array (i.e., mark the end of valid memory that can be accessed), will try to access past the valid memory which causes undefined behavior. Once you hit UB, nothing is guaranteed.
So, considering the fact that you want to use
"....the first 2 characters of the argument....."
you need to make salt a 3-element char array, and make sure that salt[2] contains a null-terminator, like '\0'.

I am trying to create a program that takes a name and outputs the initials but keep running into an array error

I am trying to create a program that take the input from the user and prints the first character of each word but every time I try to Here is my code.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
int main(void)
{
char leng[100];
int len;
scanf("%s", &leng[100]);
len = strlen(&leng[100]);
char name[len];
//checking if at end or not
while (name[len] != '\0')
{
if (name[len] == ' ')
printf("%c", name[len + 1]);
len++;
}
}
Every time I give a name it shows an error something like:
index 3 out of bounds for type 'char [len]'
These two lines are incorrect:
scanf("%s", &leng[100]);
len = strlen(&leng[100]);
If you translate these into English, their meanings as written are:
Scan a string to the memory at the address of 101st element of the
leng array.
Get the length of the string that starts at the address
of the 101st element of the leng array.
The array index is out of bounds because leng[100] is past the end of the array. Remember that a 100 element array goes from 0 to 99, not from 1 to 100!
You want to be scanning into the base address of the array, and passing the base address of the array into strlen(). I'll leave the syntax for you to figure out from your textbook.
And by the way, you also have a problem in your code because you're reading your data into an array named leng, but your loop is working with an array named len. There are at least two additional problems in your code, but I'll leave them for you to debug.
There are a few things to consider with your code. As #richardschwartz already mentioned, you are not referencing your char arrays correctly. you have:
scanf("%s", &leng[100]);
len = strlen(&leng[100]);
You may want the following instead:
scanf("%s", leng);
len = strlen(leng);
Also, keep in mind that scanf with the %s flag will stop reading input once white-space is detected. For example, if you input "hello world",
scanf("%s", leng);
will only catch the characters "hello". To get around this, you could loop scanf to read multiple words and return the first character of each word as you desire.
Lastly, scanf is not advised for beginners though. See paxdiablo's excellent reason regarding lack of overflow protection, here: https://stackoverflow.com/a/1248017/6870832

lack of understanding about sscanf usage

I would like to parse a specific line. So, I wrote the following piece of code in order to test the logic but I probably understand something wrongly :
typedef struct vers
{
char tu8UVersion[5];
char tu8UCommit[32];
}tst_prg_versions;
int main(int argc, char **argv)
{
tst_prg_versions lstVer;
char buf1[32];
char buf2[32];
char str[] = "BOARD-VERS-v1.0.0-git+9abc12345a";
sscanf(str, "BOARD-VERS-v%5s-git+%s", lstVer.tu8UVersion, lstVer.tu8UCommit);
printf("vers='%s'\n", lstVer.tu8UVersion);
printf("commit='%s'\n", lstVer.tu8UCommit);
sscanf(str, "BOARD-VERS-v%5s-git+%s", buf1, buf2);
printf("vers='%s'\n", buf1);
printf("commit='%s'\n", buf2);
return 0;
}
Once executed it returns :
vers='1.0.09abc12345a'
commit='9abc12345a'
vers='1.0.0'
commit='9abc12345a
Why the first vers is equals to 1.0.09abc12345a and not 1.0.0 ?
The first actually reads 1.0.0! Problem is, however, that tu8UVersion is not null-terminated, thus printf (not sscanf) prints beyound the field (doing so is undefined behaviour, however, as noted by sjsam) - which is immediately followed by tu8UCommit (does not necessarily have to be so, there could still be some fill bytes in between for alignment reasons!).
You need to either print 5 characters at most (%.5s in printf format string) or leave place for terminating the tu8UVersion with 0, as proposed in a comment already.
Something similar could have happened with your buffers, too. You are lucky that they appearently have been initialized to 0 already (probably because of compiled as debug version), which again does not necessarily have to happen. So with bad luck, you could have printed the whole rest of buf1 (having been left at garbage) and even beyond.
Why the first vers is equals to 1.0.09abc12345a and not 1.0.0 ?
Remember that you have
typedef struct vers
{
char tu8UVersion[5];
char tu8UCommit[32];
}tst_prg_versions;
I guess, there is a good chance the memory for tu8UVersion and tu8UCommit is contiguous. Since you have not null-terminated tu8UVersion when you do :
printf("vers='%s'\n", lstVer.tu8UVersion);
it goes on to print tu8UCommit and it stops because tu8UCommit is null terminated.
While sscanf seem the most sensible solution here you could also introduce some formatting :
char tu8UVersion[32];
/* version number can't get too big.
* So the first step is do allocated a
* reasonably - but not too - big size for it.
* So that you can be sure there are few empty bytes at the end.
*/
and then use a function to sanitize a string :
char* sanitized(char* ptr)
{
if(ptr[strlen(ptr)]!='\0') // include string.h for strlen
ptr[strlen(ptr)]='\0';
return ptr;
}
and print it like :
printf("vers='%s'\n", sanitized(lstVer.tu8UVersion));
Your problem has already been identified in the comments: You don't leave space for the terminating null character and the two strings are run together.
If you want to scan a version whose size you don't know beforehand, you can limit the characters to scan to decimal digits and points with %[.-9] or to everything except a hyphen with %[^-]. (The %[...] format is like %s, except that you must provide a list of valid characters in the brackets. A caret as first letter means that the string is made up of characters that are not listed. In other words, %s is short for %[^ \t\n]
When you scan a string, you should test the return value of sscanf to be sure that all items have been scanned correctly and contain valid values.
Here's a variant that scans version numbers of up to 11 letters:
#include <stdlib.h>
#include <stdio.h>
typedef struct vers
{
char tu8UVersion[12];
char tu8UCommit[32];
} tst_prg_versions;
int main(int argc, char **argv)
{
tst_prg_versions lstVer;
char str[] = "BOARD-VERS-v1.0.0-git+9abc12345a";
int n;
n = sscanf(str, "BOARD-VERS-v%11[^-]-git+%s",
lstVer.tu8UVersion, lstVer.tu8UCommit);
if (n == 2) {
printf("vers='%s'\n", lstVer.tu8UVersion);
printf("commit='%s'\n", lstVer.tu8UCommit);
} else {
puts("Parse error.");
}
return 0;
}

Resources